import { useCallback, useEffect, useMemo, useState } from "react" import { Link } from "react-router" import { Activity, AlertTriangle, Cpu, Database, Globe, HardDrive, Loader2, RefreshCw, RotateCw, Server, Users, Zap, } from "lucide-react" import { ArcadiaError, useArcadiaClient } from "@crema/arcadia-client" import { AlertBanner } from "@crema/feedback-ui" import { BarChart, Donut, Heatmap, LineChart, Sparkline, type ChartDatum, type SeriesPoint, } from "@crema/chart-ui" import { KpiTile, formatCompact, formatPercent } from "@crema/dashboard-ui" import { ComponentRow, IncidentTimeline, OverallStatus, type ComponentState, type StatusComponent, type StatusIncident, } from "@crema/status-ui" import { ChoroplethMap, WorldMapSvg, } from "@crema/map-ui" import { formatBytes } from "@crema/file-ui" import { AppShell } from "~/components/layout/app-shell" import { Badge } from "~/components/ui/badge" import { Button } from "~/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card" import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs" import { getActiveSessions, getAuditStats, getInfrastructureSummary, getJobStats, getRateLimits, getRecentJobs, getSpaces, listDroplets, retryJob, type ActiveSession, type AuditStats, type Droplet, type InfrastructureSummary, type JobStats, type ObanJob, type RateLimit, type Space, } from "~/lib/arcadia/monitoring" import { pageTitle } from "~/lib/page-meta" import { useSession } from "~/lib/session" import { useRegisterAdminContext } from "~/lib/admin-context" export const meta = () => pageTitle("Monitoring") interface DashboardData { jobStats: JobStats | null recentJobs: ObanJob[] sessions: { sessions: ActiveSession[]; count: number } | null rateLimits: RateLimit[] infraSummary: InfrastructureSummary | null spaces: Space[] droplets: Droplet[] auditStats: AuditStats | null } const EMPTY: DashboardData = { jobStats: null, recentJobs: [], sessions: null, rateLimits: [], infraSummary: null, spaces: [], droplets: [], auditStats: null, } export default function MonitoringRoute() { const session = useSession() const arcadia = useArcadiaClient() const [data, setData] = useState(EMPTY) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [autoRefresh, setAutoRefresh] = useState(true) const refresh = useCallback(async () => { setError(null) setLoading(true) try { const [ jobStats, recentJobs, sessions, rateLimits, infraSummary, spaces, droplets, auditStats, ] = await Promise.all([ getJobStats(arcadia).catch(() => null), getRecentJobs(arcadia, { limit: 50 }).catch(() => []), getActiveSessions(arcadia).catch(() => null), getRateLimits(arcadia).catch(() => []), getInfrastructureSummary(arcadia), getSpaces(arcadia), listDroplets(arcadia), getAuditStats(arcadia, { from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString(), }).catch(() => null), ]) setData({ jobStats, recentJobs, sessions, rateLimits, infraSummary, spaces, droplets, auditStats, }) } catch (err) { setError(err instanceof ArcadiaError ? err.message : "Failed to load monitoring data.") } finally { setLoading(false) } }, [arcadia]) useEffect(() => { if (session) refresh() }, [session, refresh]) // Auto-refresh every 30s for the live feel. useEffect(() => { if (!session || !autoRefresh) return const t = setInterval(refresh, 30000) return () => clearInterval(t) }, [session, autoRefresh, refresh]) const components = useMemo(() => buildStatusComponents(data), [data]) const summary = useMemo( () => ({ jobs: data.jobStats?.counts ?? {}, jobs_executing: data.jobStats?.counts?.executing ?? 0, jobs_retryable: data.jobStats?.counts?.retryable ?? 0, sessions_24h: data.sessions?.count ?? 0, droplets: data.droplets.length, spaces: data.spaces.length, audit_total_7d: data.auditStats?.total ?? 0, }), [data], ) useRegisterAdminContext("monitoring", summary) if (!session) { return (
Sign in required Monitoring requires an admin session.
) } return (

Server stats & health

Live view of background jobs, active sessions, infrastructure, and audit activity. Refreshes every 30s.

{error ? ( setError(null)}> {error} ) : null} {/* Service status board */}
Service health Derived from live signals on each subsystem.
{components.map((c) => ( ))}
{/* KPI tiles */}
} /> } tone={ (data.jobStats?.counts?.executing ?? 0) > 0 ? "info" : "neutral" } /> } tone={ (data.jobStats?.counts?.retryable ?? 0) > 0 ? "warning" : "neutral" } /> } />
Background jobs Sessions Audit activity Infrastructure Rate limits { try { await retryJob(arcadia, id) await refresh() } catch (err) { setError( err instanceof ArcadiaError ? err.message : "Retry failed.", ) } }} />
) } // Synthesize a status board from the live signals we have. function buildStatusComponents(d: DashboardData): StatusComponent[] { const apiOk = d.rateLimits.length > 0 const dbOk = d.sessions !== null const workersState: ComponentState = (() => { if (!d.jobStats) return "partial-outage" const r = d.jobStats.counts.retryable ?? 0 const x = d.jobStats.counts.discarded ?? 0 if (x > 100) return "major-outage" if (r > 50 || x > 0) return "degraded" return "operational" })() const storageState: ComponentState = d.spaces.length > 0 || d.infraSummary ? "operational" : "partial-outage" return [ { id: "api", name: "API", description: "/api/v1 — auth, REST endpoints", state: apiOk ? "operational" : "partial-outage", }, { id: "db", name: "Database", description: "Postgres — sessions, audit log", state: dbOk ? "operational" : "partial-outage", }, { id: "workers", name: "Background workers", description: "Oban — webhook delivery, scheduled tasks", state: workersState, }, { id: "storage", name: "Storage", description: "DigitalOcean Spaces / S3-compatible object storage", state: storageState, }, ] } // --- Jobs panel -------------------------------------------------------- function JobsPanel({ stats, recent, onRetry, }: { stats: JobStats | null recent: ObanJob[] onRetry: (id: number) => Promise }) { if (!stats) { return } text="No job stats available." /> } const stateData: ChartDatum[] = (Object.entries(stats.counts) as [string, number][]) .filter(([, n]) => n > 0) .map(([state, n]) => ({ label: state, value: n, color: jobStateColor(state) })) const queueData: ChartDatum[] = stats.queues.map((q) => { const totals = stats.by_queue[q] ?? {} const sum = Object.values(totals).reduce((a, n) => a + (n ?? 0), 0) return { label: q, value: sum } }) return (
Jobs by state {stateData.length === 0 ? (

No active jobs.

) : ( )}
    {stateData.map((d) => (
  • {d.label} {d.value}
  • ))}
