Procházet zdrojové kódy

auto update script executed

Toby Chui před 1 rokem
rodič
revize
b9c1cec216
6 změnil soubory, kde provedl 229 přidání a 30 odebrání
  1. 1 0
      api.go
  2. 17 0
      cert.go
  3. 100 12
      mod/acme/autorenew.go
  4. 2 1
      mod/pathrule/pathrule.go
  5. 5 4
      start.go
  6. 104 13
      web/snippet/acme.html

+ 1 - 0
api.go

@@ -155,6 +155,7 @@ func initAPIs() {
 	//ACME & Auto Renewer
 	authRouter.HandleFunc("/api/acme/listExpiredDomains", acmeHandler.HandleGetExpiredDomains)
 	authRouter.HandleFunc("/api/acme/obtainCert", AcmeCheckAndHandleRenewCertificate)
+	authRouter.HandleFunc("/api/acme/autoRenew/enable", acmeAutoRenewer.HandleAutoRenewEnable)
 	authRouter.HandleFunc("/api/acme/autoRenew/email", acmeAutoRenewer.HandleACMEEmail)
 	authRouter.HandleFunc("/api/acme/autoRenew/setDomains", acmeAutoRenewer.HandleSetAutoRenewDomains)
 	authRouter.HandleFunc("/api/acme/autoRenew/listDomains", acmeAutoRenewer.HandleLoadAutoRenewDomains)

+ 17 - 0
cert.go

@@ -145,6 +145,23 @@ func handleListDomains(w http.ResponseWriter, r *http.Request) {
 		}
 	}
 
+	requireCompact, _ := utils.GetPara(r, "compact")
+	if requireCompact == "true" {
+		result := make(map[string][]string)
+
+		for key, value := range certnameToDomainMap {
+			if _, ok := result[value]; !ok {
+				result[value] = make([]string, 0)
+			}
+
+			result[value] = append(result[value], key)
+		}
+
+		js, _ := json.Marshal(result)
+		utils.SendJSONResponse(w, string(js))
+		return
+	}
+
 	js, _ := json.Marshal(certnameToDomainMap)
 	utils.SendJSONResponse(w, string(js))
 }

+ 100 - 12
mod/acme/autorenew.go

