package handlers import ( "net/http" "time" "xorm.io/xorm" "github.com/forgeo/forgebucket/internal/events" "github.com/forgeo/forgebucket/internal/models" "github.com/forgeo/forgebucket/internal/observability" ) // ── /health ─────────────────────────────────────────────────────────────────── type HealthHandler struct { db *xorm.Engine bus events.EventBus } func NewHealthHandler(db *xorm.Engine, bus events.EventBus) *HealthHandler { return &HealthHandler{db: db, bus: bus} } func (h *HealthHandler) Health(w http.ResponseWriter, r *http.Request) { status := observability.Check(h.db, h.bus) if status.Status != "healthy" { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusServiceUnavailable) jsonOK(w, status) return } jsonOK(w, status) } // ── /api/v1/repos/{owner}/{repo}/health ────────────────────────────────────── type RepoHealthHandler struct{ db *xorm.Engine } func NewRepoHealthHandler(db *xorm.Engine) *RepoHealthHandler { return &RepoHealthHandler{db: db} } type latestDeployment struct { EnvName string `json:"envName"` Status string `json:"status"` SHA string `json:"sha"` FinishedAt *time.Time `json:"finishedAt"` } type repoHealthResponse struct { CIPassRate7d float64 `json:"ciPassRate7d"` TotalRuns7d int `json:"totalRuns7d"` LatestRun *models.PipelineRun `json:"latestRun"` LatestDeployments []latestDeployment `json:"latestDeployments"` OpenDriftCount int `json:"openDriftCount"` OpenPRCount int `json:"openPRCount"` } // Get returns an operational health summary for a repository. // This feeds the repo page header: CI pass rate, latest deploy per env, drift count. func (h *RepoHealthHandler) Get(w http.ResponseWriter, r *http.Request) { repoID, ok := resolveRepoID(h.db, w, r) if !ok { return } since7d := time.Now().UTC().Add(-7 * 24 * time.Hour) // CI pass rate over last 7 days. var runs []models.PipelineRun h.db.Where("repo_id = ? AND created_at >= ?", repoID, since7d).Find(&runs) total := len(runs) succeeded := 0 for _, run := range runs { if run.Status == "succeeded" { succeeded++ } } var passRate float64 if total > 0 { passRate = float64(succeeded) / float64(total) } // Latest run overall. var latestRun models.PipelineRun var hasLatest bool hasLatest, _ = h.db.Where("repo_id = ?", repoID).Desc("id").Limit(1).Get(&latestRun) // Latest deployment per environment. var envs []models.Environment h.db.Where("repo_id = ?", repoID).Find(&envs) deploys := make([]latestDeployment, 0, len(envs)) for _, env := range envs { var d models.Deployment if found, _ := h.db.Where("env_id = ?", env.ID).Desc("id").Limit(1).Get(&d); found { deploys = append(deploys, latestDeployment{ EnvName: env.Name, Status: string(d.Status), SHA: d.SHA, FinishedAt: d.FinishedAt, }) } } // Open drift count (GitOpsConfigs where sync_status = 'drifted'). driftCount, _ := h.db.Where("repo_id = ? AND sync_status = 'drifted'", repoID). Count(&models.GitOpsConfig{}) // Open PR count. prCount, _ := h.db.Where("repo_id = ? AND status = 'open'", repoID). Count(&models.PullRequest{}) resp := repoHealthResponse{ CIPassRate7d: passRate, TotalRuns7d: total, LatestDeployments: deploys, OpenDriftCount: int(driftCount), OpenPRCount: int(prCount), } if hasLatest { resp.LatestRun = &latestRun } jsonOK(w, resp) }