162e495f29dccdf7df6473b2b5ee4e7743a70d56
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
162e495f29 |
feat(portals): serve each portal with Caddy instead of nginx (refs #166)
nginx resolves a variable `proxy_pass` upstream itself, using only the `resolver` directive and never the search domains in /etc/resolv.conf. That cost two workarounds in one script: rewriting the resolver address for rootless podman (Docker's 127.0.0.11 is wrong there), and injecting a full FQDN so the bare `bff` name could resolve on Kubernetes at all. Caddy dials its upstream per request through the system resolver, which reads nameserver *and* search domains, so `reverse_proxy bff:8080` resolves on every engine with no per-engine configuration — and it still starts before the BFF exists and picks up its restarts. Both workarounds are deleted with the script. Routing uses mutually-exclusive `handle` blocks, not a bare `try_files`: Caddy sorts rewrites *before* reverse_proxy, so a top-level SPA fallback would rewrite every API path to /index.html before the proxy saw it. |
||
|
|
8b206a005f |
S-26/#162 · Werkbak refreshes itself when a registration is ready for beoordeling (#164)
## What & why The behandel werkbak now **refreshes itself** while it is open, so a registration that reaches beoordeling after the behandelaar opened the page shows up on its own — no reload. `interval(WERKBAK_REFRESH_MS)` (5 s) re-reads the existing BFF endpoint, scoped to the page with `takeUntilDestroyed()`. A *background* read leaves the rows and states on screen alone until it has an answer, so a tick never flashes the loading state over rows being read and one failed poll never swaps the list for the error alert; a read that comes back also clears an earlier failure, so the view recovers on its own rather than needing the very reload this slice removes. No new endpoint, dependency or server-side state, and no service boundary moves — rxjs and `GET /behandel/werkbak` are both already here. **ADR-0032** records why polling rather than a pushed stream: nothing notifies the BFF either, so SSE/WebSockets would poll the domain *inside* the BFF for the same freshness, plus connection lifecycle, nginx buffering and a stateful BFF. Proposal: #163. Closes #162 ## Definition of Done - [x] Linked Gitea issue (above). - [x] Failing test committed before the implementation. - [x] Implementation makes the test pass; refactor commit if structure improved. - [x] Conventional Commits referencing the issue (`refs #162`). - [ ] CI green — all Gitea Actions jobs. - [x] `docker compose up` from a fresh clone reaches green health checks within 3 minutes (unchanged; only the behandel bundle differs). - [x] Docs updated if behaviour, contracts, or operations changed. - [x] ADR added in `docs/architecture/` (ADR-0032). - [x] Demo note in `docs/demo-script.md` (user-visible). ## Notes for reviewers **The e2e is the real acceptance test, and it took two goes to make it one.** Simply dropping the `staff.reload()` from the happy path proved nothing: the werkbak was visited *after* the documents were supplied, so the row was already there at page load. The spec now logs the behandelaar in **first**, asserts the row is not there yet, and only then has the citizen supply the documents that route it to Beoordelen — so the row can only reach that already-open, never-reloaded page via the refresh. Verified both ways against a live stack: with the interval stubbed out it fails at `Goedkeuren <ref> … element(s) not found` after 30 s; with it, the behandel nginx logs the poll that delivers the row. The page is foregrounded before the assertion because Chromium throttles timers in a hidden tab. **Ceiling (named in the ADR):** a fixed 5 s interval, per open page, that keeps polling in a background tab; each tick costs one Flowable task query plus a store read per open task. Upgrade path: publish task events from the domain, then swap the `interval` for a stream — the endpoint contract and the rendering stay put. Gate on `document.visibilityState` first if request volume is the concern. **Two housekeeping notes, neither blocking:** - #162 is on **no milestone** (DoD item 1). It is portal UX, so it fits neither *Data Governance* nor *Production Posture* cleanly — your call where it lands. - The issue titles itself **S-26**, which already belongs to the self-service resume slice (#111, `BACKLOG.md`). Everything here references **#162**; worth renumbering the title if the S-ids are meant to stay unique. `BACKLOG.md` is untouched for the same reason (it mirrors the active milestone, and this slice is on none).Reviewed-on: #164 |
||
|
|
fff88ca23d |
ci: richer step reports via Gitea 1.27 job summaries (closes #136) (#137)
## What & why Use the standard `$GITHUB_STEP_SUMMARY` (Gitea 1.27 + act_runner 2.0.0) to surface on the run page what was previously buried in logs or download-only artifacts. All five quick wins from #136, **reporting-only** — no job's pass/fail gating changes. Closes #136 ### Items 1. **Mutation scores** — added the `markdown` reporter to each `stryker-config.json`; the `mutation` job concatenates each service's `mutation-report.md` into the summary (`if: always()`). Also reveals where `make mutation` stopped on a ratchet break. 2. **Per-frontend tests** — the 4 apps' `test` targets emit vitest JSON to `test-output/{projectName}.json` (Nx token interpolation); `infra/vitest-summary.py` renders a per-frontend table. 3. **Per-service unit tests** — `make unit` now writes TRX; `infra/trx-summary.py` renders a per-service table (service name derived from the `services/<name>/` path, so `domain` shows, not `big.tests`). 4. **e2e per-spec results** — Playwright writes `playwright-report.json`; `run-e2e-check.sh` copies it out of the container (capturing the exit code first); `infra/playwright-summary.py` renders a per-spec table. Turns a red e2e into a one-glance "which spec". 5. **verify-stack check table** — each live-stack check has an `id`; a final `if: always()` step tabulates each check's ✅/❌/⏭️. Docs: `gitea-actions-gotchas.md` §8 (version requirement + `$GITHUB_STEP_SUMMARY` guard + step-level `always()` note). ### Notes - Every summary write is guarded with `[ -n "${GITHUB_STEP_SUMMARY:-}" ]`, so it no-ops on an unsupported runner / locally. - New helper scripts are stdlib-only Python, matching the existing `infra/*.py` check scripts (no new dependency — a few lines of parsing rather than a test-logger package). - `TestResults/` and `test-output/` gitignored. - This is also the first PR-run exercising the #135 verify-stack fix end to end. ## Verified locally `make unit` (TRX) ✓ · 4 apps' vitest JSON ✓ · ACL Stryker markdown report ✓ · all four parsers + the two summary shell blocks ✓ · `ci.yaml` + `run-e2e-check.sh` syntax ✓. The rendered summaries themselves only appear on the run page — this PR's CI run is the end-to-end check. ## Definition of Done - [x] Each item writes to `$GITHUB_STEP_SUMMARY` (guarded), renders on the run page. - [x] No change to any job's pass/fail gating. - [x] Conventional Commits referencing #136 (one per item + docs). - [ ] CI green; summaries visible on the run. - [x] Runbook note (gotchas §8). 🤖 Generated with [Claude Code](https://claude.com/claude-code)Reviewed-on: #137 |
||
|
|
8a537edd6c |
fix(infra): engine-portable portal nginx resolver (closes #96) (#97)
## What & why Closes #96. The portal nginx configs hardcode `resolver 127.0.0.11` (Docker's embedded DNS) for their variable `proxy_pass` to the BFF, so on rootless **podman** (network-specific aardvark DNS) every proxied call 502'd — the portals loaded and login worked, but no in-app data flowed. Add a shared `/docker-entrypoint.d` hook (`apps/portal-nginx-resolver.sh`, wired into all three portal Dockerfiles) that rewrites the resolver from the container's own `/etc/resolv.conf` at startup: a **no-op on Docker** (nameserver *is* 127.0.0.11) and **correct on podman** (rewrites to e.g. 10.89.0.1). nginx.conf is unchanged (the hardcoded value is the substitution anchor). ## How verified Built the behandel image and ran it on the compose network under podman: the hook rewrote the config to `resolver 10.89.0.1`, and `GET /behandel/werkbak` proxied to the BFF returning **401** (auth), not 502. On Docker the nameserver is 127.0.0.11 so the substitution is a no-op and CI/e2e behaviour is unchanged. Reviewed-on: #97 |
||
|
|
3abf8f7ccf |
feat(behandel): behandel-portal — werkbak + beoordeling (closes #13) (#87)
## What & why
Finishes **S-12 · Behandel-portal — werkbak + beoordeling**. The backend sub-slices (S-12a/b/c-1/c-2) were merged, but the slice's stated outcome — a behandel *portal* with medewerker login, a werkbak, and decide — had no frontend. This adds it.
- **`libs/auth`**: `MedewerkerAuthService` + `provideMedewerkerAuth` (Keycloak `medewerker` realm), a `roles`/`hasRole` surface on the shared `AuthService`, and a realm-roles protocol mapper so the SPA can read `behandelaar`/`teamlead` from the token. The BFF remains the security boundary (ADR-0013).
- **`apps/behandel`**: a new Nx Angular app mirroring self-service — medewerker OIDC login and a **werkbak** page listing registrations awaiting beoordeling (`GET /behandel/werkbak`) with per-row **Goedkeuren/Afwijzen** actions (`POST /behandel/registrations/{id}/decide`) that refresh the list. NL DS/Utrecht, standalone + signals.
- **e2e**: the walking-skeleton happy path now approves through the real portal (behandelaar logs in, finds the row by reference, clicks Goedkeuren) instead of the temporary admin endpoint.
- **infra/docs**: behandel service in compose (`:8142`, depends on Keycloak); added to the smoke `WAIT_SVCS` + CI log dump; `frontend-decisions.md` and `demo-script.md` updated.
Closes #13
## Definition of Done
- [x] Linked Gitea issue (above).
- [x] Failing test committed before the implementation.
- [x] Implementation makes the test pass; refactor commit if structure improved.
- [x] Conventional Commits referencing the issue (`refs #13`).
- [ ] CI green — all Gitea Actions jobs.
- [x] `docker compose up` from a fresh clone reaches green health checks within 3 minutes. *(behandel image + container verified locally; full stack gated in CI.)*
- [x] Docs updated if behaviour, contracts, or operations changed.
- [x] ADR added — ADR-0013 (merged with the backend sub-slices) already covers the wiring; no new decision here.
- [x] Demo note in `docs/demo-script.md`.
## Notes for reviewers
- Verified locally: auth + behandel + all frontend projects pass lint & unit tests (incl. axe WCAG 2.1 AA); production build green; the behandel Docker image builds and serves with the correct baked `medewerker` config + SPA fallback.
- The full compose-up smoke, e2e, and mutation are CI-gated (known local full-stack verify limits).
- **Follow-ups (not in scope):** the `WerkbakItem` contract has no citizen name (werkbak shows the BSN) — adding one is a BFF+domain contract change; and the domain's temporary admin `approve` endpoint is now unused by the e2e and could be removed.
Reviewed-on: #87
|