// Package dashboard implements the admin dashboard's HTTP surface: a // server-rendered (html/template + htmx + Alpine.js) web UI giving full // coverage of control-api's /api/v1/admin/* API. It never talks to // internal/db directly and has no state of its own — every page and // fragment is computed fresh, on each request, from control-api's API via // the client in client.go. package dashboard import ( "html/template" "log/slog" "net/http" "time" ) type Config struct { ControlAPIBaseURL string ControlAPITimeout time.Duration LastCompletedCount int OverviewPollIntervalS int } type Server struct { CA *client Cfg Config tmpl *template.Template Log *slog.Logger } func New(cfg Config, log *slog.Logger) (*Server, error) { tmpl, err := parseTemplates() if err != nil { return nil, err } return &Server{ CA: newClient(cfg.ControlAPIBaseURL, cfg.ControlAPITimeout), Cfg: cfg, tmpl: tmpl, Log: log, }, nil } func (s *Server) Handler() http.Handler { mux := http.NewServeMux() s.routes(mux) return loggingMiddleware(s.Log, mux) } func loggingMiddleware(log *slog.Logger, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { next.ServeHTTP(w, r) log.Debug("request", "method", r.Method, "path", r.URL.Path) }) }