updated layout for webmail and added http dns letsencrypt
This commit is contained in:
@@ -7,15 +7,23 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"mailgoserver/internal/acmecert"
|
||||
)
|
||||
|
||||
// leAlwaysOverwriteFields are plain (non-secret) [LetsEncrypt] settings — always
|
||||
// persisted from the submitted form, same as any other settings.html field.
|
||||
// leAlwaysOverwriteFields are plain (non-secret) [LetsEncrypt] (DNS-01) settings —
|
||||
// always persisted from the submitted form, same as any other settings.html field.
|
||||
var leAlwaysOverwriteFields = []string{
|
||||
"enabled", "staging", "contact_email", "domains", "dns_provider",
|
||||
"route53_region", "route53_hosted_zone_id", "gcloud_project",
|
||||
}
|
||||
|
||||
// leHTTPFields are the [LetsEncryptHTTP] (HTTP-01) settings, all plain — no secrets to
|
||||
// redact, unlike the DNS-01 providers' API credentials.
|
||||
var leHTTPFields = []string{
|
||||
"enabled", "staging", "contact_email", "domains", "include_ip", "ip_override",
|
||||
}
|
||||
|
||||
// leSecretFields hold DNS provider credentials. They're never rendered back into the
|
||||
// form (always blank) and the save handler only overwrites the stored value when the
|
||||
// submitted field is non-empty — "leave blank to keep the current value", the same
|
||||
@@ -25,8 +33,9 @@ var leSecretFields = []string{
|
||||
"digitalocean_api_token", "gcloud_service_account_json_path",
|
||||
}
|
||||
|
||||
// letsEncryptPage shows the current Let's Encrypt status and configuration form.
|
||||
// Secret fields are always blank in the rendered form — see leSecretFields.
|
||||
// letsEncryptPage shows the current Let's Encrypt status and configuration forms for
|
||||
// both the DNS-01 and HTTP-01 managers. Secret fields are always blank in the rendered
|
||||
// form — see leSecretFields.
|
||||
func (a *App) letsEncryptPage(w http.ResponseWriter, r *http.Request) {
|
||||
sec := a.Cfg.Section("LetsEncrypt")
|
||||
kv := M{}
|
||||
@@ -36,7 +45,18 @@ func (a *App) letsEncryptPage(w http.ResponseWriter, r *http.Request) {
|
||||
for _, k := range leSecretFields {
|
||||
kv[k] = ""
|
||||
}
|
||||
a.render(w, r, "letsencrypt.html", M{"active": "letsencrypt", "le": kv, "status": a.ACME.Status()})
|
||||
|
||||
httpSec := a.Cfg.Section("LetsEncryptHTTP")
|
||||
httpKV := M{}
|
||||
for _, k := range leHTTPFields {
|
||||
httpKV[k] = httpSec.Key(k).String()
|
||||
}
|
||||
|
||||
a.render(w, r, "letsencrypt.html", M{
|
||||
"active": "letsencrypt",
|
||||
"le": kv, "status": a.ACME.Status(),
|
||||
"leHTTP": httpKV, "statusHTTP": a.ACMEHTTP.Status(),
|
||||
})
|
||||
}
|
||||
|
||||
// letsEncryptSave is a dedicated handler (not the generic settingsUpdate reflection)
|
||||
@@ -79,6 +99,39 @@ func (a *App) letsEncryptObtainNow(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, Prefix+"/letsencrypt", http.StatusFound)
|
||||
}
|
||||
|
||||
// letsEncryptHTTPSave mirrors letsEncryptSave for the [LetsEncryptHTTP] section — a
|
||||
// separate handler (not the generic settingsUpdate reflection) purely so it can
|
||||
// redirect back to /letsencrypt like its DNS-01 sibling; every field here is plain, so
|
||||
// unlike letsEncryptSave there's no secret-redaction concern.
|
||||
func (a *App) letsEncryptHTTPSave(w http.ResponseWriter, r *http.Request) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
setFlash(w, "error", "Invalid form data")
|
||||
http.Redirect(w, r, Prefix+"/letsencrypt", http.StatusFound)
|
||||
return
|
||||
}
|
||||
sec := a.Cfg.Section("LetsEncryptHTTP")
|
||||
for _, k := range leHTTPFields {
|
||||
sec.Key(k).SetValue(r.FormValue(k))
|
||||
}
|
||||
if err := a.Cfg.SaveTo(a.ConfigPath); err != nil {
|
||||
setFlash(w, "error", "Error saving settings: "+err.Error())
|
||||
http.Redirect(w, r, Prefix+"/letsencrypt", http.StatusFound)
|
||||
return
|
||||
}
|
||||
setFlash(w, "success", `Let's Encrypt HTTP-01 settings saved. If you just changed "Enable HTTP-01" or the port, restart the server before using "Obtain / Renew Now" — the challenge responder only starts at boot.`)
|
||||
http.Redirect(w, r, Prefix+"/letsencrypt", http.StatusFound)
|
||||
}
|
||||
|
||||
// letsEncryptHTTPObtainNow mirrors letsEncryptObtainNow for the HTTP-01 manager.
|
||||
func (a *App) letsEncryptHTTPObtainNow(w http.ResponseWriter, r *http.Request) {
|
||||
if err := a.ACMEHTTP.ObtainOrRenew(r.Context()); err != nil {
|
||||
setFlash(w, "error", "Could not obtain certificate: "+err.Error())
|
||||
} else {
|
||||
setFlash(w, "success", "Certificate obtained successfully")
|
||||
}
|
||||
http.Redirect(w, r, Prefix+"/letsencrypt", http.StatusFound)
|
||||
}
|
||||
|
||||
// uploadGCloudServiceAccount mirrors settings.go's uploadTLSFile two-step flow: upload
|
||||
// the file, return its saved path as JSON, and the browser fills a sibling text input
|
||||
// with that path — the path only actually persists once the surrounding form (Save)
|
||||
@@ -113,3 +166,14 @@ func (a *App) uploadGCloudServiceAccount(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
writeJSON(w, http.StatusOK, M{"status": "success", "filepath": filePath})
|
||||
}
|
||||
|
||||
// detectWANIP backs the "Detect" button next to the HTTP-01 manual IP override field —
|
||||
// a live lookup, not persisted anywhere until the surrounding form is saved.
|
||||
func (a *App) detectWANIP(w http.ResponseWriter, r *http.Request) {
|
||||
ip, err := acmecert.DetectWANIP(r.Context())
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadGateway, M{"status": "error", "message": err.Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, M{"status": "success", "ip": ip})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user