Commit graph

45 commits

Author SHA1 Message Date
Scrum4Me Agent
e5423de319 fix(deploy): update build context naar /srv/ops/repos/ops-dashboard
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 22:15:09 +02:00
Scrum4Me Agent
f6d0807a81 feat(widgets): voeg kleurbadges toe aan SystemdWidget en GitWidget
- SystemdWidget: groen als N=M healthy, oranje als 0<N<M, rood als N=0
- GitWidget: groen als 0 dirty repos, oranje als >0; toon K/M formaat

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 22:14:01 +02:00
Scrum4Me Agent
08d4b48190 feat(widgets): voeg relativeTime toe in lib/utils, expiringWarning-badge in CaddyWidget
- relativeTime(date: Date) helper toegevoegd aan lib/utils.ts
- AuditWidget gebruikt nu gedeelde relativeTime in plaats van inline functie
- CaddyWidget toont rode badge als soonest cert-expiry <30 dagen
- app/page.tsx berekent expiringWarning voor CaddyInitial

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 22:10:51 +02:00
Scrum4Me Agent
faa1463cd7 feat(dashboard): vervang SECTIONS-grid door 5 live status-widgets
Parallel server-side fetches via Promise.allSettled voor Docker, Caddy,
systemd, Git en Audit. Iedere widget toont geaggregeerde status en
refresht elke 30s client-side onafhankelijk van de andere widgets.

