import { redirect } from 'next/navigation' import { getCurrentUser } from '@/lib/session' import { execAgent } from '@/lib/agent-client' import { prisma } from '@/lib/prisma' import { parseDockerPs } from '@/lib/parse-docker' import { parseCertList } from '@/lib/parse-caddy' import { parseSystemctlStatus } from '@/lib/parse-systemd' import { parseGitStatus } from '@/lib/parse-git' import DockerWidget, { type DockerInitial } from './_components/DockerWidget' import CaddyWidget, { type CaddyInitial } from './_components/CaddyWidget' import SystemdWidget, { type SystemdInitial } from './_components/SystemdWidget' import GitWidget, { type GitInitial } from './_components/GitWidget' import AuditWidget, { type AuditInitial } from './_components/AuditWidget' export const dynamic = 'force-dynamic' export default async function Home() { const user = await getCurrentUser() if (!user) redirect('/login') const systemdUnits = (process.env.SYSTEMD_UNITS ?? '') .split(',') .map((u) => u.trim()) .filter(Boolean) const repoPaths = (process.env.REPO_PATHS ?? '') .split(',') .map((p) => p.trim()) .filter(Boolean) const [[dockerResult, caddyResult, auditResult], unitResults, repoResults] = await Promise.all([ Promise.allSettled([ execAgent('docker_ps'), execAgent('caddy_list_certs'), prisma.flowRun.findFirst({ where: { user_id: user.id }, orderBy: { started_at: 'desc' }, select: { id: true, flow_key: true, status: true, started_at: true }, }), ]), Promise.allSettled(systemdUnits.map((unit) => execAgent('systemctl_status', [unit]))), Promise.allSettled(repoPaths.map((path) => execAgent('git_status', [path]))), ]) // Docker widget initial state const dockerInitial: DockerInitial = dockerResult.status === 'rejected' ? { data: null, error: dockerResult.reason instanceof Error ? dockerResult.reason.message : 'failed' } : (() => { const containers = parseDockerPs(dockerResult.value) return { data: { running: containers.filter((c) => c.status.toLowerCase().startsWith('up')).length, total: containers.length, }, error: null, } })() // Caddy widget initial state const caddyInitial: CaddyInitial = caddyResult.status === 'rejected' ? { data: null, error: caddyResult.reason instanceof Error ? caddyResult.reason.message : 'failed' } : (() => { const certs = parseCertList(caddyResult.value) const expiryTimes = certs .filter((c) => c.notAfter) .map((c) => new Date(c.notAfter).getTime()) return { data: { soonestExpiryMs: expiryTimes.length > 0 ? Math.min(...expiryTimes) : null, count: certs.length, expiringWarning: certs.some((c) => c.expiringWarning), }, error: null, } })() // Systemd widget initial state let systemdInitial: SystemdInitial if (systemdUnits.length === 0) { systemdInitial = { configured: false } } else if (unitResults.every((r) => r.status === 'rejected')) { const first = unitResults[0] systemdInitial = { data: null, error: first.status === 'rejected' && first.reason instanceof Error ? first.reason.message : 'all units failed', } } else { const healthy = unitResults.reduce((count, r, i) => { if (r.status !== 'fulfilled') return count return parseSystemctlStatus(r.value, systemdUnits[i]).activeState === 'active' ? count + 1 : count }, 0) systemdInitial = { data: { healthy, total: systemdUnits.length }, error: null } } // Git widget initial state let gitInitial: GitInitial if (repoPaths.length === 0) { gitInitial = { configured: false } } else if (repoResults.every((r) => r.status === 'rejected')) { const first = repoResults[0] gitInitial = { data: null, error: first.status === 'rejected' && first.reason instanceof Error ? first.reason.message : 'all repos failed', } } else { const dirty = repoResults.filter( (r) => r.status === 'fulfilled' && parseGitStatus(r.value).dirty, ).length gitInitial = { data: { dirty, total: repoPaths.length }, error: null } } // Audit widget initial state const auditInitial: AuditInitial = auditResult.status === 'rejected' ? { data: null, error: auditResult.reason instanceof Error ? auditResult.reason.message : 'failed' } : { data: auditResult.value ? { ...auditResult.value, started_at: auditResult.value.started_at.toISOString() } : null, error: null, } return (

Ops Dashboard

Welkom {user.email}

) }