package handler import ( "net/http" "time" "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5/pgxpool" ) type DashboardHandler struct { Pool *pgxpool.Pool } type DashboardStats struct { TotalArtifacts int `json:"total_artifacts"` TotalRepos int `json:"total_repos"` TotalCrashGroups int `json:"total_crash_groups"` OpenCrashGroups int `json:"open_crash_groups"` ActiveCampaigns int `json:"active_campaigns"` } type TrendPoint struct { Date string `json:"date"` Count int `json:"count"` } type TopCrasher struct { Title string `json:"title"` OccurrenceCount int `json:"occurrence_count"` RepoName string `json:"repo_name"` CrashGroupID string `json:"crash_group_id"` } type DashboardResponse struct { Stats DashboardStats `json:"stats"` Trend []TrendPoint `json:"trend"` TopCrashers []TopCrasher `json:"top_crashers"` } func (h *DashboardHandler) Stats(c *gin.Context) { ctx := c.Request.Context() var stats DashboardStats _ = h.Pool.QueryRow(ctx, "SELECT COUNT(*) FROM artifacts").Scan(&stats.TotalArtifacts) _ = h.Pool.QueryRow(ctx, "SELECT COUNT(*) FROM repositories").Scan(&stats.TotalRepos) _ = h.Pool.QueryRow(ctx, "SELECT COUNT(*) FROM crash_groups").Scan(&stats.TotalCrashGroups) _ = h.Pool.QueryRow(ctx, "SELECT COUNT(*) FROM crash_groups WHERE status = 'open'").Scan(&stats.OpenCrashGroups) _ = h.Pool.QueryRow(ctx, "SELECT COUNT(*) FROM campaigns WHERE status = 'running'").Scan(&stats.ActiveCampaigns) // Artifact trend for the last 30 days. var trend []TrendPoint rows, err := h.Pool.Query(ctx, ` SELECT DATE(created_at) as day, COUNT(*) FROM artifacts WHERE created_at >= $1 GROUP BY day ORDER BY day `, time.Now().AddDate(0, 0, -30)) if err == nil { defer rows.Close() for rows.Next() { var tp TrendPoint var d time.Time if rows.Scan(&d, &tp.Count) == nil { tp.Date = d.Format("2006-01-02") trend = append(trend, tp) } } } // Top crashers (most frequent open crash groups). var topCrashers []TopCrasher rows2, err := h.Pool.Query(ctx, ` SELECT cg.id, cg.title, cs.occurrence_count, r.name FROM crash_groups cg JOIN crash_signatures cs ON cs.id = cg.crash_signature_id JOIN repositories r ON r.id = cg.repository_id WHERE cg.status = 'open' ORDER BY cs.occurrence_count DESC LIMIT 10 `) if err == nil { defer rows2.Close() for rows2.Next() { var tc TopCrasher if rows2.Scan(&tc.CrashGroupID, &tc.Title, &tc.OccurrenceCount, &tc.RepoName) == nil { topCrashers = append(topCrashers, tc) } } } c.JSON(http.StatusOK, DashboardResponse{ Stats: stats, Trend: trend, TopCrashers: topCrashers, }) }