- lib/agent-fetch.ts: gedeelde client-side streaming helper
- app/api/audit/latest/route.ts: GET endpoint voor AuditWidget refresh
- app/_components/DockerWidget.tsx: running/total containers
- app/_components/CaddyWidget.tsx: soonest cert expiry in dagen
- app/_components/SystemdWidget.tsx: healthy/total units (of niet geconfigureerd)
- app/_components/GitWidget.tsx: dirty repo count (of niet geconfigureerd)
- app/_components/AuditWidget.tsx: laatste FlowRun status + relatief tijdstip
- app/page.tsx: vervangt SECTIONS-grid, doet parallel fetches, rendert widgets

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 22:06:00 +02:00
Scrum4Me Agent
ae63876f21 feat(layout): integreer AppNav en update metadata
Importeer AppNav in root-layout, render boven <main className="flex-1">.
Metadata bijgewerkt naar title "Ops Dashboard" en ops-beschrijving.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:57:19 +02:00
Scrum4Me Agent
b604a828a1 feat(nav): voeg AppNav.tsx toe als sticky client-component met active-link state
Implementeert de globale top-navbar met 8 NAV_ITEMS (Dashboard + 7 modules),
actieve-link-detectie via usePathname, en Tailwind sticky/backdrop-blur styling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 21:54:29 +02:00
Janpeter Visser
f7821c05be
Merge pull request #3 from madhura68/fix/agent-bind-and-index-routes
Fix ops-agent install + add index pages voor /flows en /settings
2026-05-13 19:43:29 +00:00
Janpeter Visser
656aa27a7f feat(routes): index pages voor /flows en /settings
Beide routes hadden alleen sub-pages; /flows en /settings zelf gaven
404. Minimale index met kaartjes naar de bestaande sub-routes,
consistent met het home-dashboard. Onderdeel van IDEA-060 voor een
rijkere indexering later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 21:42:24 +02:00
Janpeter Visser
252e535f23 fix(deploy): install dev deps voor TypeScript-build, prune erna
`npm ci --omit=dev` voor `npx tsc` faalde omdat TypeScript in
devDependencies zit. npx probeerde de typo-squatter `tsc@2.0.4` te
installeren. Nu: volledige install → tsc → prune --omit=dev voor
slanke runtime.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 21:42:24 +02:00
Janpeter Visser
199ff06a88
Merge pull request #2 from madhura68/fix/nextjs16-and-prisma-v7-build
Fix Next.js 16 + Prisma v7 build + dashboard home
2026-05-13 19:22:53 +00:00
Janpeter Visser
9fbc5220bd feat(home): vervang Next.js boilerplate door dashboard-index
Sprint SP-1 maakte 7 module-routes (docker/git/systemd/caddy/flows/
audit/settings) maar liet app/page.tsx de create-next-app starter
houden. Tijdelijke kaartjes-grid die auth-check doet en doorlinkt
naar elke module. IDEA-060 is gelogd voor een rijke dashboard met
live status per module.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 21:20:24 +02:00
Janpeter Visser
2812bc83e1 fix(build): placeholder DATABASE_URL in builder stage
Prisma v7 vereist DATABASE_URL bij prisma generate (config-load via
prisma.config.ts) zelfs als generate niet daadwerkelijk verbindt.
Container-builds zonder env-file faalden hierop. Echte URL wordt nog
steeds runtime gezet via env_file in docker-compose.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 21:20:24 +02:00
Janpeter Visser
9a7191f4c1 fix(proxy): merge middleware.ts into proxy.ts for Next.js 16 compat
Next.js 16 staat alleen proxy.ts toe als de twee co-existeren; build
faalt met "Both middleware file and proxy file are detected". De CSP-
en CSRF-logica uit middleware.ts is samengevoegd in proxy.ts en de
auth-redirect blijft. CSRF-validatie geldt nu alleen voor POST /api/*,
auth-redirect alleen buiten /api — matcher uitgebreid om beide te
dekken.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 21:20:24 +02:00
Janpeter Visser
c147870456
Merge pull request #1 from madhura68/feat/sprint-sjg11oxq
Sprint: Ops dashboard
2026-05-13 18:23:55 +00:00
Scrum4Me Agent
caeb5f3306 feat(ops): self-update script, systemd units, README install guide, recovery runbook
- deploy/ops-dashboard-updater/update.sh: git pull → docker build → force-recreate → smoke-test
- deploy/ops-dashboard-updater/install.sh: installs script + systemd units to host
- ops-dashboard-updater.service / .timer: oneshot + daily 03:00 scheduled trigger
- README.md: Installation and Configuration sections (env files, ops-agent, updater)
- docs/runbooks/recovery.md: agent-crash, DB corruption/restore, container failure, cert expiry

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 20:10:21 +02:00
Scrum4Me Agent
09050d5ce7 feat(backup): add /settings/backups UI page with Backup now button
Server component fetches backup list via list_ops_backups agent command
and parses filename/size output. Client BackupsPanel component shows a
backup table and a Backup now button that triggers the backup_ops_db
flow with streaming terminal output and audit log link.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 20:07:34 +02:00
Scrum4Me Agent
4dd0490afc feat(backup): add ops-db backup commands, flow, and systemd timer
Adds pg_dump_ops_db, list_ops_backups, and cleanup_ops_backups to the
agent command whitelist. Includes a backup_ops_db flow YAML (dump +
30-day retention), and a systemd service/timer for daily automated
backups at 02:00.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 20:07:14 +02:00
Scrum4Me Agent
aa1fd41bec feat(security): rate-limit /api/flows/start, CSRF double-submit cookie, CSP headers
- Rate-limit /api/flows/start to 10 req/min per user (in-memory, matches login pattern)
- Add middleware.ts: validates x-csrf-token header against csrf_token cookie on all
  API POST requests; issues the cookie on GET if missing; sets CSP, X-Frame-Options,
  X-Content-Type-Options, and Referrer-Policy on all responses
- Add lib/csrf.ts: client-side apiFetch() wrapper that injects the CSRF header
- Update all client components (login, useFlowRun, docker, caddy, git, systemd)
  to use apiFetch() for POST requests
- Cookie config in login route already correct (NODE_ENV check, httpOnly, sameSite=strict)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 20:01:43 +02:00
Scrum4Me Agent
1e31e3b584 feat(flows): add update_caddy_config flow with validate, reload/force-restart, and smoke test
- Update flows.example/update_caddy_config.yml with caddy_validate → caddy_reload → smoke test steps and hostname comments
- Add flows.example/update_caddy_config_force.yml for docker compose hard restart variant
- Add /flows/update-caddy-config UI page with reload/force-restart toggle, dry-run mode showing pending Caddyfile preview, hostname detection, and audit log link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 19:54:03 +02:00
Scrum4Me Agent
6bee8e8741 feat(flows): add update_mcp_worker flow with git_status, force-recreate, and health check
- update_mcp_worker.yml: git_status -> git_fetch -> git_pull (all cwd=scrum4me-docker) -> docker_compose_build -> docker_compose_up_recreate -> wait_for_health_worker
- commands.yml.example: add docker_compose_up_recreate (--force-recreate) and wait_for_health_worker (polls /var/log/agent/current for 'pre-flight passed')

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 19:45:36 +02:00
Scrum4Me Agent
9f590f1732 feat(flows): add update_scrum4me_web flow and UI page
- Update ops-agent/flows.example/update_scrum4me_web.yml with full
  deployment steps: git_status, git_fetch, git_log_ahead, git_pull,
  npm_ci, prisma_migrate_deploy, npm_run_build, systemctl_restart,
  and smoke test against thuis.jp-visser.nl/api/products
- Add npm_ci, prisma_migrate_deploy, npm_run_build, and
  curl_smoke_scrum4me_thuis to commands.yml.example
- Add /flows/update-scrum4me-web UI page with Run and Dry Run buttons,
  streaming terminal output, and link to audit log on completion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 19:42:39 +02:00
Scrum4Me Agent
bdc24b57ba feat(flows): add YAML flow format, flow-runner, and /agent/v1/flow endpoint
- ops-agent/src/lib/flow-runner.ts: loads YAML flows, validates all steps
  against the command whitelist, executes sequentially; supports dry_run
  (emits WOULD RUN lines) and on_failure: abort|continue per step
- ops-agent/src/routes/flow.ts: POST /agent/v1/flow { flow_key, dry_run }
  streams step_start/stdout/stderr/step_done/done SSE events
- ops-agent/src/index.ts: register flow route, add FLOWS_PATH env var
- ops-agent/flows.example/: three flow definitions — update_scrum4me_web,
  update_mcp_worker, update_caddy_config; deploy to /etc/ops-agent/flows/
- ops-agent/commands.yml.example: add curl_smoke_scrum4me_web and
  docker_compose_ps_worker smoke-test commands
- app/api/flows/run/route.ts: Next.js proxy — creates FlowRun/FlowStep
  DB records per step, forwards SSE stream to browser
- hooks/useFlowRun.ts: add startFlow(flowKey, dryRun) method; handle
  step_start events to display step headers in the terminal
- components/StreamingTerminal.tsx: add 'info' line type (sky-400) for
  step headers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 19:22:34 +02:00
Scrum4Me Agent
3781fce1e2 feat(ui): add action buttons to Docker, Git, systemd, and Caddy modules
- Docker table: Restart and Stop buttons per container row (docker_compose_restart / docker_compose_stop)
- Git repos list: Fetch and Pull buttons per repo; Pull disabled when working tree is dirty
- systemd units list: Restart button per unit (systemctl_restart)
- Caddy: Edit link on /caddy page, new /caddy/edit page with textarea + 3-step Validate → Save+Reload flow
- All buttons open ConfirmDialog with exact agent-call preview, then stream output via StreamingTerminal
- Add docker_compose_stop to ops-agent/commands.yml.example

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 19:14:49 +02:00
Scrum4Me Agent
b74cf3d75f feat(audit): truncate stdout/stderr to 64KB + index FlowRun(user_id, started_at desc)
- Truncate accumulated stdout/stderr to last 64KB before persisting FlowStep
  to prevent unbounded DB growth on verbose commands
- Add @@index([user_id, started_at(sort: Desc)]) to FlowRun schema so audit
  list queries (WHERE user_id = ? ORDER BY started_at DESC) use the index
- Add migration 20260513200000_flowrun_user_idx

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 18:03:06 +02:00
Scrum4Me Agent
2ed378fb8f feat(audit): add /audit list and /audit/[flow_run_id] detail pages 2026-05-13 18:00:37 +02:00
Scrum4Me Agent
f99b12ad5c feat(hooks): add useFlowRun hook for SSE flow execution 2026-05-13 18:00:26 +02:00
Scrum4Me Agent
394e8cdde3 feat(ui): add ConfirmDialog and StreamingTerminal components 2026-05-13 18:00:15 +02:00
Scrum4Me Agent
2baf116841 feat(flows): add /api/flows/start SSE endpoint with FlowRun/FlowStep DB logging 2026-05-13 18:00:04 +02:00
Scrum4Me Agent
12172eec95 feat(deploy): add sudoers config + setup.sh integration for systemctl_restart
/etc/sudoers.d/ops-agent grants NOPASSWD to ops-agent for the exact
systemctl restart invocations whitelisted in commands.yml.
setup.sh installs and validates it via visudo -c.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:53:09 +02:00
Scrum4Me Agent
234b2d1a58 feat(ops-agent): extend whitelist with destructive commands + preconditions
Adds docker_compose_restart/build/up, git_pull (guarded by
git_status_clean precondition), systemctl_restart (via sudo),
caddy_validate, caddy_reload, and caddy_write_config (atomic
stdin→Caddyfile.new→Caddyfile write).

- CommandDef gains preconditions[] and stdin_from_body fields
- exec route checks git_status_clean before git_pull; returns 409 on
  dirty tree with a clear message
- stdin field in ExecBody is piped to child stdin for caddy_write_config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:53:05 +02:00
Scrum4Me Agent
30f1b452a8 feat(caddy): /caddy page with config view and cert status table
- app/caddy/page.tsx: server component fetches caddy_show_config (shiki-highlighted Caddyfile) and caddy_list_certs (parsed CertInfo[])
- app/caddy/_components/caddy-view.tsx: client component shows cert table (domain, issuer CN, notBefore, notAfter) with Valid/Expiring soon/Expired badges; auto-refreshes every 60 seconds

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:48:51 +02:00
Scrum4Me Agent
1c51a0868f feat(caddy): add caddy_list_certs whitelist entry and cert parser
- Extend commands.yml.example with caddy_list_certs (sh loop over /data/caddy/certificates/*/*.crt using openssl)
- Add lib/parse-caddy.ts: parseCertList() parses CERTFILE/CERTEND delimited openssl output
- Add shiki ^1.29.2 dependency for server-side Caddyfile syntax highlighting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:48:41 +02:00
Scrum4Me Agent
c12e36e0a4 feat(systemd): unit overview + journal viewer pages
- Add journalctl_recent command and scrum4me-web to whitelist in commands.yml.example
- Add SYSTEMD_UNITS env var to .env.example
- lib/parse-systemd.ts: parse activeState, subState, uptime, description
- /app/systemd: server page reading SYSTEMD_UNITS, client list with 10s polling and status badges
- /app/systemd/[unit]: server detail page, client component showing systemctl status + last 100 journal lines (polling 10s)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:41:54 +02:00
Scrum4Me Agent
9e08a7c31f feat(git): /git overview page and diff viewer
Add git module with repo status overview (/git) and per-repo detail page
(/git/[repo]) featuring a color-coded diff viewer (+ green, - red).
Reads repo paths from REPO_PATHS env var, calls ops-agent git_status and
git_diff commands. Status badges: clean (green), dirty (orange),
behind-origin (blue).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:35:11 +02:00
Scrum4Me Agent
4821d29670 feat(ops-agent): cwd_pattern support + git command whitelist
Add validateCwd() to whitelist.ts for dynamic-cwd validation, update
exec.ts to resolve first arg as cwd when cwd_pattern is set, and extend
commands.yml.example with git_status, git_log_ahead, git_diff, git_fetch.
Add REPO_PATHS to .env.example.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:32:50 +02:00
Scrum4Me Agent
3cc966c70c fix(tsconfig): exclude ops-agent from Next.js TypeScript check
ops-agent has its own separate tsconfig and dependencies (fastify, js-yaml)
that are not installed in the root node_modules.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:28:55 +02:00
Scrum4Me Agent
90eacc963d feat(docker): agent-client helper, Docker container list page
- lib/agent-client.ts: server-side execAgent() that calls the ops-agent
  directly via OPS_AGENT_URL/OPS_AGENT_SECRET and streams SSE output
