mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-08-07 21:58:29 +02:00
v3.0.2 init commit
+ Fixed zeroSSL bug (said by @yeungalan ) #45 + Fixed manual renew button bug + Seperated geodb module with access controller + Added per hosts access control (experimental) #69 + Fixed basic auth not working on TLS bypass mode bug + Fixed empty domain crash bug #120
This commit is contained in:
267
src/web/snippet/accessRuleEditor.html
Normal file
267
src/web/snippet/accessRuleEditor.html
Normal file
@@ -0,0 +1,267 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- Notes: This should be open in its original path-->
|
||||
<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>
|
||||
#refreshAccessRuleListBtn{
|
||||
position: absolute;
|
||||
top: 0.4em;
|
||||
right: 1em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<br>
|
||||
<div class="ui container">
|
||||
<div class="ui header">
|
||||
<div class="content">
|
||||
Access Rule Editor
|
||||
<div class="sub header">Create, Edit or Remove Access Rules</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<div class="ui top attached tabular menu">
|
||||
<a class="active item" data-tab="new"><i class="ui green add icon"></i> New</a>
|
||||
<a class="item" data-tab="edit"><i class="ui grey edit icon"></i> Edit</a>
|
||||
</div>
|
||||
<div class="ui bottom attached active tab segment" data-tab="new">
|
||||
<p>Create a new Access Rule</p>
|
||||
<form class="ui form" id="accessRuleForm">
|
||||
<div class="field">
|
||||
<label>Rule Name</label>
|
||||
<input type="text" name="accessRuleName" placeholder="Rule Name" required>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Description</label>
|
||||
<textarea name="description" placeholder="Description" required></textarea>
|
||||
</div>
|
||||
<button class="ui basic button" type="submit"><i class="ui green add icon"></i> Create</button>
|
||||
</form>
|
||||
<br>
|
||||
</div>
|
||||
<div class="ui bottom attached tab segment" data-tab="edit">
|
||||
<p>Select an Access Rule to edit</p>
|
||||
<button id="refreshAccessRuleListBtn" class="ui circular basic icon button" onclick="reloadAccessRuleList()"><i class="ui green refresh icon"></i></button>
|
||||
<div class="ui selection fluid dropdown" id="accessRuleSelector">
|
||||
<input type="hidden" name="targetAccessRule" value="default">
|
||||
<i class="dropdown icon"></i>
|
||||
<div class="default text"></div>
|
||||
<div class="menu" id="accessRuleList">
|
||||
<div class="item" data-value="default"><i class="ui yellow star icon"></i> Default</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<form class="ui form" id="modifyRuleInfo">
|
||||
<div class="disabled field">
|
||||
<label>Rule ID</label>
|
||||
<input type="text" name="accessRuleUUID">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Rule Name</label>
|
||||
<input type="text" name="accessRuleName" placeholder="Rule Name" required>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>Description</label>
|
||||
<textarea name="description" placeholder="Description" required></textarea>
|
||||
</div>
|
||||
<button class="ui basic button" type="submit"><i class="ui green save icon"></i> Save Changes</button>
|
||||
<button class="ui basic button" onclick="removeAccessRule(event);"><i class="ui red trash icon"></i> Remove Rule</button>
|
||||
</form>
|
||||
</div>
|
||||
<br>
|
||||
<button class="ui basic button" style="float: right;" onclick="parent.hideSideWrapper();"><i class="remove icon"></i> Close</button>
|
||||
<br><br><br>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let accessRuleList = [];
|
||||
$('.dropdown').dropdown();
|
||||
$('.menu .item').tab();
|
||||
|
||||
function handleCreateNewAccessRule(event) {
|
||||
event.preventDefault(); // Prevent the default form submission
|
||||
const formData = new FormData(event.target);
|
||||
const accessRuleName = formData.get('accessRuleName');
|
||||
const description = formData.get('description');
|
||||
|
||||
console.log('Access Rule Name:', accessRuleName);
|
||||
console.log('Description:', description);
|
||||
|
||||
$("#accessRuleForm input[name='accessRuleName']").val("");
|
||||
$("#accessRuleForm textarea[name='description']").val("");
|
||||
|
||||
$.ajax({
|
||||
url: "/api/access/create",
|
||||
method: "POST",
|
||||
data: {
|
||||
"name": accessRuleName,
|
||||
"desc": description
|
||||
},
|
||||
success: function(data){
|
||||
if (data.error != undefined){
|
||||
parent.msgbox(data.error, false);
|
||||
}else{
|
||||
parent.msgbox("Access Rule Created", true);
|
||||
reloadAccessRuleList();
|
||||
if (parent != undefined && parent.reloadAccessRules != undefined){
|
||||
parent.reloadAccessRules();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//Handle on change of the dropdown selection
|
||||
function handleSelectEditingAccessRule(){
|
||||
const selectedValue = document.querySelector('#accessRuleSelector').querySelector('input').value;
|
||||
console.log('Selected Value:', selectedValue);
|
||||
//Load the information from list
|
||||
loadAccessRuleInfoIntoEditFields(selectedValue);
|
||||
}
|
||||
|
||||
//Load the access rules information into the fields
|
||||
function loadAccessRuleInfoIntoEditFields(targetAccessRuleUUID){
|
||||
var targetAccessRule = undefined;
|
||||
for (var i = 0; i < accessRuleList.length; i++){
|
||||
let thisAccessRule = accessRuleList[i];
|
||||
if (thisAccessRule.ID == targetAccessRuleUUID){
|
||||
targetAccessRule = thisAccessRule;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetAccessRule == undefined){
|
||||
//Target exists rule no longer exists
|
||||
return;
|
||||
}
|
||||
|
||||
let accessRuleID = targetAccessRule.ID;
|
||||
let accessRuleName = targetAccessRule.Name;
|
||||
let accessRuleDesc = targetAccessRule.Desc;
|
||||
|
||||
//Load the information into the form input field
|
||||
//Load the information into the form input field
|
||||
document.querySelector('#modifyRuleInfo input[name="accessRuleUUID"]').value = accessRuleID;
|
||||
document.querySelector('#modifyRuleInfo input[name="accessRuleName"]').value = accessRuleName;
|
||||
document.querySelector('#modifyRuleInfo textarea[name="description"]').value = accessRuleDesc;
|
||||
}
|
||||
|
||||
//Bind events to modify rule form
|
||||
document.getElementById('modifyRuleInfo').addEventListener('submit', function(event){
|
||||
event.preventDefault(); // Prevent the default form submission
|
||||
|
||||
const accessRuleUUID = document.querySelector('#modifyRuleInfo input[name="accessRuleUUID"]').value;
|
||||
const accessRuleName = document.querySelector('#modifyRuleInfo input[name="accessRuleName"]').value;
|
||||
const description = document.querySelector('#modifyRuleInfo textarea[name="description"]').value;
|
||||
|
||||
|
||||
console.log('Access Rule UUID:', accessRuleUUID);
|
||||
console.log('Access Rule Name:', accessRuleName);
|
||||
console.log('Description:', description);
|
||||
|
||||
$.ajax({
|
||||
url: "/api/access/update",
|
||||
method: "POST",
|
||||
data: {
|
||||
"id":accessRuleUUID,
|
||||
"name":accessRuleName,
|
||||
"desc":description
|
||||
},
|
||||
success: function(data){
|
||||
if (data.error != undefined){
|
||||
parent.msgbox(data.error, false);
|
||||
}else{
|
||||
parent.msgbox("Access rule updated", true);
|
||||
initAccessRuleList(function(){
|
||||
$("#accessRuleSelector").dropdown("set selected", accessRuleUUID);
|
||||
loadAccessRuleInfoIntoEditFields(accessRuleUUID);
|
||||
});
|
||||
if (parent != undefined && parent.reloadAccessRules != undefined){
|
||||
parent.reloadAccessRules();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
function initAccessRuleList(callback=undefined){
|
||||
$.get("/api/access/list", function(data){
|
||||
if (data.error == undefined){
|
||||
$("#accessRuleList").html("");
|
||||
data.forEach(function(rule){
|
||||
let icon = `<i class="ui grey filter icon"></i>`;
|
||||
if (rule.ID == "default"){
|
||||
icon = `<i class="ui yellow star icon"></i>`;
|
||||
}else if (rule.BlacklistEnabled && !rule.WhitelistEnabled){
|
||||
//This is a blacklist filter
|
||||
icon = `<i class="ui red filter icon"></i>`;
|
||||
}else if (rule.WhitelistEnabled && !rule.BlacklistEnabled){
|
||||
//This is a whitelist filter
|
||||
icon = `<i class="ui green filter icon"></i>`;
|
||||
}
|
||||
$("#accessRuleList").append(`<div class="item" data-value="${rule.ID}">${icon} ${rule.Name}</div>`);
|
||||
});
|
||||
accessRuleList = data;
|
||||
$(".dropdown").dropdown();
|
||||
if (callback != undefined){
|
||||
callback();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
initAccessRuleList(function(){
|
||||
$("#accessRuleSelector").dropdown("set selected", "default");
|
||||
loadAccessRuleInfoIntoEditFields("default");
|
||||
});
|
||||
|
||||
function reloadAccessRuleList(){
|
||||
initAccessRuleList(function(){
|
||||
$("#accessRuleSelector").dropdown("set selected", "default");
|
||||
loadAccessRuleInfoIntoEditFields("default");
|
||||
});
|
||||
}
|
||||
|
||||
function removeAccessRule(event){
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
let accessRuleUUID = $("#modifyRuleInfo input[name='accessRuleUUID']").val();
|
||||
if (accessRuleUUID == ""){
|
||||
return;
|
||||
}
|
||||
if (accessRuleUUID == "default"){
|
||||
parent.msgbox("Default access rule cannot be removed", false);
|
||||
return;
|
||||
}
|
||||
let accessRuleName = $("#modifyRuleInfo input[name='accessRuleName']").val();
|
||||
if (confirm("Confirm removing access rule " + accessRuleName + "?")){
|
||||
$.ajax({
|
||||
url: "/api/access/remove",
|
||||
data: {
|
||||
"id": accessRuleUUID
|
||||
},
|
||||
method: "POST",
|
||||
success: function(data){
|
||||
if (data.error != undefined){
|
||||
parent.msgbox(data.error, false);
|
||||
}else{
|
||||
parent.msgbox("Access rule removed", true);
|
||||
reloadAccessRuleList();
|
||||
if (parent != undefined && parent.reloadAccessRules != undefined){
|
||||
parent.reloadAccessRules();
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
document.getElementById('accessRuleSelector').addEventListener('change', handleSelectEditingAccessRule);
|
||||
document.getElementById('accessRuleForm').addEventListener('submit', handleCreateNewAccessRule);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@@ -118,6 +118,14 @@
|
||||
<label>ACME Server URL</label>
|
||||
<input id="caURL" type="text" placeholder="https://example.com/acme/dictionary">
|
||||
</div>
|
||||
<div class="field" id="kidInput" style="display:none;">
|
||||
<label>EAB Credentials (KID) for current provider</label>
|
||||
<input id="eab_kid" type="text" placeholder="Leave this field blank to keep the current configuration">
|
||||
</div>
|
||||
<div class="field" id="hmacInput" style="display:none;">
|
||||
<label>EAB HMAC Key for current provider</label>
|
||||
<input id="eab_hmac" type="text" placeholder="Leave this field blank to keep the current configuration">
|
||||
</div>
|
||||
<div class="field" id="skipTLS" style="display:none;">
|
||||
<div class="ui checkbox">
|
||||
<input type="checkbox" id="skipTLSCheckbox">
|
||||
@@ -314,19 +322,88 @@
|
||||
// Button click event handler for obtaining certificate
|
||||
$("#obtainButton").click(function() {
|
||||
$("#obtainButton").addClass("loading").addClass("disabled");
|
||||
updateCertificateEAB();
|
||||
obtainCertificate();
|
||||
});
|
||||
|
||||
$("input[name=ca]").on('change', function() {
|
||||
if(this.value == "Custom ACME Server") {
|
||||
$("#caInput").show();
|
||||
$("#kidInput").show();
|
||||
$("#hmacInput").show();
|
||||
$("#skipTLS").show();
|
||||
} else {
|
||||
} else if (this.value == "ZeroSSL") {
|
||||
$("#kidInput").show();
|
||||
$("#hmacInput").show();
|
||||
} else if (this.value == "Buypass") {
|
||||
$("#kidInput").show();
|
||||
$("#hmacInput").show();
|
||||
}else {
|
||||
$("#caInput").hide();
|
||||
$("#skipTLS").hide();
|
||||
$("#kidInput").hide();
|
||||
$("#hmacInput").hide();
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
// Obtain certificate from API
|
||||
function updateCertificateEAB() {
|
||||
var ca = $("#ca").dropdown("get value");
|
||||
var caURL = "";
|
||||
if (ca == "Custom ACME Server") {
|
||||
ca = "custom";
|
||||
caURL = $("#caURL").val();
|
||||
}else if(ca == "Buypass") {
|
||||
caURL = "https://api.buypass.com/acme/directory";
|
||||
}else if(ca == "ZeroSSL") {
|
||||
caURL = "https://acme.zerossl.com/v2/DV90";
|
||||
}
|
||||
|
||||
if(caURL == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
var kid = $("#eab_kid").val();
|
||||
var hmac = $("#eab_hmac").val();
|
||||
|
||||
if(kid == "" || hmac == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(caURL + " " + kid + " " + hmac);
|
||||
|
||||
$.ajax({
|
||||
url: "/api/acme/autoRenew/setEAB",
|
||||
method: "GET",
|
||||
data: {
|
||||
acmeDirectoryURL: caURL,
|
||||
kid: kid,
|
||||
hmacEncoded: hmac,
|
||||
},
|
||||
success: function(response) {
|
||||
//$("#obtainButton").removeClass("loading").removeClass("disabled");
|
||||
if (response.error) {
|
||||
console.log("Error:", response.error);
|
||||
// Show error message
|
||||
parent.msgbox(response.error, false, 12000);
|
||||
} else {
|
||||
console.log("Certificate EAB updated successfully");
|
||||
// Show success message
|
||||
parent.msgbox("Certificate EAB updated successfully");
|
||||
|
||||
// Renew the parent certificate list
|
||||
parent.initManagedDomainCertificateList();
|
||||
}
|
||||
},
|
||||
error: function(error) {
|
||||
//$("#obtainButton").removeClass("loading").removeClass("disabled");
|
||||
console.log("Failed to update EAB configuration:", error);
|
||||
parent.msgbox("Failed to update EAB configuration");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Obtain certificate from API
|
||||
function obtainCertificate() {
|
||||
var domains = $("#domainsInput").val();
|
||||
|
187
src/web/snippet/hostAccessEditor.html
Normal file
187
src/web/snippet/hostAccessEditor.html
Normal file
@@ -0,0 +1,187 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<!-- Notes: This should be open in its original path-->
|
||||
<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>
|
||||
.accessRule{
|
||||
cursor: pointer;
|
||||
border-radius: 0.4em !important;
|
||||
border: 1px solid rgb(233, 233, 233) !important;
|
||||
}
|
||||
|
||||
.accessRule:hover{
|
||||
background-color: rgb(241, 241, 241) !important;
|
||||
}
|
||||
|
||||
.accessRule.active{
|
||||
background-color: rgb(241, 241, 241) !important;
|
||||
}
|
||||
|
||||
.accessRule .selected{
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
right: 0.6em;
|
||||
}
|
||||
|
||||
.accessRule:not(.active) .selected{
|
||||
display:none;
|
||||
}
|
||||
|
||||
#accessRuleList{
|
||||
padding: 0.6em;
|
||||
border: 1px solid rgb(228, 228, 228);
|
||||
border-radius: 0.4em !important;
|
||||
max-height: calc(100vh - 15em);
|
||||
min-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<br>
|
||||
<div class="ui container">
|
||||
<div class="ui header">
|
||||
<div class="content">
|
||||
Host Access Settings
|
||||
<div class="sub header" id="epname"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ui divider"></div>
|
||||
<p>Select an access rule to apply blacklist / whitelist filtering</p>
|
||||
<div id="accessRuleList">
|
||||
<div class="ui segment accessRule">
|
||||
<div class="ui header">
|
||||
<i class="filter icon"></i>
|
||||
<div class="content">
|
||||
Account Settings
|
||||
<div class="sub header">Manage your preferences</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<button class="ui basic button" onclick="applyChangeAndClose()"><i class="ui green check icon"></i> Apply Change</button>
|
||||
|
||||
<button class="ui basic button" style="float: right;" onclick="parent.hideSideWrapper();"><i class="remove icon"></i> Close</button>
|
||||
<br><br><br>
|
||||
|
||||
</div>
|
||||
<script>
|
||||
let editingEndpoint = {};
|
||||
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;
|
||||
}catch(ex){
|
||||
console.log("Unable to load endpoint data from hash")
|
||||
}
|
||||
}
|
||||
|
||||
function initAccessRuleList(callback = undefined){
|
||||
$("#accessRuleList").html("<small>Loading</small>");
|
||||
$.get("/api/access/list", function(data){
|
||||
if (data.error == undefined){
|
||||
$("#accessRuleList").html("");
|
||||
data.forEach(function(rule){
|
||||
let icon = `<i class="ui grey filter icon"></i>`;
|
||||
if (rule.ID == "default"){
|
||||
icon = `<i class="ui yellow star icon"></i>`;
|
||||
}else if (rule.BlacklistEnabled && !rule.WhitelistEnabled){
|
||||
//This is a blacklist filter
|
||||
icon = `<i class="ui red filter icon"></i>`;
|
||||
}else if (rule.WhitelistEnabled && !rule.BlacklistEnabled){
|
||||
//This is a whitelist filter
|
||||
icon = `<i class="ui green filter icon"></i>`;
|
||||
}else if (rule.WhitelistEnabled && rule.BlacklistEnabled){
|
||||
//Whitelist and blacklist filter
|
||||
icon = `<i class="ui yellow filter icon"></i>`;
|
||||
}
|
||||
|
||||
$("#accessRuleList").append(`<div class="ui basic segment accessRule" ruleid="${rule.ID}" onclick="selectThisRule(this);">
|
||||
<h5 class="ui header">
|
||||
${icon}
|
||||
<div class="content">
|
||||
${rule.Name}
|
||||
<div class="sub header">${rule.ID}</div>
|
||||
</div>
|
||||
</h5>
|
||||
<p>${rule.Desc}</p>
|
||||
${rule.BlacklistEnabled?`<small><i class="ui red filter icon"></i> Blacklist Enabled</small>`:""}
|
||||
${rule.WhitelistEnabled?`<small><i class="ui green filter icon"></i> Whitelist Enabled</small>`:""}
|
||||
<div class="selected"><i class="ui large green check icon"></i></div>
|
||||
</div>`);
|
||||
});
|
||||
accessRuleList = data;
|
||||
$(".dropdown").dropdown();
|
||||
if (callback != undefined){
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
initAccessRuleList(function(){
|
||||
$.ajax({
|
||||
url: "/api/proxy/detail",
|
||||
method: "POST",
|
||||
data: {"type":"host", "epname": editingEndpoint.ep },
|
||||
success: function(data){
|
||||
console.log(data);
|
||||
if (data.error != undefined){
|
||||
alert(data.error);
|
||||
}else{
|
||||
let currentAccessFilter = data.AccessFilterUUID;
|
||||
if (currentAccessFilter == ""){
|
||||
//Use default
|
||||
currentAccessFilter = "default";
|
||||
}
|
||||
|
||||
$(`.accessRule[ruleid=${currentAccessFilter}]`).addClass("active");
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
function selectThisRule(accessRuleObject){
|
||||
let accessRuleID = $(accessRuleObject).attr("ruleid");
|
||||
$(".accessRule").removeClass('active');
|
||||
$(accessRuleObject).addClass('active');
|
||||
}
|
||||
|
||||
function applyChangeAndClose(){
|
||||
let newAccessRuleID = $(".accessRule.active").attr("ruleid");
|
||||
let targetEndpoint = editingEndpoint.ep;
|
||||
$.ajax({
|
||||
url: "/api/access/attach",
|
||||
method: "POST",
|
||||
data: {
|
||||
id: newAccessRuleID,
|
||||
host: targetEndpoint
|
||||
},
|
||||
success: function(data){
|
||||
if (data.error != undefined){
|
||||
parent.msgbox(data.error, false);
|
||||
}else{
|
||||
parent.msgbox("Access Rule Updated");
|
||||
|
||||
//Modify the parent list if exists
|
||||
if (parent != undefined && parent.updateAccessRuleNameUnderHost){
|
||||
parent.updateAccessRuleNameUnderHost(targetEndpoint, newAccessRuleID);
|
||||
}
|
||||
parent.hideSideWrapper();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user