feat(PROJ-40,PROJ-41): Prometheus Metriken + Dashboard Zeitreihe
- PROJ-40: /api/health mit Version+Uptime, /metrics Prometheus-Format (mails_last_60min/24h/7d/30d, mails_total, storage_bytes, tenants_total, users_total, uptime_seconds) — Token-Schutz optional konfigurierbar - PROJ-41: GET /api/admin/stats/timeseries (30-Tage tagesgenau, Tenant-scoped) + SVG-Balkendiagramm im Dashboard (Mail-Eingang letzte 30 Tage) - storage.DBQueryRow() Helper für Metrics-Queries ohne Pool-Exposition - config.MetricsConfig (enabled, token) in config.go Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,32 @@ import (
|
||||
"archivmail/pkg/mailparser"
|
||||
)
|
||||
|
||||
// ── Mail Timeseries handler ───────────────────────────────────────────────
|
||||
|
||||
// handleMailTimeseries returns daily mail counts for the last 30 days.
|
||||
// GET /api/admin/stats/timeseries?days=30
|
||||
func (s *Server) handleMailTimeseries(w http.ResponseWriter, r *http.Request) {
|
||||
days := 30
|
||||
sess := sessionFromCtx(r.Context())
|
||||
tenantID := tenantFromCtx(r.Context())
|
||||
|
||||
// domain_admin sees only own tenant; superadmin sees all
|
||||
var tid *int64
|
||||
if sess.TenantID != nil {
|
||||
tid = tenantID
|
||||
}
|
||||
|
||||
points, err := s.store.MailTimeseries(r.Context(), days, tid)
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "timeseries query failed")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"days": days,
|
||||
"points": points,
|
||||
})
|
||||
}
|
||||
|
||||
// ── Audit Log handler ─────────────────────────────────────────────────────
|
||||
|
||||
func (s *Server) handleAuditLog(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user