- lib/parse-docker.ts: pure parser for fixed-width docker ps table output
- app/docker/page.tsx: server component that fetches initial container list
  and passes it to the client component
- app/docker/_components/docker-table.tsx: client component with 5s
  auto-refresh via useEffect, status badge, and Link to /docker/[name]
- app/docker/[name]/page.tsx: placeholder detail page (logs in Story 3)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:27:35 +02:00
Scrum4Me Agent
92d450609c feat(auth): shared-secret auth web-app → ops-agent
- ops-agent/src/auth.ts: constant-time compare via timingSafeEqual to prevent timing attacks; store secret as Buffer
- ops-agent/src/index.ts + ops-agent.service: bind on 127.0.0.1:3099 (was 4242, per plan)
- app/api/agent/[...path]/route.ts: Next.js proxy route that verifies ops_session cookie then forwards requests to agent with Authorization: Bearer <secret>
- .env.example + deploy/ops-dashboard.env.example: add OPS_AGENT_SECRET and OPS_AGENT_URL
- README.md: rotation procedure for the shared secret

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:22:37 +02:00
Scrum4Me Agent
d605eb17a5 feat(ops-agent): whitelist-config parser + strict command executor
- CommandDef now uses cmd[] array instead of exec string — no shell splitting
- validateArgs() checks every request arg against allowed list; rejects unknown values
- spawn() called with shell:false (execFile semantics); cwd from config
- Audit log (JSON) per call to stdout → captured by systemd journal
- commands.yml.example updated to new schema with 4 read-only commands

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:18:45 +02:00
Scrum4Me Agent
4bccbf28f3 feat: ops-agent Fastify service met SSE, whitelist en systemd-unit
- ops-agent/: Node.js Fastify+TypeScript service
  - GET /agent/v1/health
  - POST /agent/v1/exec → SSE stream (stdout/stderr/exit events)
  - Whitelist geladen uit /etc/ops-agent/commands.yml bij opstart
  - Auth via Bearer shared secret (/etc/ops-agent/secret, mode 0640)
  - Vier standaard commando's: docker_ps, git_status, systemctl_status,
    caddy_show_config
