package httpserver import ( "net/http" "strconv" "time" ) func (h *handler) health(w http.ResponseWriter, r *http.Request) { payload := map[string]any{ "status": "ok", "database": "ok", "service": h.config.AppName, "timestamp": time.Now().UTC().Format(time.RFC3339), } if h.store == nil { payload["status"] = "degraded" payload["database"] = "unavailable" payload["error"] = "database store is not initialized" writeJSON(w, http.StatusServiceUnavailable, payload) return } if err := h.store.HealthCheck(r.Context()); err != nil { payload["status"] = "degraded" payload["database"] = "unavailable" payload["error"] = "database ping failed" writeJSON(w, http.StatusServiceUnavailable, payload) return } writeJSON(w, http.StatusOK, payload) } func (h *handler) home(w http.ResponseWriter, r *http.Request) { data := HomePageData{ PageData: PageData{ Title: "Client Update API Ready", Eyebrow: "Usable Product Flow", Heading: h.config.AppName, BaseURL: h.config.BaseURL, Description: "Protected admin flows now connect all the way through to bearer-authenticated client endpoints for project discovery, latest-release metadata, release metadata, and private artifact downloads.", }, Links: []PageLink{ {Label: "Health check", Href: "/healthz", Description: "JSON readiness endpoint covering both the HTTP server and SQLite connectivity."}, {Label: "Admin login", Href: "/admin/login", Description: "Server-rendered login form for the protected admin workspace."}, {Label: "Projects", Href: "/admin/projects", Description: "Create projects, attach tags, upload releases, and inspect the client-facing metadata paths from the protected admin UI."}, {Label: "Tags", Href: "/admin/tags", Description: "Manage reusable tags that projects can use from day one."}, {Label: "API keys", Href: "/admin/api-keys", Description: "Generate hashed API keys, reveal the raw key once, preview accessible projects, and copy the client API quick-start flow."}, {Label: "Client API", Href: "/api/v1", Description: "Versioned JSON API entrypoint documenting the bearer-authenticated project, metadata, and download endpoints."}, }, } renderPage(w, h.renderer, "home", data) } func (h *handler) adminHome(w http.ResponseWriter, r *http.Request) { projects, err := h.store.Projects.List(r.Context()) if err != nil { http.Error(w, "project summary lookup failed", http.StatusInternalServerError) return } tags, err := h.store.Tags.List(r.Context()) if err != nil { http.Error(w, "tag summary lookup failed", http.StatusInternalServerError) return } apiKeys, err := h.store.APIKeys.List(r.Context()) if err != nil { http.Error(w, "api key summary lookup failed", http.StatusInternalServerError) return } totalReleases := 0 for _, item := range projects { totalReleases += item.ReleaseCount } data := DashboardPageData{ PageData: h.basePageData( r, "Admin Dashboard", "Protected Admin", "Admin Dashboard", "Projects, tags, releases, and API keys now feed the client update API so admins can complete the end-to-end product flow in one workspace.", ), Metrics: []DashboardMetric{ {Label: "Projects", Value: strconv.Itoa(len(projects)), Description: "Active and archived projects available for management."}, {Label: "Tags", Value: strconv.Itoa(len(tags)), Description: "Reusable labels ready for project assignment and future access scopes."}, {Label: "Releases", Value: strconv.Itoa(totalReleases), Description: "Stored release records with checksum and disk path metadata."}, {Label: "API Keys", Value: strconv.Itoa(len(apiKeys)), Description: "Hashed client credentials with permission flags and scope rules."}, }, Links: []PageLink{ {Label: "Manage projects", Href: "/admin/projects", Description: "Open project detail pages to edit metadata, attach tags, upload releases, and inspect client metadata or download paths."}, {Label: "Manage tags", Href: "/admin/tags", Description: "Create tags once and reuse them across projects from the same protected UI."}, {Label: "Manage API keys", Href: "/admin/api-keys", Description: "Generate, revoke, and scope client credentials, then copy the bearer-authenticated client API flow."}, {Label: "Client API", Href: "/api/v1", Description: "Public JSON index for accessible projects, latest release metadata, release metadata, and authenticated downloads."}, }, } renderPage(w, h.renderer, "admin", data) } func (h *handler) apiIndex(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, map[string]any{ "service": h.config.AppName, "version": "v1", "status": "client-api-ready", "auth": map[string]any{ "type": "bearer", "header": "Authorization: Bearer ", }, "routes": []map[string]string{ { "method": http.MethodGet, "path": "/api/v1/projects", "description": "List active projects accessible to the API key.", }, { "method": http.MethodGet, "path": "/api/v1/projects/{projectSlug}/releases/latest", "description": "Get the latest active release metadata for an accessible project.", }, { "method": http.MethodPost, "path": "/api/v1/projects/{projectSlug}/releases", "description": "Upload a release artifact for an accessible project.", }, { "method": http.MethodGet, "path": "/api/v1/releases/{releaseID}", "description": "Get release metadata for an accessible release.", }, { "method": http.MethodGet, "path": "/api/v1/releases/{releaseID}/download", "description": "Download the private artifact for an accessible release.", }, }, }) } func renderPage(w http.ResponseWriter, renderer *Renderer, name string, data any) { renderPageStatus(w, renderer, name, http.StatusOK, data) } func renderPageStatus(w http.ResponseWriter, renderer *Renderer, name string, status int, data any) { if err := renderer.Render(w, name, status, data); err != nil { http.Error(w, "template rendering failed", http.StatusInternalServerError) } }