package server import ( "html/template" "io/fs" "log/slog" "net/http" "git.coopcloud.tech/wiki-cafe/member-console/internal/auth" "git.coopcloud.tech/wiki-cafe/member-console/internal/embeds" "git.coopcloud.tech/wiki-cafe/member-console/internal/middleware" "github.com/spf13/viper" ) // OperatorHandler handles the main operator page type OperatorHandler struct { AuthConfig *auth.Config Logger *slog.Logger Templates *SafeTemplates } // OperatorHandlerConfig holds configuration for the operator handler type OperatorHandlerConfig struct { AuthConfig *auth.Config Logger *slog.Logger } // NewOperatorHandler creates a new OperatorHandler func NewOperatorHandler(cfg OperatorHandlerConfig) (*OperatorHandler, error) { // Parse operator template templateSubFS, err := fs.Sub(embeds.Templates, "templates") if err != nil { return nil, err } tmpl, err := template.ParseFS(templateSubFS, "operator.html") if err != nil { return nil, err } return &OperatorHandler{ AuthConfig: cfg.AuthConfig, Logger: cfg.Logger, Templates: NewSafeTemplates(tmpl, cfg.Logger), }, nil } // RegisterRoutes registers operator routes func (h *OperatorHandler) RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("GET /operator", h.requireOperatorRole(h.GetOperatorPage)) } // requireOperatorRole is middleware that checks for the operator role func (h *OperatorHandler) requireOperatorRole(next http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if !h.AuthConfig.HasRole(r, OperatorRole) { h.Logger.Warn("operator access denied", slog.String("path", r.URL.Path), slog.String("reason", "missing operator-member role")) http.Error(w, "Forbidden: operator-member role required", http.StatusForbidden) return } next(w, r) } } // OperatorPageData holds data for the operator page template type OperatorPageData struct { Name string Username string Email string KeycloakAccountURL string CSRFToken string } // GetOperatorPage handles GET /operator func (h *OperatorHandler) GetOperatorPage(w http.ResponseWriter, r *http.Request) { ctx := r.Context() name := h.AuthConfig.GetUserName(ctx) username := h.AuthConfig.GetUsername(ctx) email := h.AuthConfig.GetUserEmail(ctx) keycloakAccountURL := viper.GetString("oidc-idp-issuer-url") + "/account" csrfToken := middleware.CSRFToken(r) data := OperatorPageData{ Name: name, Username: username, Email: email, KeycloakAccountURL: keycloakAccountURL, CSRFToken: csrfToken, } h.Templates.Render(w, "operator.html", data) }