- deploy/ops-agent/ops-agent.service: systemd-unit (User=ops-agent,
  Restart=on-failure, StandardOutput=journal)
- deploy/ops-agent/setup.sh: aanmaken system-user, build, deploy,
  systemctl enable --now ops-agent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:15:44 +02:00
Scrum4Me Agent
ad9cde6fb7 feat: Dockerfile, deploy configs en Caddy-block voor ops.jp-visser.nl
- Multi-stage Dockerfile (deps → builder → runner) met next standalone output
- .dockerignore zodat node_modules en .next niet mee worden gebundeld
- next.config.ts: output standalone ingeschakeld voor minimale image
- deploy/docker-compose.ops-dashboard.yml: service-fragment voor /srv/scrum4me/compose
- deploy/caddy/Caddyfile.ops-dashboard: reverse_proxy block voor Caddy
- deploy/ops-dashboard.env.example: env-template voor /srv/ops/ops-dashboard.env

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:12:37 +02:00
Scrum4Me Agent
be05724de0 feat: login page, session management, auth API routes en proxy guard
- lib/session.ts: token generatie, SHA-256 hashing, createSession/getCurrentUser/invalidateSession
- app/api/auth/login: bcrypt verificatie, session aanmaken, ops_session cookie (httpOnly, sameSite=strict, 24h TTL), rate-limit 5/min per IP
- app/api/auth/logout: session invalideren en cookie verwijderen
- app/login/page.tsx: login form (client component)
- proxy.ts: route-protectie – redirect naar /login zonder sessie (middleware.ts is deprecated in Next.js 16)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:10:07 +02:00
Scrum4Me Agent
cce0f25419 feat: Prisma schema, migrations en seed voor auth en audit-log
- Models: User, Session, FlowRun, FlowStep met FlowStatus enum
- prisma.config.ts met DATABASE_URL via @prisma/adapter-pg (Prisma 7 API)
- Initiële migratie applied op ops_dashboard database
- Seed-script voor 1 user via SEED_USER_EMAIL/SEED_USER_PASSWORD env-vars
- lib/prisma.ts als gedeelde singleton client voor Next.js

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 17:04:27 +02:00
Scrum4Me Agent
60393e40b1 feat: Next.js + Tailwind + shadcn/ui project skeleton
- create-next-app with TypeScript, App Router, Tailwind CSS
- shadcn/ui initialized with defaults (button component + utils)
- .env.example with DATABASE_URL and SESSION_SECRET placeholders
- README.md with docs/runbooks/ reference

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 16:59:21 +02:00
Janpeter Visser
f6cfa9f918 initial commit 2026-05-13 16:56:24 +02:00