zoraxy/src/web/snippet/acme.html
Toby Chui 5f64b622b5 Fixed #353 and #327
- Added user defined polling and propagation timeout option in ACME
- Updated lego and added a few new DNS challenge providers
- Updated code gen to support new parameters
2024-10-27 16:17:44 +08:00

965 lines
34 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<!-- Notes: This should be open in its original path-->
<meta charset="utf-8">
<meta name="zoraxy.csrf.Token" content="{{.csrfToken}}">
<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>
<script src="../script/utils.js"></script>
<style>
.disabled.table{
opacity: 0.5;
pointer-events: none;
}
.expiredDomain{
color: rgb(238, 31, 31);
}
.validDomain{
color: rgb(49, 192, 113);
}
</style>
</head>
<body>
<br>
<div class="ui container">
<div class="ui header">
<div class="content">
Certificates Auto Renew Settings
<div class="sub header">Fetch and renew your certificates with Automated Certificate Management Environment (ACME) protocol</div>
</div>
</div>
<div class="ui basic segment">
<p style="float: right; color: #21ba45; display:none;" id="enableToggleSucc"><i class="green checkmark icon"></i> Setting Updated</p>
<div class="ui toggle checkbox">
<input type="checkbox" id="enableCertAutoRenew">
<label>Enable Certificate Auto Renew</label>
</div>
<br>
<h3>ACME Email</h3>
<p>Email is required by many CAs for renewing via ACME protocol</p>
<div class="ui fluid action input">
<input id="caRegisterEmail" type="text" placeholder="webmaster@example.com">
<button class="ui icon basic button" onclick="saveEmailToConfig(this);">
<i class="blue save icon"></i>
</button>
</div>
<small>If you don't want to share your private email address, you can also fill in an email address that point to a mailbox not exists on your domain.</small>
</div>
<div class="ui basic segment" style="background-color: #f7f7f7; border-radius: 1em;">
<div class="ui accordion advanceSettings">
<div class="title">
<i class="dropdown icon"></i>
Advance Renew Policy
</div>
<div class="content">
<p>Renew all certificates with ACME supported CAs</p>
<div class="ui toggle checkbox">
<input type="checkbox" id="renewAllSupported" onchange="setAutoRenewIfCASupportMode(this.checked);">
<label>Renew All Certs</label>
</div><br>
<button id="renewNowBtn" onclick="renewNow();" class="ui basic right floated button" style="margin-top: -2em;"><i class="yellow refresh icon"></i> Renew Now</button>
<div class="ui horizontal divider"> OR </div>
<p>Select the certificates to automatic renew in the list below</p>
<table id="domainCertFileTable" class="ui very compact unstackable basic disabled table">
<thead>
<tr>
<th>Domain Name</th>
<th>Match Rule</th>
<th>Auto-Renew</th>
</tr>
</thead>
<tbody id="domainTableBody"></tbody>
</table>
<small><i class="ui red info circle icon"></i> Domain in red are expired</small><br>
<div class="ui yellow message">
Certificate Renew only works on the certification authority (CA) supported by Zoraxy. Check Zoraxy wiki for more information on supported list of CAs.
</div>
<button class="ui basic right floated button" onclick="saveAutoRenewPolicy();"><i class="blue save icon"></i> Save Changes</button>
<button id="renewSelectedButton" onclick="renewNow();" class="ui basic right floated disabled button"><i class="yellow refresh icon"></i> Renew Selected</button>
<br><br>
</div>
</div>
</div>
<div class="ui divider"></div>
<h3>Generate New Certificate</h3>
<p>Enter a new / existing domain(s) to request new certificate(s)</p>
<div class="ui form">
<div class="field">
<label>Domain(s)</label>
<input id="domainsInput" type="text" placeholder="example.com" onkeyup="handlePostInputAutomation();">
<small>If you have more than one domain in a single certificate, enter the domains separated by commas (e.g. s1.dev.example.com,s2.dev.example.com)
<span id="caNoDNSSupportWarning" style="color: #ffaf2e; display:none;"><br> <i class="exclamation triangle icon"></i> Current selected CA do not support DNS challenge</span>
</small>
</div>
<div class="field multiDomainOnly" style="display:none;">
<label>Matching Rule</label>
<input id="filenameInput" type="text" placeholder="Enter filename (no file extension)">
<small>Matching rule to let Zoraxy pick which certificate to use (Also be used as filename). Usually is the longest common suffix of the entered addresses. (e.g. dev.example.com)</small>
</div>
<div class="field multiDomainOnly" style="display:none;">
<button class="ui basic fluid button" onclick="autoDetectMatchingRules();">Auto Detect Matching Rule</button>
</div>
<div class="field">
<label>Certificate Authority (CA)</label>
<div class="ui selection dropdown" id="ca">
<input type="hidden" name="ca">
<i class="dropdown icon"></i>
<div class="default text">Let's Encrypt</div>
<div class="menu">
<div class="item" data-value="Let's Encrypt">Let's Encrypt</div>
<div class="item" data-value="Buypass">Buypass</div>
<div class="item" data-value="ZeroSSL">ZeroSSL</div>
<div class="item" data-value="Custom ACME Server">Custom ACME Server</div>
</div>
</div>
</div>
<div class="field" id="dnsChallenge">
<div class="ui checkbox">
<input type="checkbox" id="useDnsChallenge" onchange="toggleDnsChallenge()">
<label>Use a DNS Challenge<br>
</div>
</div>
<div class="field dnsChallengeOnly" style="display:none;">
<label>DNS Provider</label>
<div class="ui search selection dropdown" id="dnsProvider">
<input type="hidden" name="dnsProvider" value="">
<i class="dropdown icon"></i>
<div class="default text">Pick a DNS Provider</div>
<div class="menu" id="dnsProviderList">
<!-- Auto populate moved to acmedns module and initDNSProviderList() -->
</div>
</div>
</div>
<div class="field dnsChallengeOnly" style="display:none;">
<div class="ui divider"></div>
<p>DNS Credentials</p>
<div id="dnsProviderAPIFields">
<p><i class="ui loading circle notch icon"></i> Generating WebForm</p>
</div>
<h4><i class="yellow exclamation triangle icon"></i> Notes & FAQ</h4>
<div class="ui bulleted list">
<div class="item">Domain DNS credentials are stored separately. For each new subdomain, you will need to enter a new DNS credentials.</div>
<div class="item">For some DNS providers like CloudFlare, you do not need to fill in all fields.</div>
<div class="item">If you are not sure what to fill in, check out the documentation from <a href="https://go-acme.github.io/lego/dns/" target="_blank">lego (DNS challenge library)</a></div>
</div>
<!--
<label>Credentials File Content</label>
<textarea id="dnsCredentials" placeholder=""></textarea>
<small>For more information on the supported DNS Providers and their attirbutes look <a href="https://go-acme.github.io/lego/dns/" target="_blank">here</a>! </small>
<div class="ui negative message">
<i class="icon exclamation triangle"></i>
These credentials will be stored as plaintext in the database and in environment variables!
</div>
-->
</div>
<div class="field" id="caInput" style="display:none;">
<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">
<label>Ignore TLS/SSL Verification Error<br><small>E.g. self-signed, expired certificate (Not Recommended)</small></label>
</div>
</div>
<button id="obtainButton" class="ui basic button" type="submit"><i class="yellow refresh icon"></i> Get Certificate</button>
</div>
<div class="ui divider"></div>
<small>First time setting up HTTPS?<br>Try out our <a href="../tools/https.html" target="_blank">wizard</a></small>
<button class="ui basic button" style="float: right;" onclick="parent.hideSideWrapper();"><i class="remove icon"></i> Cancel</button>
<br><br><br><br>
</div>
<script>
let expiredDomains = [];
let enableTrigerOnChangeEvent = true;
$(".accordion").accordion();
$(".dropdown").dropdown();
$(".checkbox").checkbox();
function setAutoRenewIfCASupportMode(useAutoMode = true){
if (useAutoMode){
$("#domainCertFileTable").addClass("disabled");
$("#renewNowBtn").removeClass("disabled");
$("#renewSelectedButton").addClass("disabled");
}else{
$("#domainCertFileTable").removeClass("disabled");
$("#renewNowBtn").addClass("disabled");
$("#renewSelectedButton").removeClass("disabled");
}
}
function initRenewerConfigFromFile(){
//Set the renew switch state
$.get("/api/acme/autoRenew/enable", function(data){
if (data == true){
$("#enableCertAutoRenew").parent().checkbox("set checked");
}
$("#enableCertAutoRenew").on("change", function(){
if (!enableTrigerOnChangeEvent){
return;
}
toggleAutoRenew();
})
});
//Load the email from server side
$.get("/api/acme/autoRenew/email", function(data){
if (data != "" && data != undefined && data != null){
$("#caRegisterEmail").val(data);
}
});
//Load the domain selection options
$.get("/api/acme/autoRenew/renewPolicy", function(data){
if (data == true){
$("#renewAllSupported").parent().checkbox("set checked");
}else{
$("#renewAllSupported").parent().checkbox("set unchecked");
}
});
}
initRenewerConfigFromFile();
function saveEmailToConfig(btn){
$.cjax({
url: "/api/acme/autoRenew/email",
method: "POST",
data: {set: $("#caRegisterEmail").val()},
success: function(data){
if (data.error != undefined){
parent.msgbox(data.error, false, 5000);
}else{
parent.msgbox("Email updated");
$(btn).html(`<i class="green check icon"></i>`);
$(btn).addClass("disabled");
setTimeout(function(){
$(btn).html(`<i class="blue save icon"></i>`);
$(btn).removeClass("disabled");
}, 3000);
}
}
});
}
function toggleAutoRenew(){
var enabled = $("#enableCertAutoRenew").parent().checkbox("is checked");
$.cjax({
url: "/api/acme/autoRenew/enable",
method: "POST",
data: {"enable": enabled},
success: function(data){
if (data.error){
parent.msgbox(data.error, false, 5000);
if (enabled){
enableTrigerOnChangeEvent = false;
$("#enableCertAutoRenew").parent().checkbox("set unchecked");
enableTrigerOnChangeEvent = true;
}
if (parent && parent.setACMEEnableStates){
parent.setACMEEnableStates(!enabled);
}
}else{
$("#enableToggleSucc").stop().finish().fadeIn("fast").delay(3000).fadeOut("fast");
if (parent && parent.setACMEEnableStates){
parent.setACMEEnableStates(enabled);
}
}
}
});
}
//Render the domains table that exists in this zoraxy host
function renderDomainTable(domainFileList) {
// Get the table body element
var tableBody = $('#domainTableBody');
// Clear the table body
tableBody.empty();
// Iterate over the domain names
var counter = 0;
for (const [srcfile, domains] of Object.entries(domainFileList)) {
// Create a table row
var row = $('<tr>');
// Create the domain name cell
var domainClass = "validDomain";
for (var i = 0; i < domains.length; i++){
let thisDomain = domains[i];
if (expiredDomains.includes(thisDomain)){
domainClass = "expiredDomain";
}
}
var domainCell = $('<td class="' + domainClass +'">').html(domains.join("<br>"));
row.append(domainCell);
var srcFileCell = $('<td>').text(srcfile);
row.append(srcFileCell);
// Create the auto-renew checkbox cell
let domainsEncoded = encodeURIComponent(JSON.stringify(domains));
var checkboxCell = $(`<td domain="${domainsEncoded}" srcfile="${srcfile}">`);
var checkbox = $(`<input name="${srcfile}">`).attr('type', 'checkbox');
checkboxCell.append(checkbox);
row.append(checkboxCell);
// Add the row to the table body
tableBody.append(row);
counter++;
}
if (Object.keys(domainFileList).length == 0){
//No certificate in this system
tableBody.append(`<tr>
<td colspan="3"><i class="ui green circle check icon"></i> No certificate in use</td>
</tr>`);
}
}
//Initiate domain table. If you needs to update the expired domain as well
//call from initDomainFileList() instead
function initDomainTable(){
$.get("/api/cert/listdomains?compact=true", function(data){
if (data.error != undefined){
parent.msgbox(data.error, false);
}else{
renderDomainTable(data);
}
initAutoRenewPolicy();
})
}
function initDomainFileList() {
$.ajax({
url: "/api/acme/listExpiredDomains",
method: "GET",
success: function(response) {
// Render domain table
expiredDomains = response.domain;
initDomainTable();
//renderDomainTable(response.domain);
},
error: function(error) {
console.log("Failed to fetch expired domains:", error);
}
});
}
initDomainFileList();
// Button click event handler for obtaining certificate
$("#obtainButton").click(function() {
$("#obtainButton").addClass("loading").addClass("disabled");
updateCertificateEAB(function(succ){
if (succ){
//Continue to next step
updateCertificateDNS(function(succ){
if (succ){
obtainCertificate(function(succ){
$("#obtainButton").removeClass("loading").removeClass("disabled");
});
}else{
$("#obtainButton").removeClass("loading").removeClass("disabled");
console.log("update Certificate DNS process halted");
}
});
}else{
console.log("Update Certificate EAB process halted");
$("#obtainButton").removeClass("loading").removeClass("disabled");
}
});
});
//On CA change in dropdown
$("input[name=ca]").on('change', function() {
if(this.value == "Custom ACME Server") {
$("#caInput").show();
$("#kidInput").show();
$("#hmacInput").show();
$("#skipTLS").show();
$("#dnsChallenge").hide();
$(".dnsChallengeOnly").hide();
} else if (this.value == "ZeroSSL") {
$("#kidInput").show();
$("#hmacInput").show();
$("#dnsChallenge").hide();
$(".dnsChallengeOnly").hide();
$("#skipTLS").hide();
} else if (this.value == "Buypass") {
$("#kidInput").show();
$("#hmacInput").show();
$("#dnsChallenge").hide();
$(".dnsChallengeOnly").hide();
$("#skipTLS").hide();
}else {
$("#caInput").hide();
$("#skipTLS").hide();
$("#kidInput").hide();
$("#hmacInput").hide();
$("#dnsChallenge").show();
if ($("#useDnsChallenge")[0].checked){
$(".dnsChallengeOnly").show();
}
}
})
//On DNS provider dropdown change
$("input[name=dnsProvider]").on('change', function() {
let newProviderName = $("#dnsProvider").find("input").val();
$.get("/api/acme/dns/providers?name=" + newProviderName, function(data){
console.log("Loaded required config", data);
$("#dnsProviderAPIFields").html("");
//Generate a form for this config
let booleanFieldsHTML = "";
let optionalFieldsHTML = "";
for (const [key, datatype] of Object.entries(data)) {
if (datatype == "int"){
$("#dnsProviderAPIFields").append(`<div class="ui fluid labeled dnsConfigField input" key="${key}" style="margin-top: 0.2em;">
<div class="ui basic blue label" style="font-weight: 300;">
${key}
</div>
<input type="number" value="300">
</div>`);
}else if (datatype == "bool"){
booleanFieldsHTML += (`<div class="ui checkbox dnsConfigField" key="${key}" style="margin-top: 1em !important; padding-left: 0.4em;">
<input type="checkbox">
<label>${key}</label>
</div>`);
}else if (datatype == "time.Duration"){
let defaultIntValue = 300;
let defaultMinValue = 60;
if (key == "PollingInterval"){
defaultIntValue = 30;
defaultMinValue = 10;
}else if (key == "PropagationTimeout"){
defaultIntValue = 300;
defaultMinValue = 60;
}
optionalFieldsHTML += (`<div class="ui fluid labeled dnsConfigField small input" key="${key}" style="margin-top: 0.2em;">
<div class="ui basic blue label" style="font-weight: 300;">
${key}
</div>
<input type="number" min="${defaultMinValue}" value="${defaultIntValue}">
<div class="ui basic label" style="font-weight: 300;">
secs
</div>
</div>`);
}else{
//Default to string
$("#dnsProviderAPIFields").append(`<div class="ui fluid labeled input dnsConfigField" key="${key}" style="margin-top: 0.2em;">
<div class="ui basic label" style="font-weight: 300;">
${key}
</div>
<input type="text">
</div>`);
}
}
//Append the boolean fields at the bottom, if exists
$("#dnsProviderAPIFields").append(booleanFieldsHTML);
if (booleanFieldsHTML != ""){
$(".dnsConfigField.checkbox").checkbox();
}
//Append the optional fields at the bottom, if exists
$("#dnsProviderAPIFields").append(optionalFieldsHTML);
});
});
// Get filename form domains and input
function getFilename() {
var domains = $("#domainsInput").val();
var filename = $("#filenameInput").val();
if (filename.trim() == "" && !domains.includes(",")){
//Zoraxy filename are the matching name for domains.
//Use the same as domains
filename = domains;
}else if (filename != "" && !domains.includes(",")){
//Invalid settings. Force the filename to be same as domain
//if there are only 1 domain
filename = domains;
}else if (filename == "" && domains.includes(",")){
parent.msgbox("Filename cannot be empty for certs containing multiple domains.", false, 5000);
$("#obtainButton").removeClass("loading").removeClass("disabled");
return;
}
//Filename cannot contain wildcards, and wildcards are possible with DNS challenges
filename = filename.replace("*", "_");
return filename;
}
// Update EAB values for autorenewal
function updateCertificateEAB(callback=undefined) {
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 == "") {
//Skip update
if (callback != undefined){
callback(true);
}
return;
}
var kid = $("#eab_kid").val();
var hmac = $("#eab_hmac").val();
if(kid == "" || hmac == "") {
//Skip update
if (callback != undefined){
callback(true);
}
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);
if (callback != undefined){
callback(false);
}
} else {
console.log("Certificate EAB updated successfully");
// Show success message
parent.msgbox("Certificate EAB updated successfully");
// Renew the parent certificate list
parent.initManagedDomainCertificateList();
if (callback != undefined){
callback(true);
}
}
},
error: function(error) {
//$("#obtainButton").removeClass("loading").removeClass("disabled");
console.log("Failed to update EAB configuration:", error);
parent.msgbox("Failed to update EAB configuration");
if (callback != undefined){
callback(false);
}
}
});
}
//Read DNS credential from form and generate a key value structure that looks like
// the old DNSCredential TextArea input
function readDnsCredentials(){
let dnsCredentials = {};
$(".dnsConfigField").each(function(){
let thisKey = $(this).attr("key");
let value = "";
if ($(this).hasClass("checkbox")){
//Boolean option
let checked = $(this).find("input")[0].checked;
dnsCredentials[thisKey] = checked;
}else{
//String or int options
let value = $(this).find("input").val().trim();
dnsCredentials[thisKey] = value;
}
});
return dnsCredentials;
}
// Update DNS values for autorenewal
function updateCertificateDNS(callback=undefined) {
var dns = $("#useDnsChallenge")[0].checked;
var dnsProvider = "";
var dnsCredentials = "";
if (!dns) {
if (callback != undefined){
callback(true);
}
return;
}
//Check if all fields is empty. If yes, do not update the config
let allFieldsEmpty = true;
$(".dnsConfigField").each(function(){
if ($(this).find("input").val().trim() != ""){
allFieldsEmpty = false;
}
});
if (allFieldsEmpty){
//Do not update config on server side
if (callback != undefined){
callback(true);
}
return;
}
dnsProvider = $("#dnsProvider").dropdown("get value");
//dnsCredentials = $("#dnsCredentials").val();
dnsCredentials = readDnsCredentials();
if(dnsProvider == "") {
parent.msgbox("DNS Provider cannot be empty", false, 5000);
$("#obtainButton").removeClass("loading").removeClass("disabled");
if (callback != undefined){
callback(false);
}
return;
}
var filename = getFilename();
if (filename == '') {
parent.msgbox("Domain to renew cannot be empty", false, 5000);
if (callback != undefined){
callback(false);
}
return;
}
$.cjax({
url: "/api/acme/autoRenew/setDNS",
method: "POST",
data: {
filename: filename,
dnsProvider: dnsProvider,
dnsCredentials: JSON.stringify(dnsCredentials),
},
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);
if (callback != undefined){
callback(false);
}
} else {
console.log("Certificate DNS Credentials updated successfully");
// Show success message
parent.msgbox("Certificate DNS Credentials updated successfully");
if (callback != undefined){
callback(true);
}
}
},
error: function(error) {
//$("#obtainButton").removeClass("loading").removeClass("disabled");
console.log("Failed to update DNS configuration:", error);
parent.msgbox("Failed to update DNS configuration");
if (callback != undefined){
callback(false);
}
}
});
}
// Obtain certificate from API
function obtainCertificate(callback=undefined) {
var domains = $("#domainsInput").val();
var filename = getFilename();
if (filename == '') {
if (callback != undefined){
parent.msgbox("Domain to obtain certificate cannot be empty", false)
callback(false);
}
return;
}
var email = $("#caRegisterEmail").val();
if (email == ""){
parent.msgbox("ACME renew email is not set", false)
if (callback != undefined){callback(false);}
return;
}
var ca = $("#ca").dropdown("get value");
var caURL = "";
if (ca == "Custom ACME Server") {
ca = "custom";
caURL = $("#caURL").val();
}
var dns = $("#useDnsChallenge")[0].checked;
var skipTLSValue = $("#skipTLSCheckbox")[0].checked;
$.ajax({
url: "/api/acme/obtainCert",
method: "GET",
data: {
domains: domains,
filename: filename,
email: email,
ca: ca,
caURL: caURL,
skipTLS: skipTLSValue,
dns: dns,
},
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);
if (callback != undefined){callback(false);}
} else {
console.log("Certificate renewed successfully");
// Show success message
parent.msgbox("Certificate renewed successfully");
// Renew the parent certificate list
parent.initManagedDomainCertificateList();
if (callback != undefined){callback(true);}
}
},
error: function(error) {
$("#obtainButton").removeClass("loading").removeClass("disabled");
console.log("Failed to renewed certificate:", error);
if (callback != undefined){callback(false);}
}
});
}
//Check if the entered domain contains multiple domains
function checkIfInputDomainIsMultiple(){
var inputDomains = $("#domainsInput").val();
if (inputDomains.includes(",")){
$(".multiDomainOnly").show();
}else{
$(".multiDomainOnly").hide();
}
}
//Validate if the current combinations of domain and CA supports DNS challenge
function validateDNSChallengeSupport(){
if ($("#domainsInput").val().includes("*")){
var ca = $("#ca").dropdown("get value");
if (ca == "Let's Encrypt" || ca == ""){
$("#caNoDNSSupportWarning").hide();
}else{
$("#caNoDNSSupportWarning").show();
}
}else{
$("#caNoDNSSupportWarning").hide();
}
}
//call to validateDNSChallengeSupport() on #ca value change
$("#ca").dropdown({
onChange: function(value, text, $selectedItem) {
validateDNSChallengeSupport();
}
});
//Handle the input change event on domain input
function handlePostInputAutomation(){
checkIfInputDomainIsMultiple();
validateDNSChallengeSupport();
}
function toggleDnsChallenge(){
if ( $("#useDnsChallenge")[0].checked){
$(".dnsChallengeOnly").show();
setTimeout(function(){
$("#dnsProvider").dropdown("set text", "Cloudflare");
}, 500);
}else{
$(".dnsChallengeOnly").hide();
}
}
//Grab the longest common suffix of all domains
//not that smart technically
function autoDetectMatchingRules(){
var domainsString = $("#domainsInput").val();
if (!domainsString.includes(",")){
return domainsString;
}
let domains = domainsString.split(",");
//Clean out any spacing between commas
for (var i = 0; i < domains.length; i++){
domains[i] = domains[i].trim();
}
function getLongestCommonSuffix(strings) {
if (strings.length === 0) {
return ''; // Return an empty string if the array is empty
}
var sortedStrings = strings.slice().sort(); // Create a sorted copy of the array
var firstString = sortedStrings[0];
var lastString = sortedStrings[sortedStrings.length - 1];
var suffix = '';
var minLength = Math.min(firstString.length, lastString.length);
for (var i = 0; i < minLength; i++) {
if (firstString[firstString.length - 1 - i] !== lastString[lastString.length - 1 - i]) {
break; // Stop iterating if characters don't match
}
suffix = firstString[firstString.length - 1 - i] + suffix;
}
return suffix;
}
let longestSuffix = getLongestCommonSuffix(domains);
//Check if the suffix is a valid domain
if (longestSuffix.substr(0,1) == "."){
//Trim off the first dot
longestSuffix = longestSuffix.substr(1);
}
if (!longestSuffix.includes(".")){
parent.msgbox("Auto Detect failed: Multiple Domains", false, 5000);
return;
}
$("#filenameInput").val(longestSuffix);
}
//Handle the renew now btn click
function renewNow(){
$.get("/api/acme/autoRenew/renewNow", function(data){
if (data.error != undefined){
parent.msgbox(data.error, false, 6000);
}else{
parent.msgbox(data)
}
})
}
function initAutoRenewPolicy(){
$.get("/api/acme/autoRenew/listDomains", function(data){
if (data.error != undefined){
parent.msgbox(data.error, false)
}else{
if (data[0] == "*"){
//Auto select and renew is enabled
$("#renewAllSupported").parent().checkbox("set checked");
}else{
//This is a list of domain files
data.forEach(function(name) {
$('#domainTableBody input[type="checkbox"][name="' + name + '"]').prop('checked', true);
});
$("#domainCertFileTable").removeClass("disabled");
$("#renewNowBtn").addClass("disabled");
$("#renewSelectedButton").removeClass("disabled");
}
}
})
}
function saveAutoRenewPolicy(){
let autoRenewAll = $("#renewAllSupported").parent().checkbox("is checked");
if (autoRenewAll == true){
$.cjax({
url: "/api/acme/autoRenew/setDomains",
method: "POST",
data: {opr: "setAuto"},
success: function(data){
parent.msgbox("Renew policy rule updated")
}
});
}else{
let checkedNames = [];
$('#domainTableBody input[type="checkbox"]:checked').each(function() {
checkedNames.push($(this).attr('name'));
});
$.cjax({
url: "/api/acme/autoRenew/setDomains",
method: "POST",
data: {opr: "setSelected", domains: JSON.stringify(checkedNames)},
success: function(data){
parent.msgbox("Renew policy rule updated")
}
});
}
}
//Load the json map and create the dropdown for DNS provider names
let dnsProviderNameMap = {};
function initDNSProviderList(){
$.get("dnsnames.json", function(namemap){
dnsProviderNameMap = namemap;
//Load a list of supported DNS provider from backend
$("#dnsProviderList").html("");
$.get("/api/acme/dns/providers", function(providerList){
providerList.sort();
providerList.forEach(providerid => {
let providerName = providerid;
if (dnsProviderNameMap[providerid] != undefined){
providerName = dnsProviderNameMap[providerid];
}
$("#dnsProviderList").append(`<div class="item" data-value="${providerid}">${providerName}</div>`);
});
$("#dnsProvider").dropdown();
setTimeout(function(){
//The dropdown is large, it takes some time to load
$("#dnsProvider").dropdown("set selected", "cloudflare");
}, 300)
});
});
}
initDNSProviderList();
//Clear up the input field when page load
$("#filenameInput").val("");
</script>
</body>
</html>