144 lines
3.1 KiB
Go
Raw Permalink Normal View History

2026-03-12 20:51:38 -07:00
package handlers
import (
"fmt"
"net/http"
"time"
"setec-manager/internal/acme"
)
type certInfo struct {
Domain string `json:"domain"`
Issuer string `json:"issuer"`
NotBefore time.Time `json:"not_before"`
NotAfter time.Time `json:"not_after"`
DaysLeft int `json:"days_left"`
AutoRenew bool `json:"auto_renew"`
}
func (h *Handler) SSLOverview(w http.ResponseWriter, r *http.Request) {
certs := h.listCerts()
h.render(w, "ssl.html", certs)
}
func (h *Handler) SSLStatus(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, h.listCerts())
}
func (h *Handler) SSLIssue(w http.ResponseWriter, r *http.Request) {
domain := paramStr(r, "domain")
if domain == "" {
writeError(w, http.StatusBadRequest, "domain required")
return
}
client := acme.NewClient(
h.Config.ACME.Email,
h.Config.ACME.Staging,
h.Config.Nginx.CertbotWebroot,
h.Config.ACME.AccountDir,
)
info, err := client.Issue(domain)
if err != nil {
writeError(w, http.StatusInternalServerError, fmt.Sprintf("certbot failed: %s", err))
return
}
// Update site SSL paths
site, _ := h.DB.GetSiteByDomain(domain)
if site != nil {
site.SSLEnabled = true
site.SSLCertPath = info.CertPath
site.SSLKeyPath = info.KeyPath
h.DB.UpdateSite(site)
}
writeJSON(w, http.StatusOK, map[string]string{"status": "issued", "cert": info.CertPath})
}
func (h *Handler) SSLRenew(w http.ResponseWriter, r *http.Request) {
domain := paramStr(r, "domain")
client := acme.NewClient(
h.Config.ACME.Email,
h.Config.ACME.Staging,
h.Config.Nginx.CertbotWebroot,
h.Config.ACME.AccountDir,
)
if err := client.Renew(domain); err != nil {
writeError(w, http.StatusInternalServerError, fmt.Sprintf("renewal failed: %s", err))
return
}
writeJSON(w, http.StatusOK, map[string]string{"status": "renewed"})
}
func (h *Handler) listCerts() []certInfo {
var certs []certInfo
// First, gather certs from DB-tracked sites
sites, _ := h.DB.ListSites()
for _, site := range sites {
if !site.SSLEnabled || site.SSLCertPath == "" {
continue
}
ci := certInfo{
Domain: site.Domain,
AutoRenew: site.SSLAuto,
}
client := acme.NewClient(
h.Config.ACME.Email,
h.Config.ACME.Staging,
h.Config.Nginx.CertbotWebroot,
h.Config.ACME.AccountDir,
)
info, err := client.GetCertInfo(site.Domain)
if err == nil {
ci.Issuer = info.Issuer
ci.NotBefore = info.ExpiresAt.Add(-90 * 24 * time.Hour) // approximate
ci.NotAfter = info.ExpiresAt
ci.DaysLeft = info.DaysLeft
}
certs = append(certs, ci)
}
// Also check Let's Encrypt certs directory via ACME client
client := acme.NewClient(
h.Config.ACME.Email,
h.Config.ACME.Staging,
h.Config.Nginx.CertbotWebroot,
h.Config.ACME.AccountDir,
)
leCerts, _ := client.ListCerts()
for _, le := range leCerts {
// Skip if already found via site
found := false
for _, c := range certs {
if c.Domain == le.Domain {
found = true
break
}
}
if found {
continue
}
certs = append(certs, certInfo{
Domain: le.Domain,
Issuer: le.Issuer,
NotAfter: le.ExpiresAt,
DaysLeft: le.DaysLeft,
})
}
return certs
}