package web import ( "database/sql" "log/slog" "net/http" "github.com/owen/vibeproxy/internal/auth" "github.com/owen/vibeproxy/internal/certificates" "github.com/owen/vibeproxy/internal/runtime" "github.com/owen/vibeproxy/internal/streams" ) type Web struct { db *sql.DB store *runtime.Store certs *certificates.Manager streams *streams.Manager csrf *auth.CSRF log *slog.Logger } // NewRouter builds the full server-rendered admin UI, protected by HTTP // Basic Auth against the single administrator account. func NewRouter(db *sql.DB, store *runtime.Store, certs *certificates.Manager, streamMgr *streams.Manager, csrf *auth.CSRF, log *slog.Logger) *http.ServeMux { web := &Web{db: db, store: store, certs: certs, streams: streamMgr, csrf: csrf, log: log} mux := http.NewServeMux() mux.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.FS(StaticFS())))) protected := http.NewServeMux() protected.HandleFunc("GET /{$}", web.handleRoot) protected.HandleFunc("GET /dashboard", web.handleDashboard) protected.HandleFunc("GET /proxy-hosts", web.handleProxyHostsList) protected.HandleFunc("GET /proxy-hosts/new", web.handleProxyHostForm) protected.HandleFunc("POST /proxy-hosts/new", web.handleProxyHostCreate) protected.HandleFunc("GET /proxy-hosts/{id}/edit", web.handleProxyHostForm) protected.HandleFunc("POST /proxy-hosts/{id}/edit", web.handleProxyHostUpdate) protected.HandleFunc("POST /proxy-hosts/{id}/delete", web.handleProxyHostDelete) protected.HandleFunc("POST /proxy-hosts/{id}/toggle", web.handleProxyHostToggle) protected.HandleFunc("GET /streams", web.handleStreamsList) protected.HandleFunc("GET /streams/new", web.handleStreamForm) protected.HandleFunc("POST /streams/new", web.handleStreamCreate) protected.HandleFunc("GET /streams/{id}/edit", web.handleStreamForm) protected.HandleFunc("POST /streams/{id}/edit", web.handleStreamUpdate) protected.HandleFunc("POST /streams/{id}/delete", web.handleStreamDelete) protected.HandleFunc("POST /streams/{id}/toggle", web.handleStreamToggle) protected.HandleFunc("GET /backends", web.handleBackendsList) protected.HandleFunc("GET /certificates", web.handleCertificatesList) protected.HandleFunc("POST /certificates/letsencrypt", web.handleCertificateIssue) protected.HandleFunc("POST /certificates/custom", web.handleCertificateUpload) protected.HandleFunc("POST /certificates/{id}/renew", web.handleCertificateRenew) protected.HandleFunc("POST /certificates/{id}/delete", web.handleCertificateDelete) protected.HandleFunc("GET /access-lists", web.handleAccessListsList) protected.HandleFunc("GET /access-lists/new", web.handleAccessListForm) protected.HandleFunc("POST /access-lists/new", web.handleAccessListCreate) protected.HandleFunc("GET /access-lists/{id}/edit", web.handleAccessListForm) protected.HandleFunc("POST /access-lists/{id}/edit", web.handleAccessListUpdate) protected.HandleFunc("POST /access-lists/{id}/delete", web.handleAccessListDelete) protected.HandleFunc("GET /logs", web.handleRequestLogs) protected.HandleFunc("GET /logs/audit", web.handleAuditLogs) protected.HandleFunc("GET /settings", web.handleSettings) protected.HandleFunc("POST /settings", web.handleSettingsUpdate) protected.HandleFunc("POST /settings/password", web.handlePasswordChange) protected.HandleFunc("POST /settings/backup", web.handleBackupNow) protected.HandleFunc("GET /api-tokens", web.handleAPITokens) protected.HandleFunc("POST /api-tokens/new", web.handleAPITokenCreate) protected.HandleFunc("POST /api-tokens/{id}/revoke", web.handleAPITokenRevoke) mux.Handle("/", auth.BasicAuthAdmin(db)(csrf.Protect(protected))) return mux } func (h *Web) handleRoot(rw http.ResponseWriter, r *http.Request) { http.Redirect(rw, r, "/dashboard", http.StatusFound) } func (h *Web) reload(r *http.Request) { if err := h.store.Reload(r.Context()); err != nil { h.log.Error("config reload failed", "err", err) } } func (h *Web) reloadStreams(r *http.Request) { if err := h.streams.Reload(r.Context()); err != nil { h.log.Error("stream reload failed", "err", err) } }