Active jobs by queue {queueData.length === 0 ? (

No queued or executing jobs.

) : ( )}
Recent jobs Latest 50 — newest first. {recent.length === 0 ? (

No recent jobs.

) : (
    {recent.map((j) => (
  • {j.state} {j.worker} queue: {j.queue} attempt {j.attempt}/{j.max_attempts} · inserted{" "} {new Date(j.inserted_at).toLocaleString()} {j.completed_at ? ` · completed ${new Date(j.completed_at).toLocaleString()}` : ""} {j.errors && j.errors.length > 0 ? ( {j.errors[j.errors.length - 1]?.error ?? "(error)"} ) : null}
    {j.state === "retryable" || j.state === "discarded" ? ( ) : null}
  • ))}
)}
) } // --- Sessions panel ---------------------------------------------------- function SessionsPanel({ sessions, }: { sessions: { sessions: ActiveSession[]; count: number } | null }) { if (!sessions) { return } text="No session data available." /> } // Bucket sign-ins by hour for a 24h sparkline. const hourly = useMemo(() => { const now = Date.now() const buckets = Array.from({ length: 24 }, (_, i) => ({ x: i, y: 0, })) for (const s of sessions.sessions) { const t = new Date(s.last_sign_in_at).getTime() const ago = (now - t) / (60 * 60 * 1000) const idx = 23 - Math.floor(ago) if (idx >= 0 && idx < 24) buckets[idx].y++ } return buckets }, [sessions]) return (
Sign-ins over the last 24h One bar per hour, latest on the right.
{sessions.count} recent sessions {sessions.sessions.length === 0 ? (

No sign-ins in the last 24 hours.

) : (
    {sessions.sessions.map((s) => (
  • {s.email} {s.first_name || s.last_name ? `${s.first_name ?? ""} ${s.last_name ?? ""}`.trim() + " · " : ""} {s.user_type ?? "user"} · status: {s.status} {s.two_factor_enabled ? " · 2FA" : ""}
    {new Date(s.last_sign_in_at).toLocaleString()}
  • ))}
)}
) } // --- Audit panel ------------------------------------------------------- function AuditPanel({ stats }: { stats: AuditStats | null }) { if (!stats) { return } text="No audit stats available." /> } const bySeverity: ChartDatum[] = Object.entries(stats.by_severity ?? {}).map( ([k, v]) => ({ label: k, value: v, color: severityColor(k) }), ) const byResource: ChartDatum[] = Object.entries(stats.by_resource_type ?? {}) .sort((a, b) => b[1] - a[1]) .slice(0, 10) .map(([k, v]) => ({ label: k, value: v })) // Time series — only render if backend supplied it. const series: SeriesPoint[] | null = stats.over_time ? stats.over_time.map((p, i) => ({ x: i, y: p.total })) : null return (
Events by severity (7d) {bySeverity.length === 0 ? (

No events.

) : ( )}
Top resource types {byResource.length === 0 ? (

No events.

) : ( )}
{series ? ( Events over time ) : null}
) } // --- Infrastructure panel ---------------------------------------------- function InfraPanel({ summary, spaces, droplets, }: { summary: InfrastructureSummary | null spaces: Space[] droplets: Droplet[] }) { const dropletsByRegion = useMemo(() => { const out: Record = {} for (const d of droplets) { const r = typeof d.region === "string" ? d.region : d.region?.slug ?? d.region?.name ?? "unknown" out[r] = (out[r] ?? 0) + 1 } return out }, [droplets]) if (!summary && spaces.length === 0 && droplets.length === 0) { return ( } text="No infrastructure connected. Wire a DigitalOcean token in arcadia's .env to see this section populate." /> ) } return (
{summary ? ( DigitalOcean summary
              {JSON.stringify(summary, null, 2)}
            
) : null}
} /> } /> } />
{droplets.length > 0 ? (
Droplet regions Coloured continents indicate any droplets in that hemisphere. Droplets ({droplets.length})
    {droplets.slice(0, 20).map((d) => (
  • {d.name} {typeof d.region === "string" ? d.region : d.region?.slug ?? "—"} {d.size_slug ? ` · ${d.size_slug}` : ""} {d.vcpus ? ` · ${d.vcpus} vCPU` : ""} {d.memory ? ` · ${formatBytes(d.memory * 1024 * 1024)}` : ""}
    {d.status}
  • ))} {droplets.length > 20 ? (
  • + {droplets.length - 20} more
  • ) : null}
) : null} {spaces.length > 0 ? ( Spaces ({spaces.length})
              {JSON.stringify(spaces, null, 2)}
            
) : null}
) } function regionColorsFor(byRegion: Record): Record { // Best-effort mapping from DO region slugs to continent IDs the WorldMapSvg knows. // The lib exposes regions like "north-america", "europe", "asia", etc. const colors: Record = {} const continentOf = (r: string): string | null => { const lc = r.toLowerCase() if (/^(nyc|sfo|tor|ams_tor)/.test(lc)) return "north-america" if (/^(lon|ams|fra)/.test(lc)) return "europe" if (/^(blr|sgp)/.test(lc)) return "asia" if (/^(syd)/.test(lc)) return "oceania" return null } for (const r of Object.keys(byRegion)) { const c = continentOf(r) if (c) colors[c] = "var(--primary)" } return colors } // --- Rate limits panel ------------------------------------------------- function RateLimitsPanel({ limits }: { limits: RateLimit[] }) { if (limits.length === 0) { return ( } text="No rate-limit configuration available." /> ) } return ( Configured rate limits The maximum requests allowed per window for each authenticated bucket.
    {limits.map((l) => (
  • {l.type} Window: {l.window_seconds}s
    {l.max_requests.toLocaleString()} req
  • ))}
) } // --- helpers ---------------------------------------------------------- function PanelStub({ icon, text }: { icon: React.ReactNode; text: string }) { return ( {icon} {text} ) } function jobStateColor(state: string): string { switch (state) { case "executing": return "#3b82f6" case "available": case "scheduled": return "#a3a3a3" case "retryable": return "#f59e0b" case "discarded": return "#ef4444" case "cancelled": return "#737373" case "completed": return "#10b981" default: return "#9ca3af" } } function jobStateVariant( state: string, ): "default" | "secondary" | "destructive" | "outline" { if (state === "executing" || state === "completed") return "default" if (state === "discarded") return "destructive" if (state === "retryable" || state === "scheduled") return "secondary" return "outline" } function severityColor(s: string): string { if (s === "critical" || s === "error") return "#ef4444" if (s === "warning") return "#f59e0b" return "#94a3b8" }