Nieuwe /worker-logs pagina: een tabel van de laatste N (10/25/50/100) worker-runs met een inline detailpaneel dat de stream-json output van Claude Code als leesbare timeline toont (system-init, assistant-tekst, tool-calls/results, result-kaart). - lib/parse-worker-log.ts: pure parser — summarizeRunLog (tabel) + parseRunLog (timeline), discriminated-union events, server-side truncatie van grote tool-results. - lib/worker-logs.ts: server-only fs-toegang, leest uit WORKER_LOGS_DIR (read-only bind mount), naam-regex + pad-confinement, .gz support. - app/api/worker-logs[/[name]]: GET-routes, auth-guarded, force-dynamic. - app/worker-logs: server page + client view (tabel, N-selector, auto-refresh) + detail (timeline, auto-refresh tijdens in-progress run). Vereist een read-only bind mount van /srv/scrum4me/worker-logs in de ops-dashboard-container (docker-compose.yml + WORKER_LOGS_DIR in .env). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
49 lines
1.4 KiB
TypeScript
49 lines
1.4 KiB
TypeScript
'use client'
|
|
|
|
import Link from 'next/link'
|
|
import { usePathname } from 'next/navigation'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
const NAV_ITEMS = [
|
|
{ href: '/', label: 'Dashboard' },
|
|
{ href: '/docker', label: 'Docker' },
|
|
{ href: '/git', label: 'Git' },
|
|
{ href: '/systemd', label: 'systemd' },
|
|
{ href: '/caddy', label: 'Caddy' },
|
|
{ href: '/flows', label: 'Flows' },
|
|
{ href: '/audit', label: 'Audit' },
|
|
{ href: '/worker-logs', label: 'Worker Logs' },
|
|
{ href: '/settings', label: 'Settings' },
|
|
]
|
|
|
|
export default function AppNav() {
|
|
const pathname = usePathname()
|
|
|
|
return (
|
|
<nav className="sticky top-0 z-10 border-b border-border bg-background/95 backdrop-blur">
|
|
<div className="mx-auto max-w-6xl px-6 py-3 flex items-center gap-6">
|
|
<Link href="/" className="mr-2 text-sm font-semibold tracking-tight shrink-0">
|
|
Ops Dashboard
|
|
</Link>
|
|
{NAV_ITEMS.map((item) => {
|
|
const isActive =
|
|
item.href === '/' ? pathname === '/' : pathname.startsWith(item.href)
|
|
return (
|
|
<Link
|
|
key={item.href}
|
|
href={item.href}
|
|
className={cn(
|
|
'text-sm transition-colors',
|
|
isActive
|
|
? 'text-foreground font-medium'
|
|
: 'text-muted-foreground hover:text-foreground',
|
|
)}
|
|
>
|
|
{item.label}
|
|
</Link>
|
|
)
|
|
})}
|
|
</div>
|
|
</nav>
|
|
)
|
|
}
|