mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-06 23:57:21 +02:00

- Rewritten the logger to make it more apache log parser friendly - Fixed uptime not updating after upstream change bug - Added SSO page (wip) - Added log viewer
528 lines
26 KiB
HTML
528 lines
26 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<!-- Notes: This should be open in its original path-->
|
|
<meta charset="utf-8">
|
|
<link rel="stylesheet" href="../script/semantic/semantic.min.css">
|
|
<script src="../script/jquery-3.6.0.min.js"></script>
|
|
<script src="../script/semantic/semantic.min.js"></script>
|
|
<style>
|
|
.upstreamActions{
|
|
position: absolute;
|
|
top: 0.6em;
|
|
right: 0.6em;
|
|
}
|
|
|
|
.upstreamLink{
|
|
max-width: 220px;
|
|
display: inline-block;
|
|
word-break: break-all;
|
|
}
|
|
|
|
.upstreamEntry .ui.toggle.checkbox input:checked ~ label::before{
|
|
background-color: #00ca52 !important;
|
|
}
|
|
|
|
#activateNewUpstream.ui.toggle.checkbox input:checked ~ label::before{
|
|
background-color: #00ca52 !important;
|
|
}
|
|
|
|
#upstreamTable{
|
|
max-height: 480px;
|
|
border-radius: 0.3em;
|
|
padding: 0.3em;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.upstreamEntry.inactive{
|
|
background-color: #f3f3f3 !important;
|
|
}
|
|
|
|
.upstreamEntry{
|
|
border-radius: 0.4em !important;
|
|
border: 1px solid rgb(233, 233, 233) !important;
|
|
}
|
|
|
|
@media (max-width: 499px) {
|
|
.upstreamActions{
|
|
position: relative;
|
|
margin-top: 1em;
|
|
margin-left: 0.4em;
|
|
margin-bottom: 0.4em;
|
|
}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<br>
|
|
<div class="ui container">
|
|
<div class="ui header">
|
|
<div class="content">
|
|
Upstreams / Load Balance
|
|
<div class="sub header epname"></div>
|
|
</div>
|
|
</div>
|
|
<div class="ui divider"></div>
|
|
<div class="ui small pointing secondary menu">
|
|
<a class="item active narrowpadding" data-tab="upstreamlist">Upstreams</a>
|
|
<a class="item narrowpadding" data-tab="newupstream">Add Upstream</a>
|
|
</div>
|
|
<div class="ui tab basic segment active" data-tab="upstreamlist">
|
|
<!-- A list of current existing upstream on this reverse proxy-->
|
|
<div id="upstreamTable">
|
|
<div class="ui segment">
|
|
<a><i class="ui loading spinner icon"></i> Loading</a>
|
|
</div>
|
|
</div>
|
|
<div class="ui message">
|
|
<i class="ui blue info circle icon"></i> Weighted random will be used for load-balancing. Set weight to 0 for fallback only.
|
|
</div>
|
|
</div>
|
|
<div class="ui tab basic segment" data-tab="newupstream">
|
|
<!-- Web Form to create a new upstream -->
|
|
<h4 class="ui header">
|
|
<i class="green add icon"></i>
|
|
<div class="content">
|
|
Add Upstream Server
|
|
<div class="sub header">Create new load balance or fallback upstream origin</div>
|
|
</div>
|
|
</h4>
|
|
<p style="margin-bottom: 0.4em;">Target IP address with port</p>
|
|
<div class="ui fluid small input">
|
|
<input type="text" id="originURL" onchange="cleanProxyTargetValue(this);"><br>
|
|
</div>
|
|
<small>E.g. 192.168.0.101:8000 or example.com</small>
|
|
<br><br>
|
|
<div id="activateNewUpstream" class="ui toggle checkbox" style="display:inline-block;">
|
|
<input type="checkbox" id="activateNewUpstreamCheckbox" style="margin-top: 0.4em;" checked>
|
|
<label>Activate<br>
|
|
<small>Enable this upstream for load balancing</small></label>
|
|
</div><br>
|
|
<div class="ui checkbox" style="margin-top: 1.2em;">
|
|
<input type="checkbox" id="requireTLS">
|
|
<label>Require TLS<br>
|
|
<small>Proxy target require HTTPS connection</small></label>
|
|
</div><br>
|
|
<div class="ui checkbox" style="margin-top: 0.6em;">
|
|
<input type="checkbox" id="skipTlsVerification">
|
|
<label>Skip Verification<br>
|
|
<small>Check this if proxy target is using self signed certificates</small></label>
|
|
</div><br>
|
|
<div class="ui checkbox" style="margin-top: 0.4em;">
|
|
<input type="checkbox" id="SkipWebSocketOriginCheck" checked>
|
|
<label>Skip WebSocket Origin Check<br>
|
|
<small>Check this to allow cross-origin websocket requests</small></label>
|
|
</div>
|
|
<br><br>
|
|
<button class="ui basic button" onclick="addNewUpstream();"><i class="ui green circle add icon"></i> Create</button>
|
|
</div>
|
|
<div class="ui divider"></div>
|
|
<div class="field" >
|
|
<button class="ui basic button" style="float: right;" onclick="closeThisWrapper();">Close</button>
|
|
</div>
|
|
</div>
|
|
<br><br><br><br>
|
|
|
|
</div>
|
|
<script>
|
|
let origins = [];
|
|
let editingEndpoint = {};
|
|
$('.menu .item').tab();
|
|
|
|
|
|
function initOriginList(){
|
|
$.ajax({
|
|
url: "/api/proxy/upstream/list",
|
|
method: "POST",
|
|
data: {
|
|
"type":"host",
|
|
"ep": editingEndpoint.ep
|
|
},
|
|
success: function(data){
|
|
if (data.error != undefined){
|
|
//This endpoint not exists?
|
|
alert(data.error);
|
|
return;
|
|
}else{
|
|
$("#upstreamTable").html("");
|
|
|
|
if (data.ActiveOrigins.length == 0){
|
|
//There is no upstream for this proxy rule
|
|
$("#upstreamTable").append(`<tr>
|
|
<td colspan="2"><b><i class="ui yellow exclamation triangle icon"></i> No Active Upstream Origin</b><br>
|
|
This HTTP proxy rule will always return Error 521 when requested. To fix this, add or enable a upstream origin to this proxy endpoint.
|
|
<div class="ui divider"></div>
|
|
</td>
|
|
</tr>`);
|
|
}
|
|
|
|
data.ActiveOrigins.forEach(upstream => {
|
|
renderUpstreamEntryToTable(upstream, true);
|
|
});
|
|
|
|
data.InactiveOrigins.forEach(upstream => {
|
|
renderUpstreamEntryToTable(upstream, false);
|
|
});
|
|
|
|
let totalUpstreams = data.ActiveOrigins.length + data.InactiveOrigins.length;
|
|
if (totalUpstreams == 1){
|
|
$(".lowPriorityButton").addClass('disabled');
|
|
}
|
|
|
|
if (parent && parent.document.getElementById("httpProxyList") != null){
|
|
//Also update the parent display
|
|
let element = $(parent.document.getElementById("httpProxyList")).find(".upstreamList.editing");
|
|
let upstreams = "";
|
|
data.ActiveOrigins.forEach(upstream => {
|
|
//Check if the upstreams require TLS connections
|
|
let tlsIcon = "";
|
|
if (upstream.RequireTLS){
|
|
tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
|
|
if (upstream.SkipCertValidations){
|
|
tlsIcon = `<i class="yellow lock icon" title="TLS/SSL mode without verification"></i>`
|
|
}
|
|
}
|
|
|
|
let upstreamLink = `${upstream.RequireTLS?"https://":"http://"}${upstream.OriginIpOrDomain}`;
|
|
|
|
upstreams += `<a href="${upstreamLink}" target="_blank">${upstream.OriginIpOrDomain} ${tlsIcon}</a><br>`;
|
|
});
|
|
|
|
if (data.ActiveOrigins.length == 0){
|
|
upstreams = `<i class="ui yellow exclamation triangle icon"></i> No Active Upstream Origin<br>`
|
|
}
|
|
$(element).html(upstreams);
|
|
}
|
|
|
|
$(".ui.checkbox").checkbox();
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
function renderUpstreamEntryToTable(upstream, isActive){
|
|
function newUID(){return"00000000-0000-4000-8000-000000000000".replace(/0/g,function(){return(0|Math.random()*16).toString(16)})};
|
|
let tlsIcon = "";
|
|
if (upstream.RequireTLS){
|
|
tlsIcon = `<i class="green lock icon" title="TLS Mode"></i>`;
|
|
if (upstream.SkipCertValidations){
|
|
tlsIcon = `<i class="yellow lock icon" title="TLS/SSL mode without verification"></i>`
|
|
}
|
|
}
|
|
|
|
//Priority Arrows
|
|
let downArrowClass = "";
|
|
if (upstream.Weight == 0 ){
|
|
//Cannot go any lower
|
|
downArrowClass = "disabled";
|
|
}
|
|
let url = `${upstream.RequireTLS?"https://":"http://"}${upstream.OriginIpOrDomain}`
|
|
let payload = encodeURIComponent(JSON.stringify(upstream));
|
|
let domUID = newUID();
|
|
$("#upstreamTable").append(`<div class="ui upstreamEntry ${isActive?"":"inactive"} basic segment" data-domid="${domUID}" data-payload="${payload}" data-priority="${upstream.Priority}">
|
|
<h4 class="ui header">
|
|
<div class="ui toggle checkbox" style="display:inline-block;">
|
|
<input type="checkbox" class="enableState" name="enabled" style="margin-top: 0.4em;" onchange="saveUpstreamUpdate('${domUID}');" ${isActive?"checked":""}>
|
|
<label></label>
|
|
</div>
|
|
<div class="content">
|
|
<a href="${url}" target="_blank" class="upstreamLink">${upstream.OriginIpOrDomain} ${tlsIcon}</a>
|
|
<div class="sub header">${isActive?(upstream.Weight==0?"Fallback Only":"Active"):"Inactive"} | Weight: ${upstream.Weight}x </div>
|
|
</div>
|
|
</h4>
|
|
<div class="advanceOptions" style="display:none;">
|
|
<div class="upstreamOriginField">
|
|
<p>New upstream origin IP address or domain</p>
|
|
<div class="ui small fluid input" style="margin-top: -0.6em;">
|
|
<input type="text" class="newOrigin" value="${upstream.OriginIpOrDomain}" onchange="handleAutoOriginClean('${domUID}');">
|
|
</div>
|
|
<small>e.g. 192.168.0.101:8000 or example.com</small>
|
|
</div>
|
|
<div class="ui divider"></div>
|
|
<div class="ui checkbox">
|
|
<input type="checkbox" class="reqTLSCheckbox" ${upstream.RequireTLS?"checked":""}>
|
|
<label>Require TLS<br>
|
|
<small>Proxy target require HTTPS connection</small></label>
|
|
</div><br>
|
|
<div class="ui checkbox" style="margin-top: 0.6em;">
|
|
<input type="checkbox" class="skipVerificationCheckbox" ${upstream.SkipCertValidations?"checked":""}>
|
|
<label>Skip Verification<br>
|
|
<small>Check this if proxy target is using self signed certificates</small></label>
|
|
</div><br>
|
|
<div class="ui checkbox" style="margin-top: 0.4em;">
|
|
<input type="checkbox" class="SkipWebSocketOriginCheck" ${upstream.SkipWebSocketOriginCheck?"checked":""}>
|
|
<label>Skip WebSocket Origin Check<br>
|
|
<small>Check this to allow cross-origin websocket requests</small></label>
|
|
</div><br>
|
|
</div>
|
|
<div class="upstreamActions">
|
|
<!-- Change Priority -->
|
|
<button class="ui basic circular icon button highPriorityButton" title="Increase Weight" onclick="increaseUpstreamWeight('${domUID}');"><i class="ui arrow up icon"></i></button>
|
|
<button class="ui basic circular icon button lowPriorityButton ${downArrowClass}" title="Reduce Weight" onclick="decreaseUpstreamWeight('${domUID}');"><i class="ui arrow down icon"></i></button>
|
|
<button class="ui basic circular icon editbtn button" onclick="handleUpstreamOriginEdit('${domUID}');" title="Edit Upstream Destination"><i class="ui grey edit icon"></i></button>
|
|
<button style="display:none;" class="ui basic circular icon trashbtn button" onclick="removeUpstream('${domUID}');" title="Remove Upstream"><i class="ui red trash icon"></i></button>
|
|
<button style="display:none;" class="ui basic circular icon savebtn button" onclick="saveUpstreamUpdate('${domUID}');" title="Save Changes"><i class="ui green save icon"></i></button>
|
|
<button style="display:none;" class="ui basic circular icon cancelbtn button" onclick="initOriginList();" title="Cancel"><i class="ui grey times icon"></i></button>
|
|
</div>
|
|
</div>`);
|
|
}
|
|
|
|
/* New Upstream Origin Functions */
|
|
|
|
//Clearn the proxy target value, make sure user do not enter http:// or https://
|
|
//and auto select TLS checkbox if https:// exists
|
|
function cleanProxyTargetValue(input){
|
|
let targetDomain = $(input).val().trim();
|
|
if (targetDomain.startsWith("http://")){
|
|
targetDomain = targetDomain.substr(7);
|
|
$(input).val(targetDomain);
|
|
$("#requireTLS").parent().checkbox("set unchecked");
|
|
}else if (targetDomain.startsWith("https://")){
|
|
targetDomain = targetDomain.substr(8);
|
|
$(input).val(targetDomain);
|
|
$("#requireTLS").parent().checkbox("set checked");
|
|
}else{
|
|
//URL does not contains https or http protocol tag
|
|
//sniff header
|
|
$.ajax({
|
|
url: "/api/proxy/tlscheck",
|
|
data: {url: targetDomain},
|
|
success: function(data){
|
|
if (data.error != undefined){
|
|
|
|
}else if (data == "https"){
|
|
$("#requireTLS").parent().checkbox("set checked");
|
|
}else if (data == "http"){
|
|
$("#requireTLS").parent().checkbox("set unchecked");
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
//Add a new upstream to this http proxy rule
|
|
function addNewUpstream(){
|
|
let origin = $("#originURL").val().trim();
|
|
let requireTLS = $("#requireTLS")[0].checked;
|
|
let skipVerification = $("#skipTlsVerification")[0].checked;
|
|
let skipWebSocketOriginCheck = $("#SkipWebSocketOriginCheck")[0].checked;
|
|
let activateLoadbalancer = $("#activateNewUpstreamCheckbox")[0].checked;
|
|
|
|
if (origin == ""){
|
|
parent.msgbox("Upstream origin cannot be empty", false);
|
|
return;
|
|
}
|
|
|
|
$.ajax({
|
|
url: "/api/proxy/upstream/add",
|
|
method: "POST",
|
|
data:{
|
|
"ep": editingEndpoint.ep,
|
|
"origin": origin,
|
|
"tls": requireTLS,
|
|
"tlsval": skipVerification,
|
|
"bpwsorg":skipWebSocketOriginCheck,
|
|
"active": activateLoadbalancer,
|
|
},
|
|
success: function(data){
|
|
if (data.error != undefined){
|
|
parent.msgbox(data.error, false);
|
|
}else{
|
|
parent.msgbox("New upstream origin added");
|
|
initOriginList();
|
|
$("#originURL").val("");
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
//Get a upstream setting data from DOM element
|
|
function getUpstreamSettingFromDOM(upstream){
|
|
//Get the original setting from DOM payload
|
|
let originalSettings = $(upstream).attr("data-payload");
|
|
originalSettings = JSON.parse(decodeURIComponent(originalSettings));
|
|
|
|
//Get the updated settings if any
|
|
let requireTLS = $(upstream).find(".reqTLSCheckbox")[0].checked;
|
|
let skipTLSVerification = $(upstream).find(".skipVerificationCheckbox")[0].checked;
|
|
let skipWebSocketOriginCheck = $(upstream).find(".SkipWebSocketOriginCheck")[0].checked;
|
|
|
|
//Update the original setting with new one just applied
|
|
originalSettings.OriginIpOrDomain = $(upstream).find(".newOrigin").val();
|
|
originalSettings.RequireTLS = requireTLS;
|
|
originalSettings.SkipCertValidations = skipTLSVerification;
|
|
originalSettings.SkipWebSocketOriginCheck = skipWebSocketOriginCheck;
|
|
|
|
//console.log(originalSettings);
|
|
return originalSettings;
|
|
}
|
|
|
|
//Handle setting change on upstream config
|
|
function saveUpstreamUpdate(upstreamDomID){
|
|
let targetDOM = $(`.upstreamEntry[data-domid=${upstreamDomID}]`);
|
|
let originalSettings = $(targetDOM).attr("data-payload");
|
|
originalSettings = JSON.parse(decodeURIComponent(originalSettings));
|
|
let newConfig = getUpstreamSettingFromDOM(targetDOM);
|
|
let isActive = $(targetDOM).find(".enableState")[0].checked;
|
|
console.log(newConfig);
|
|
$.ajax({
|
|
url: "/api/proxy/upstream/update",
|
|
method: "POST",
|
|
data: {
|
|
ep: editingEndpoint.ep,
|
|
origin: originalSettings.OriginIpOrDomain, //Original ip or domain as key
|
|
payload: JSON.stringify(newConfig),
|
|
active: isActive,
|
|
},
|
|
success: function(data){
|
|
if (data.error != undefined){
|
|
parent.msgbox(data.error, false);
|
|
|
|
}else{
|
|
parent.msgbox("Upstream setting updated");
|
|
initOriginList();
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
//Edit the upstream origin of this upstream entry
|
|
function handleUpstreamOriginEdit(upstreamDomID){
|
|
let targetDOM = $(`.upstreamEntry[data-domid=${upstreamDomID}]`);
|
|
let originalSettings = getUpstreamSettingsFromDomID(upstreamDomID);
|
|
let originIP = originalSettings.OriginIpOrDomain;
|
|
|
|
//Change the UI to edit mode
|
|
$(".editbtn").hide();
|
|
$(".lowPriorityButton").hide();
|
|
$(".highPriorityButton").hide();
|
|
$(targetDOM).find(".trashbtn").show();
|
|
$(targetDOM).find(".savebtn").show();
|
|
$(targetDOM).find(".cancelbtn").show();
|
|
$(targetDOM).find(".advanceOptions").show();
|
|
}
|
|
|
|
//Check if the entered URL contains http or https
|
|
function handleAutoOriginClean(domid){
|
|
let targetDOM = $(`.upstreamEntry[data-domid=${domid}]`);
|
|
let targetTLSCheckbox = $(targetDOM).find(".reqTLSCheckbox");
|
|
let targetDomain = $(targetDOM).find(".newOrigin").val().trim();
|
|
if (targetDomain.startsWith("http://")){
|
|
targetDomain = targetDomain.substr(7);
|
|
$(input).val(targetDomain);
|
|
$(targetTLSCheckbox).parent().checkbox("set unchecked");
|
|
}else if (targetDomain.startsWith("https://")){
|
|
targetDomain = targetDomain.substr(8);
|
|
$(input).val(targetDomain);
|
|
$(targetTLSCheckbox).parent().checkbox("set checked");
|
|
}else{
|
|
//URL does not contains https or http protocol tag
|
|
//sniff header
|
|
$.ajax({
|
|
url: "/api/proxy/tlscheck",
|
|
data: {url: targetDomain},
|
|
success: function(data){
|
|
if (data.error != undefined){
|
|
|
|
}else if (data == "https"){
|
|
$(targetTLSCheckbox).parent().checkbox("set checked");
|
|
}else if (data == "http"){
|
|
$(targetTLSCheckbox).parent().checkbox("set unchecked");
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
function getUpstreamSettingsFromDomID(domid){
|
|
let targetDOM = $(`.upstreamEntry[data-domid=${domid}]`);
|
|
if (targetDOM.length == 0){
|
|
return undefined;
|
|
}
|
|
let upstreamSettings = $(targetDOM).attr("data-payload");
|
|
upstreamSettings = JSON.parse(decodeURIComponent(upstreamSettings));
|
|
return upstreamSettings;
|
|
}
|
|
|
|
function increaseUpstreamWeight(domid){
|
|
let upstreamSetting = getUpstreamSettingsFromDomID(domid);
|
|
let originIP = upstreamSetting.OriginIpOrDomain;
|
|
let currentWeight = upstreamSetting.Weight;
|
|
setUpstreamWeight(originIP, currentWeight+1);
|
|
}
|
|
|
|
function decreaseUpstreamWeight(domid){
|
|
let upstreamSetting = getUpstreamSettingsFromDomID(domid);
|
|
let originIP = upstreamSetting.OriginIpOrDomain;
|
|
let currentWeight = upstreamSetting.Weight;
|
|
setUpstreamWeight(originIP, currentWeight-1);
|
|
}
|
|
|
|
//Set a weight of a upstream
|
|
function setUpstreamWeight(originIP, newWeight){
|
|
$.ajax({
|
|
url: "/api/proxy/upstream/setPriority",
|
|
method: "POST",
|
|
data: {
|
|
ep: editingEndpoint.ep,
|
|
origin: originIP,
|
|
weight: newWeight,
|
|
},
|
|
success: function(data){
|
|
if (data.error != undefined){
|
|
parent.msgbox(data.error, false);
|
|
}else{
|
|
parent.msgbox("Upstream Weight Updated");
|
|
initOriginList();
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
//Handle removal of an upstream
|
|
function removeUpstream(domid){
|
|
let targetDOM = $(`.upstreamEntry[data-domid=${domid}]`);
|
|
let originalSettings = $(targetDOM).attr("data-payload");
|
|
originalSettings = JSON.parse(decodeURIComponent(originalSettings));
|
|
let UpstreamKey = originalSettings.OriginIpOrDomain;
|
|
if (!confirm("Confirm removing " + UpstreamKey + " from upstream?")){
|
|
return;
|
|
}
|
|
//Remove the upstream
|
|
$.ajax({
|
|
url: "/api/proxy/upstream/remove",
|
|
method: "POST",
|
|
data: {
|
|
ep: editingEndpoint.ep,
|
|
origin: originalSettings.OriginIpOrDomain, //Original ip or domain as key
|
|
},
|
|
success: function(data){
|
|
if (data.error != undefined){
|
|
parent.msgbox(data.error, false);
|
|
|
|
}else{
|
|
parent.msgbox("Upstream deleted");
|
|
initOriginList();
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
if (window.location.hash.length > 1){
|
|
let payloadHash = window.location.hash.substr(1);
|
|
try{
|
|
payloadHash = JSON.parse(decodeURIComponent(payloadHash));
|
|
$(".epname").text(payloadHash.ep);
|
|
editingEndpoint = payloadHash;
|
|
initOriginList();
|
|
}catch(ex){
|
|
console.log("Unable to load endpoint data from hash")
|
|
}
|
|
}
|
|
|
|
function closeThisWrapper(){
|
|
parent.hideSideWrapper(true);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html> |