This repository has been archived on 2026-04-03. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
core/server/handlers/dashboard_admin.go
Jeffrey Smith 11fd8c1e57 step 1: rename module chat-switchboard → switchboard-core
- go.mod module name
- All 714 import references across 289 Go files
- VERSION: 0.1.0
- CI DB names: switchboard_core_{ci,dev,test}
- Docker image: gobha/switchboard-core
- Test fixtures: JWT issuer, repo names
- .env.example, docker-compose container name
- Compiles clean (go build exit 0)
2026-03-25 19:48:04 -04:00

119 lines
3.0 KiB
Go

package handlers
import (
"database/sql"
"net/http"
"runtime"
"time"
"github.com/gin-gonic/gin"
"switchboard-core/database"
"switchboard-core/events"
"switchboard-core/health"
"switchboard-core/models"
"switchboard-core/store"
)
// ── Dashboard Admin Handler ─────────────────
// GET /api/v1/admin/dashboard
// Aggregates live operational data for the built-in admin monitoring page.
var processStartTime = time.Now()
type DashboardAdminHandler struct {
stores store.Stores
healthStore health.Store
hub *events.Hub
}
func NewDashboardAdminHandler(stores store.Stores, hs health.Store, hub *events.Hub) *DashboardAdminHandler {
return &DashboardAdminHandler{stores: stores, healthStore: hs, hub: hub}
}
type runtimeStats struct {
Goroutines int `json:"goroutines"`
HeapMB int `json:"heap_mb"`
SysMB int `json:"sys_mb"`
NumGC uint32 `json:"num_gc"`
GoVersion string `json:"go_version"`
}
func (h *DashboardAdminHandler) GetDashboard(c *gin.Context) {
ctx := c.Request.Context()
// Provider health summaries
var providerHealth []models.ProviderHealthSummary
windows, err := h.healthStore.ListAllCurrent(ctx)
if err == nil {
for _, w := range windows {
providerHealth = append(providerHealth, models.ProviderHealthSummary{
ProviderConfigID: w.ProviderConfigID,
Status: health.DeriveStatus(w.ErrorRate(), w.RequestCount),
RequestCount: w.RequestCount,
ErrorRate: w.ErrorRate(),
ErrorCount: w.ErrorCount,
RateLimitCount: w.RateLimitCount,
TimeoutCount: w.TimeoutCount,
AvgLatencyMs: w.AvgLatencyMs(),
MaxLatencyMs: w.MaxLatencyMs,
LastError: w.LastError,
LastErrorAt: w.LastErrorAt,
})
}
}
// 24h usage totals
since := time.Now().Add(-24 * time.Hour)
totals, _ := h.stores.Usage.GetTotals(ctx, store.UsageQueryOptions{
Since: &since,
})
// DB pool stats
var dbPool *sql.DBStats
if database.DB != nil {
stats := database.DB.Stats()
dbPool = &stats
}
// WebSocket connections
wsCount := 0
if h.hub != nil {
wsCount = h.hub.ConnCount()
}
// Recent errors from audit log (last 10 error-related entries)
var recentErrors []models.AuditEntry
entries, _, auditErr := h.stores.Audit.List(ctx, store.AuditListOptions{
ListOptions: store.ListOptions{Limit: 10},
ResourceType: "error",
})
if auditErr == nil {
recentErrors = entries
}
// Go runtime stats
var mem runtime.MemStats
runtime.ReadMemStats(&mem)
rt := runtimeStats{
Goroutines: runtime.NumGoroutine(),
HeapMB: int(mem.HeapAlloc / 1024 / 1024),
SysMB: int(mem.Sys / 1024 / 1024),
NumGC: mem.NumGC,
GoVersion: runtime.Version(),
}
// Uptime
uptime := time.Since(processStartTime).Truncate(time.Second).String()
SafeJSON(c, http.StatusOK, gin.H{
"provider_health": providerHealth,
"usage_24h": totals,
"db_pool": dbPool,
"ws_connections": wsCount,
"recent_errors": recentErrors,
"runtime": rt,
"uptime": uptime,
})
}