feat(ui): Quick-Toggles, Config-Preview, Dashboard-Alerts, Domain-Detail-Health

Quick-Toggle-Switches (kein Modal nötig) für: Backends, Backend-Server,
DNS-Zonen, DNS-Records, Domains (active), Firewall-Rules, NAT-Rules,
Forward-Proxy ACLs, Routing-Rules.

Dashboard: Alert-Banner für komplett ausgefallene Backends (HAProxy-Stats)
und Domains im Maintenance-Mode.

Domain-Detail: HAProxy-Live-Health-Badge (15s Polling), TLS-Cert
ausstellen/erneuern direkt aus dem Detail, Routing-Rules-Panel inline.

Config-Preview (Settings): alle 4 Generatoren (haproxy, nftables, squid,
unbound) rendern via RenderToString ohne Disk-Write — GET /system/config-preview.

ActionButtons: Viewer-Rolle blendet Delete aus (RBAC-Ergänzung).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-05-24 08:55:22 +02:00
parent 386972366b
commit 99982ef9d1
23 changed files with 755 additions and 63 deletions

View File

@@ -36,6 +36,10 @@ type SystemHandler struct {
Audit *audit.Repo
NodeID string
Pool *pgxpool.Pool
// ConfigPreviewers: generator name → render-to-string func. Wired
// in main.go after generators are constructed. Read-only access —
// no writes, no reloads.
ConfigPreviewers map[string]func(stdcontext.Context) (string, error)
}
func NewSystemHandler(version string) *SystemHandler {
@@ -64,6 +68,13 @@ func (h *SystemHandler) WithDB(pool *pgxpool.Pool) *SystemHandler {
return h
}
// WithConfigPreviewers injectet die RenderToString-Funktionen der
// Config-Generatoren für den /system/config-preview Endpoint.
func (h *SystemHandler) WithConfigPreviewers(previewers map[string]func(stdcontext.Context) (string, error)) *SystemHandler {
h.ConfigPreviewers = previewers
return h
}
func (h *SystemHandler) Register(rg *gin.RouterGroup) {
g := rg.Group("/system")
g.GET("/health", h.Health)
@@ -87,6 +98,7 @@ func (h *SystemHandler) Register(rg *gin.RouterGroup) {
g.GET("/upgrade-status", h.UpgradeStatus)
g.GET("/ipv6", h.IPv6)
g.POST("/ipv6", h.SetIPv6)
g.GET("/config-preview", h.ConfigPreview)
}
// RegisterAgent mountet die read-only System-Endpoints auf der mTLS-
@@ -680,6 +692,38 @@ func (h *SystemHandler) ToggleAutoUpdate(c *gin.Context) {
response.OK(c, gin.H{"enabled": req.Enabled})
}
// ConfigPreview rendert eine Service-Config aus dem aktuellen DB-State
// und gibt sie als Plain-Text zurück — ohne sie auf Disk zu schreiben
// oder den Dienst zu reloaden. Query-Parameter: ?generator=haproxy|nftables|squid|unbound
func (h *SystemHandler) ConfigPreview(c *gin.Context) {
gen := strings.TrimSpace(c.Query("generator"))
if gen == "" {
names := make([]string, 0, len(h.ConfigPreviewers))
for k := range h.ConfigPreviewers {
names = append(names, k)
}
response.OK(c, gin.H{"generators": names})
return
}
if h.ConfigPreviewers == nil {
response.Err(c, http.StatusServiceUnavailable, simpleErr("previewers not wired"))
return
}
fn, ok := h.ConfigPreviewers[gen]
if !ok {
response.Err(c, http.StatusBadRequest, simpleErr("unknown generator: "+gen))
return
}
ctx, cancel := stdcontext.WithTimeout(c.Request.Context(), 5*time.Second)
defer cancel()
content, err := fn(ctx)
if err != nil {
response.Internal(c, err)
return
}
response.OK(c, gin.H{"generator": gen, "content": content})
}
func (h *SystemHandler) Health(c *gin.Context) {
response.OK(c, gin.H{
"status": "ok",