@@ -6,6 +6,7 @@ import (
 	"fmt"
 	"log"
 	"net/http"
+	"net/mail"
 	"os"
 	"path/filepath"
 	"time"
@@ -20,10 +21,10 @@ import (
 */
 
 type AutoRenewConfig struct {
-	Email          string //Email for acme
-	AutomaticRenew bool   //Automatic renew is enabled
-	RenewAll       bool   //Renew all or selective renew with the slice below
-	DomainsToRenew []string
+	Enabled      bool     //Automatic renew is enabled
+	Email        string   //Email for acme
+	RenewAll     bool     //Renew all or selective renew with the slice below
+	FilesToRenew []string //If RenewAll is false, renew these certificate files
 }
 
 type AutoRenewer struct {
@@ -48,8 +49,8 @@ func NewAutoRenewer(config string, certFolder string, renewCheckInterval int64)
 		//Create one
 		os.MkdirAll(filepath.Dir(config), 0775)
 		newConfig := AutoRenewConfig{
-			RenewAll:       true,
-			DomainsToRenew: []string{},
+			RenewAll:     true,
+			FilesToRenew: []string{},
 		}
 		js, _ := json.MarshalIndent(newConfig, "", " ")
 		err := os.WriteFile(config, js, 0775)
@@ -96,27 +97,108 @@ func NewAutoRenewer(config string, certFolder string, renewCheckInterval int64)
 	return &thisRenewer, nil
 }
 
+// Handle update auto renew domains
+// Set opr for different mode of operations
+// opr = setSelected -> Enter a list of file names (or matching rules) for auto renew
+// opr = setAuto -> Set to use auto detect certificates and renew
 func (a *AutoRenewer) HandleSetAutoRenewDomains(w http.ResponseWriter, r *http.Request) {
+	opr, err := utils.GetPara(r, "opr")
+	if err != nil {
+		utils.SendErrorResponse(w, "Operation not set")
+		return
+	}
+
+	if opr == "setSelected" {
+		files, err := utils.PostPara(r, "domains")
+		if err != nil {
+			utils.SendErrorResponse(w, "Domains is not defined")
+			return
+		}
+
+		//Parse it int array of string
+		matchingRuleFiles := []string{}
+		err = json.Unmarshal([]byte(files), &matchingRuleFiles)
+		if err != nil {
+			utils.SendErrorResponse(w, err.Error())
+			return
+		}
+
+		//Update the configs
+		a.RenewerConfig.RenewAll = false
+		a.RenewerConfig.FilesToRenew = matchingRuleFiles
+		a.saveRenewConfigToFile()
+		utils.SendOK(w)
+	} else if opr == "setAuto" {
+		a.RenewerConfig.RenewAll = true
+		a.saveRenewConfigToFile()
+		utils.SendOK(w)
+	}
 
 }
 
+// if auto renew all is true (aka auto scan), it will return []string{"*"}
 func (a *AutoRenewer) HandleLoadAutoRenewDomains(w http.ResponseWriter, r *http.Request) {
+	results := []string{}
+	if a.RenewerConfig.RenewAll {
+		//Auto pick which cert to renew.
+		results = append(results, "*")
+	} else {
+		//Manually set the files to renew
+		results = a.RenewerConfig.FilesToRenew
+	}
 
+	js, _ := json.Marshal(results)
+	utils.SendJSONResponse(w, string(js))
 }
 
 func (a *AutoRenewer) HandleRenewNow(w http.ResponseWriter, r *http.Request) {
 
 }
 
-func (a *AutoRenewer) HandleACMEEmail(w http.ResponseWriter, r *http.Request) {
-	/*
-		email, err := utils.PostPara(r, "set")
-		if err != nil {
-			currentEmail := ""
+func (a *AutoRenewer) HandleAutoRenewEnable(w http.ResponseWriter, r *http.Request) {
+	val, err := utils.PostPara(r, "enable")
+	if err != nil {
+		js, _ := json.Marshal(a.RenewerConfig.Enabled)
+		utils.SendJSONResponse(w, string(js))
+	} else {
+		if val == "true" {
+			//Check if the email is not empty
+			if a.RenewerConfig.Email == "" {
+				utils.SendErrorResponse(w, "Email is not set")
+				return
+			}
+
+			a.RenewerConfig.Enabled = true
+			a.saveRenewConfigToFile()
+			log.Println("[AutoRenew] ACME auto renew enabled")
 		} else {
+			a.RenewerConfig.Enabled = false
+			a.saveRenewConfigToFile()
+			log.Println("[AutoRenew] ACME auto renew disabled")
+		}
+	}
+}
+
+func (a *AutoRenewer) HandleACMEEmail(w http.ResponseWriter, r *http.Request) {
 
+	email, err := utils.PostPara(r, "set")
+	if err != nil {
+		//Return the current email to user
+		js, _ := json.Marshal(a.RenewerConfig.Email)
+		utils.SendJSONResponse(w, string(js))
+	} else {
+		//Check if the email is valid
+		_, err := mail.ParseAddress(email)
+		if err != nil {
+			utils.SendErrorResponse(w, err.Error())
+			return
 		}
-	*/
+
+		//Set the new config
+		a.RenewerConfig.Email = email
+		a.saveRenewConfigToFile()
+	}
+
 }
 
 // Check and renew certificates. This check all the certificates in the
@@ -133,3 +215,9 @@ func (a *AutoRenewer) CheckAndRenewCertificates() ([]string, error) {
 	fmt.Println("[ACME DEBUG] Cert found: ", files)
 	return []string{}, nil
 }
+
+// Write the current renewer config to file
+func (a *AutoRenewer) saveRenewConfigToFile() error {
+	js, _ := json.MarshalIndent(a.RenewerConfig, "", " ")
+	return os.WriteFile(a.ConfigFilePath, js, 0775)
+}

+ 2 - 1
mod/pathrule/pathrule.go

@@ -19,6 +19,7 @@ import (
 */
 
 type Options struct {
+	Enabled      bool   //If the pathrule is enabled.
 	ConfigFolder string //The folder to store the path blocking config files
 }
 
@@ -39,7 +40,7 @@ type Handler struct {
 }
 
 // Create a new path blocker handler
-func NewPathBlocker(options *Options) *Handler {
+func NewPathRuleHandler(options *Options) *Handler {
 	//Create folder if not exists
 	if !utils.FileExists(options.ConfigFolder) {
 		os.Mkdir(options.ConfigFolder, 0775)

+ 5 - 4
start.go

@@ -96,13 +96,14 @@ func startupSequence() {
 	}
 
 	/*
-		Path Blocker
+		Path Rules
 
-		This section of starutp script start the pathblocker
-		from file.
+		This section of starutp script start the path rules where
+		user can define their own routing logics
 	*/
 
-	pathRuleHandler = pathrule.NewPathBlocker(&pathrule.Options{
+	pathRuleHandler = pathrule.NewPathRuleHandler(&pathrule.Options{
+		Enabled:      false,
 		ConfigFolder: "./rules/pathrules",
 	})
 

+ 104 - 13
web/snippet/acme.html

@@ -31,19 +31,21 @@
         </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 generally required for renewing via ACME. Zoraxy do not support no-email renew due to security reasons.</p>
+      <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="[email protected]">
-        <button class="ui icon basic button">
+        <button class="ui icon basic button" onclick="saveEmailToConfig();">
             <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">
@@ -57,14 +59,14 @@
                 <input type="checkbox" id="renewAllSupported" onchange="setAutoRenewIfCASupportMode(this.checked);" checked>
                 <label>Auto renew if CA is supported</label>
               </div><br>
-              <button class="ui basic right floated button" style="margin-top: -2em;"><i class="yellow refresh icon"></i> Renew Now</button>
+              <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>Filename</th>
+                    <th>Match Rule</th>
                     <th>Auto-Renew</th>
                   </tr>
                 </thead>
@@ -75,7 +77,7 @@
                 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"><i class="blue save icon"></i> Save Changes</button>
-              <button class="ui basic right floated button"><i class="yellow refresh icon"></i> Renew Selected</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>
@@ -126,13 +128,61 @@
     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(){
+      $.get("/api/acme/autoRenew/enable", function(data){
+        if (data == true){
+          $("#enableCertAutoRenew").parent().checkbox("set checked");
+        }
+
+        $("#enableCertAutoRenew").on("change", function(){
+          toggleAutoRenew();
+        })
+      });
+
+      $.get("/api/acme/autoRenew/email", function(data){
+        if (data != "" && data != undefined && data != null){
+          $("#caRegisterEmail").val(data);
+        }
+      });
+    }
+    initRenewerConfigFromFile();
+
+    function saveEmailToConfig(){
+      $.ajax({
+        url: "/api/acme/autoRenew/email",
+        data: {set: $("#caRegisterEmail").val()},
+        success: function(data){
+          if (data.error != undefined){
+            parent.msgbox(data.error, false, 5000);
+          }else{
+            parent.msgbox("Email updated");
+          }
+        }
+      });
+    }
+
+    function toggleAutoRenew(){
+      var enabled = $("#enableCertAutoRenew").parent().checkbox("is checked");
+      $.post("/api/acme/autoRenew/enable?enable=" + enabled, function(data){
+        if (data.error){
+          parent.msgbox(data.error, false, 5000);
+        }else{
+          $("#enableToggleSucc").stop().finish().fadeIn("fast").delay(3000).fadeOut("fast");
+        }
+      });
+    }
+
     //Render the domains table that exists in this zoraxy host
-    function renderDomainTable(domains) {
+    function renderDomainTable(domainFileList) {
       // Get the table body element
       var tableBody = $('#domainTableBody');
       
@@ -141,25 +191,30 @@
       
       // Iterate over the domain names
       var counter = 0;
-      for (const [domain, srcfile] of Object.entries(domains)) {
+      for (const [srcfile, domains] of Object.entries(domainFileList)) {
 
         // Create a table row
         var row = $('<tr>');
         
         // Create the domain name cell
         var domainClass = "validDomain";
-        if (expiredDomains.includes(domain)){
-          domainClass = "expiredDomain";
+        for (var i = 0; i < domains.length; i++){
+          let thisDomain = domains[i];
+          if (expiredDomains.includes(thisDomain)){
+            domainClass = "expiredDomain";
+          }
         }
-        var domainCell = $('<td class="' + domainClass  +'">').text(domain);
+       
+        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
-        var checkboxCell = $(`<td domain="${domain}" srcfile="${srcfile}">`);
-        var checkbox = $('<input>').attr('type', 'checkbox');
+        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);
         
@@ -173,12 +228,13 @@
     //Initiate domain table. If you needs to update the expired domain as well
     //call from initDomainFileList() instead
     function initDomainTable(){
-      $.get("/api/cert/listdomains", function(data){
+      $.get("/api/cert/listdomains?compact=true", function(data){
         if (data.error != undefined){
           parent.msgbox(data.error, false);
         }else{
           renderDomainTable(data);
         }
+        initAutoRenewPolicy();
       })
     }
 
@@ -261,6 +317,8 @@
       }
     }
 
+    //Grab the longest common suffix of all domains
+    //not that smart technically
     function autoDetectMatchingRules(){
       var domainsString = $("#domainsInput").val();
       if (!domainsString.includes(",")){
@@ -312,6 +370,39 @@
       $("#filenameInput").val(longestSuffix);
     }
 
+    //Handle the renew now btn click
+    function renewNow(){
+      alert("wip");
+      return
+      $.get("/api/acme/autoRenew/renewNow", function(data){
+        alert(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
+
+          }
+        }
+      })
+    }
+
+    function saveAutoRenewPolicy(){
+      let autoRenewAll = $("#renewAllSupported").parent().checkbox("is checked");
+      if (autoRenewAll == true){
+
+      }else{
+
+      }
+    }
 
     //Clear  up the input field when page load
     $("#filenameInput").val("");