package handlers import ( "github.com/gin-gonic/gin" "git.netcell-it.de/projekte/edgeguard-native/internal/handlers/response" "git.netcell-it.de/projekte/edgeguard-native/internal/services/audit" "git.netcell-it.de/projekte/edgeguard-native/internal/services/setup" ) // SetupHandler exposes the first-run wizard endpoints. Both endpoints // are mounted before SetupGate so they remain reachable while the API // is in setup mode. type SetupHandler struct { Store *setup.Store Audit *audit.Repo NodeID string } func NewSetupHandler(store *setup.Store) *SetupHandler { return &SetupHandler{Store: store} } // WithAudit injiziert Audit-Repo + Node-ID damit Mutationen (contact-emails) // in audit_log landen. Optional — wenn Audit nil bleibt, läuft die // Mutation, aber ohne Log-Eintrag. func (h *SetupHandler) WithAudit(a *audit.Repo, nodeID string) *SetupHandler { h.Audit = a h.NodeID = nodeID return h } func (h *SetupHandler) Register(rg *gin.RouterGroup) { g := rg.Group("/setup") g.GET("/status", h.Status) g.POST("/complete", h.Complete) } // RegisterAuthed mountet die Endpoints die nach abgeschlossenem Setup // den Admin-Modus brauchen — Aufrufer hat requireAuth schon dran. func (h *SetupHandler) RegisterAuthed(rg *gin.RouterGroup) { g := rg.Group("/setup") g.POST("/contact-emails", h.SetContactEmails) } // Status returns just the public bits of the setup state: whether // it's done and (if so) the configured admin_email + acme_email + // fqdn. Never exposes the password hash. func (h *SetupHandler) Status(c *gin.Context) { st, err := h.Store.Load() if err != nil { response.Internal(c, err) return } response.OK(c, gin.H{ "completed": st.Completed, "admin_email": st.AdminEmail, "acme_email": st.ACMEEmail, "fqdn": st.FQDN, }) } // SetContactEmails: Admin-only Update der zwei E-Mail-Felder. // Sessions bleiben aktiv (Cookie referenziert den alten Actor); auf // nächstem Login zählt der neue Wert. func (h *SetupHandler) SetContactEmails(c *gin.Context) { var req struct { AdminEmail string `json:"admin_email" binding:"required,email"` ACMEEmail string `json:"acme_email" binding:"required,email"` } if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err) return } if err := h.Store.SetContactEmails(req.AdminEmail, req.ACMEEmail); err != nil { response.BadRequest(c, err) return } st, err := h.Store.Load() if err != nil { response.Internal(c, err) return } if h.Audit != nil { _ = h.Audit.Log(c.Request.Context(), actorOf(c), "setup.contact_emails", st.AdminEmail, gin.H{ "admin_email": st.AdminEmail, "acme_email": st.ACMEEmail, }, h.NodeID) } response.OK(c, gin.H{ "admin_email": st.AdminEmail, "acme_email": st.ACMEEmail, }) } func (h *SetupHandler) Complete(c *gin.Context) { var req setup.Request if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, err) return } st, err := h.Store.Complete(req) if err != nil { response.BadRequest(c, err) return } response.OK(c, gin.H{ "completed": st.Completed, "admin_email": st.AdminEmail, "fqdn": st.FQDN, }) }