mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-08-06 13:18:30 +02:00
Added stream proxy UDP support
+ Added UDP support #147 (wip) + Updated structure for proxy storage + Renamed TCPprox module to streamproxy + Added multi selection for white / blacklist #176
This commit is contained in:
451
src/web/components/streamprox.html
Normal file
451
src/web/components/streamprox.html
Normal file
@@ -0,0 +1,451 @@
|
||||
<div class="standardContainer">
|
||||
<div class="ui basic segment">
|
||||
<h2>Stream Proxy</h2>
|
||||
<p>Proxy traffic flow on layer 3 via TCP or UDP</p>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="ui basic segment" style="margin-top: 0;">
|
||||
<h4>TCP / UDP Proxy Rules</h4>
|
||||
<p>A list of TCP proxy rules created on this host. To enable them, use the toggle button on the right.</p>
|
||||
<div style="overflow-x: auto; min-height: 400px;">
|
||||
<table id="proxyTable" class="ui celled unstackable table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Port/Addr A</th>
|
||||
<th>Port/Addr B</th>
|
||||
<th>Mode</th>
|
||||
<th>Timeout (s)</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button class="ui basic right floated button" onclick="initProxyConfigList();" title="Refresh List"><i class="ui green refresh icon"></i>Refresh</button>
|
||||
<br><br>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="ui basic segment" id="addproxyConfig">
|
||||
<h4>Add or Edit Stream Proxy</h4>
|
||||
<p>Create or edit a new stream proxy instance</p>
|
||||
<form id="streamProxyForm" class="ui form">
|
||||
<div class="field" style="display:none;">
|
||||
<label>UUID</label>
|
||||
<input type="text" name="uuid">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Name</label>
|
||||
<input type="text" name="name" placeholder="Config Name">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Listening Port / Address with Port</label>
|
||||
<input type="text" name="listenAddr" placeholder="">
|
||||
<small>Port to listen on this host. e.g. :25565 or 127.0.0.1:25565. <br>
|
||||
If you are using Docker, you will need to expose this port to host network.</small>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Proxy Target Address with Port</label>
|
||||
<input type="text" name="proxyAddr" placeholder="">
|
||||
<small>Server address to forward TCP / UDP package. e.g. 192.168.1.100:25565</small>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Timeout (s)</label>
|
||||
<input type="text" name="timeout" placeholder="" value="10">
|
||||
<small>Connection timeout in seconds</small>
|
||||
</div>
|
||||
<Br>
|
||||
<div class="field">
|
||||
<div class="ui toggle checkbox">
|
||||
<input type="checkbox" tabindex="0" name="useTCP" class="hidden">
|
||||
<label>Enable TCP<br>
|
||||
<small>Forward TCP request on this listening socket</small>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="ui toggle checkbox">
|
||||
<input type="checkbox" tabindex="0" name="useUDP" class="hidden">
|
||||
<label>Enable UDP<br>
|
||||
<small>Forward UDP request on this listening socket</small></label>
|
||||
</div>
|
||||
</div>
|
||||
<button id="addStreamProxyButton" class="ui basic button" type="submit"><i class="ui green add icon"></i> Create</button>
|
||||
<button id="editStreamProxyButton" class="ui basic button" onclick="confirmEditTCPProxyConfig(event);" style="display:none;"><i class="ui green check icon"></i> Update</button>
|
||||
<button class="ui basic red button" onclick="event.preventDefault(); cancelStreamProxyEdit(event);"><i class="ui red remove icon"></i> Cancel</button>
|
||||
<!--
|
||||
<div class="ui basic inverted segment" style="background: var(--theme_background_inverted); border-radius: 0.6em;">
|
||||
<p>TCP Proxy support the following TCP sockets proxy modes</p>
|
||||
<table class="ui celled padded inverted basic table">
|
||||
<thead>
|
||||
<tr><th class="single line">Mode</th>
|
||||
<th>Public-IP</th>
|
||||
<th>Concurrent Access</th>
|
||||
<th>Flow Diagram</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<h4 class="ui center aligned inverted header">Transport</h4>
|
||||
</td>
|
||||
<td class="single line">
|
||||
Server: <i class="ui green check icon"></i><br>
|
||||
A: <i class="ui remove icon"></i><br>
|
||||
B: <i class="ui green check icon"></i> (or same LAN)<br>
|
||||
</td>
|
||||
<td>
|
||||
<i class="ui green check icon"></i>
|
||||
</td>
|
||||
<td>Port A (e.g. 25565) <i class="arrow right icon"></i> Server<br>
|
||||
Server <i class="arrow right icon"></i> Port B (e.g. 192.168.0.2:25565)<br>
|
||||
<small>Traffic from Port A will be forward to Port B's (IP if provided and) Port</small>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h4 class="ui center aligned inverted header">Listen</h4>
|
||||
</td>
|
||||
<td class="single line">
|
||||
Server: <i class="ui green check icon"></i><br>
|
||||
A: <i class="ui remove icon"></i><br>
|
||||
B: <i class="ui remove icon"></i><br>
|
||||
</td>
|
||||
<td>
|
||||
<i class="ui red times icon"></i>
|
||||
</td>
|
||||
<td>Port A (e.g. 8080) <i class="arrow right icon"></i> Server<br>
|
||||
Port B (e.g. 8081) <i class="arrow right icon"></i> Server<br>
|
||||
<small>Server will act as a bridge to proxy traffic between Port A and B</small>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<h4 class="ui center aligned inverted header">Starter</h4>
|
||||
</td>
|
||||
<td class="single line">
|
||||
Server: <i class="ui times icon"></i><br>
|
||||
A: <i class="ui green check icon"></i><br>
|
||||
B: <i class="ui green check icon"></i><br>
|
||||
</td>
|
||||
<td>
|
||||
<i class="ui red times icon"></i>
|
||||
</td>
|
||||
<td>Server <i class="arrow right icon"></i> Port A (e.g. remote.local.:8080) <br>
|
||||
Server <i class="arrow right icon"></i> Port B (e.g. recv.local.:8081) <br>
|
||||
<small>Port A and B will be actively bridged</small>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
-->
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
let editingStreamProxyConfigUUID = ""; //The current editing TCP Proxy config UUID
|
||||
|
||||
$("#streamProxyForm .dropdown").dropdown();
|
||||
$('#streamProxyForm').on('submit', function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
//Check if update mode
|
||||
if ($("#editStreamProxyButton").is(":visible")){
|
||||
confirmEditTCPProxyConfig(event);
|
||||
return;
|
||||
}
|
||||
|
||||
var form = $(this);
|
||||
|
||||
var formValid = validateTCPProxyConfig(form);
|
||||
if (!formValid){
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the AJAX POST request
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/api/streamprox/config/add',
|
||||
data: form.serialize(),
|
||||
success: function(response) {
|
||||
if (response.error) {
|
||||
msgbox(response.error, false, 6000);
|
||||
}else{
|
||||
msgbox("Config Added");
|
||||
}
|
||||
clearStreamProxyAddEditForm();
|
||||
initProxyConfigList();
|
||||
$("#addproxyConfig").slideUp("fast");
|
||||
},
|
||||
error: function() {
|
||||
msgbox('An error occurred while processing the request', false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function clearStreamProxyAddEditForm(){
|
||||
$('#streamProxyForm input, #streamProxyForm select').val('');
|
||||
$('#streamProxyForm select').dropdown('clear');
|
||||
}
|
||||
|
||||
function cancelStreamProxyEdit(event=undefined) {
|
||||
clearStreamProxyAddEditForm();
|
||||
$("#addStreamProxyButton").show();
|
||||
$("#editStreamProxyButton").hide();
|
||||
}
|
||||
|
||||
function validateTCPProxyConfig(form){
|
||||
//Check if name is filled. If not, generate a random name for it
|
||||
var name = form.find('input[name="name"]').val()
|
||||
if (name == ""){
|
||||
let randomName = "Proxy Rule (#" + Math.round(Date.now()/1000) + ")";
|
||||
form.find('input[name="name"]').val(randomName);
|
||||
}
|
||||
|
||||
// Validate timeout is an integer
|
||||
var timeout = parseInt(form.find('input[name="timeout"]').val());
|
||||
if (form.find('input[name="timeout"]').val() == ""){
|
||||
//Not set. Assign a random one to it
|
||||
form.find('input[name="timeout"]').val("10");
|
||||
timeout = 10;
|
||||
}
|
||||
|
||||
if (isNaN(timeout)) {
|
||||
form.find('input[name="timeout"]').parent().addClass("error");
|
||||
msgbox('Timeout must be a valid integer', false, 5000);
|
||||
return false;
|
||||
}else{
|
||||
form.find('input[name="timeout"]').parent().removeClass("error");
|
||||
}
|
||||
|
||||
// Validate mode is selected
|
||||
var mode = form.find('select[name="mode"]').val();
|
||||
if (mode === '') {
|
||||
form.find('select[name="mode"]').parent().addClass("error");
|
||||
msgbox('Please select a mode', false, 5000);
|
||||
return false;
|
||||
}else{
|
||||
form.find('select[name="mode"]').parent().removeClass("error");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function renderProxyConfigs(proxyConfigs) {
|
||||
var tableBody = $('#proxyTable tbody');
|
||||
tableBody.empty();
|
||||
if (proxyConfigs.length === 0) {
|
||||
var noResultsRow = $('<tr><td colspan="7"><i class="green check circle icon"></i>No Proxy Configs</td></tr>');
|
||||
tableBody.append(noResultsRow);
|
||||
} else {
|
||||
|
||||
proxyConfigs.forEach(function(config) {
|
||||
var runningLogo = 'Stopped';
|
||||
var runningClass = "stopped";
|
||||
var startButton = `<button onclick="startStreamProx('${config.UUID}');" class="ui button" title="Start Proxy"><i class="green play icon"></i> Start Proxy</button>`;
|
||||
if (config.Running){
|
||||
runningLogo = 'Running';
|
||||
startButton = `<button onclick="stopStreamProx('${config.UUID}');" class="ui button" title="Start Proxy"><i class="red stop icon"></i> Stop Proxy</button>`;
|
||||
runningClass = "running"
|
||||
}
|
||||
|
||||
var modeText = [];
|
||||
if (config.UseTCP){
|
||||
modeText.push("TCP")
|
||||
}
|
||||
|
||||
if (config.UseUDP){
|
||||
modeText.push("UDP")
|
||||
}
|
||||
|
||||
modeText = modeText.join(" | ")
|
||||
|
||||
var thisConfig = encodeURIComponent(JSON.stringify(config));
|
||||
|
||||
var row = $(`<tr class="tcproxConfig ${runningClass}" uuid="${config.UUID}" config="${thisConfig}">`);
|
||||
row.append($('<td>').html(`
|
||||
${config.Name}
|
||||
<div class="statusText">${runningLogo}</div>`));
|
||||
row.append($('<td>').text(config.ListeningAddress));
|
||||
row.append($('<td>').text(config.ProxyTargetAddr));
|
||||
row.append($('<td>').text(modeText));
|
||||
row.append($('<td>').text(config.Timeout));
|
||||
row.append($('<td>').html(`
|
||||
<div class="ui basic vertical fluid tiny buttons">
|
||||
${startButton}
|
||||
<button onclick="editTCPProxyConfig('${config.UUID}');" class="ui button" title="Edit Config"><i class="edit icon"></i> Edit </button>
|
||||
<button onclick="deleteTCPProxyConfig('${config.UUID}');" class="ui red basic button" title="Delete Config"><i class="trash icon"></i> Remove</button>
|
||||
</div>
|
||||
`));
|
||||
tableBody.append(row);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getConfigDetailsFromDOM(configUUID){
|
||||
let thisConfig = null;
|
||||
$(".tcproxConfig").each(function(){
|
||||
let uuid = $(this).attr("uuid");
|
||||
if (configUUID == uuid){
|
||||
//This is the one we are looking for
|
||||
thisConfig = JSON.parse(decodeURIComponent($(this).attr("config")));
|
||||
}
|
||||
});
|
||||
return thisConfig;
|
||||
}
|
||||
|
||||
function editTCPProxyConfig(configUUID){
|
||||
let targetConfig = getConfigDetailsFromDOM(configUUID);
|
||||
if (targetConfig != null){
|
||||
$("#addStreamProxyButton").hide();
|
||||
$("#editStreamProxyButton").show();
|
||||
$.each(targetConfig, function(key, value) {
|
||||
var field;
|
||||
if (key == "UseTCP"){
|
||||
let checkboxEle = $("#streamProxyForm input[name=useTCP]").parent();
|
||||
if (value === true){
|
||||
$(checkboxEle).checkbox("set checked");
|
||||
}else{
|
||||
$(checkboxEle).checkbox("set unchecked");
|
||||
}
|
||||
return;
|
||||
}else if (key == "UseUDP"){
|
||||
let checkboxEle = $("#streamProxyForm input[name=useUDP]").parent();
|
||||
if (value === true){
|
||||
$(checkboxEle).checkbox("set checked");
|
||||
}else{
|
||||
$(checkboxEle).checkbox("set unchecked");
|
||||
}
|
||||
return;
|
||||
}else if (key == "ListeningAddress"){
|
||||
field = $("#streamProxyForm input[name=listenAddr]");
|
||||
}else if (key == "ProxyTargetAddr"){
|
||||
field = $("#streamProxyForm input[name=proxyAddr]");
|
||||
}else if (key == "UUID"){
|
||||
field = $("#streamProxyForm input[name=uuid]");
|
||||
}else if (key == "Name"){
|
||||
field = $("#streamProxyForm input[name=name]");
|
||||
}else if (key == "Timeout"){
|
||||
field = $("#streamProxyForm input[name=timeout]");
|
||||
}
|
||||
|
||||
if (field != undefined && field.length > 0) {
|
||||
field.val(value);
|
||||
}
|
||||
});
|
||||
editingStreamProxyConfigUUID = configUUID;
|
||||
$("#addproxyConfig").slideDown("fast");
|
||||
|
||||
}else{
|
||||
msgbox("Unable to load target config", false);
|
||||
}
|
||||
}
|
||||
|
||||
function confirmEditTCPProxyConfig(event){
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
var form = $("#streamProxyForm");
|
||||
|
||||
var formValid = validateTCPProxyConfig(form);
|
||||
if (!formValid){
|
||||
return;
|
||||
}
|
||||
|
||||
// Send the AJAX POST request
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: '/api/streamprox/config/edit',
|
||||
method: "POST",
|
||||
data: {
|
||||
uuid: $("#streamProxyForm input[name=uuid]").val().trim(),
|
||||
name: $("#streamProxyForm input[name=name]").val().trim(),
|
||||
listenAddr: $("#streamProxyForm input[name=listenAddr]").val().trim(),
|
||||
proxyAddr: $("#streamProxyForm input[name=proxyAddr]").val().trim(),
|
||||
useTCP: $("#streamProxyForm input[name=useTCP]")[0].checked ,
|
||||
useUDP: $("#streamProxyForm input[name=useUDP]")[0].checked ,
|
||||
timeout: parseInt($("#streamProxyForm input[name=timeout]").val().trim()),
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.error) {
|
||||
msgbox(response.error, false, 6000);
|
||||
}else{
|
||||
msgbox("Config Updated");
|
||||
}
|
||||
initProxyConfigList();
|
||||
cancelStreamProxyEdit();
|
||||
|
||||
},
|
||||
error: function() {
|
||||
msgbox('An error occurred while processing the request', false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteTCPProxyConfig(configUUID){
|
||||
$.ajax({
|
||||
url: "/api/streamprox/config/delete",
|
||||
method: "POST",
|
||||
data: {uuid: configUUID},
|
||||
success: function(data){
|
||||
if (data.error != undefined){
|
||||
msgbox(data.error, false, 6000);
|
||||
}else{
|
||||
msgbox("Proxy Config Removed");
|
||||
initProxyConfigList();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//Start a TCP proxy by their config UUID
|
||||
function startStreamProx(configUUID){
|
||||
$.ajax({
|
||||
url: "/api/streamprox/config/start",
|
||||
method: "POST",
|
||||
data: {uuid: configUUID},
|
||||
success: function(data){
|
||||
if (data.error != undefined){
|
||||
msgbox(data.error, false, 6000);
|
||||
}else{
|
||||
msgbox("Service Started");
|
||||
initProxyConfigList();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
//Stop a TCP proxy by their config UUID
|
||||
function stopStreamProx(configUUID){
|
||||
$.ajax({
|
||||
url: "/api/streamprox/config/stop",
|
||||
method: "POST",
|
||||
data: {uuid: configUUID},
|
||||
success: function(data){
|
||||
if (data.error != undefined){
|
||||
msgbox(data.error, false, 6000);
|
||||
}else{
|
||||
msgbox("Service Stopped");
|
||||
initProxyConfigList();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function initProxyConfigList(){
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: '/api/streamprox/config/list',
|
||||
success: function(response) {
|
||||
renderProxyConfigs(response);
|
||||
},
|
||||
error: function() {
|
||||
msgbox('Unable to load proxy configs', false);
|
||||
}
|
||||
});
|
||||
}
|
||||
initProxyConfigList();
|
||||
</script>
|
||||
</div>
|
Reference in New Issue
Block a user