docs(adr-0022): documentation site — vitepress + mermaid plugin
CI / check (pull_request) Successful in 2m16s
CI / scan (pull_request) Successful in 1m56s
CI / commits (pull_request) Successful in 1m55s
CI / a11y (pull_request) Successful in 1m36s
CI / perf (pull_request) Successful in 4m54s

Records the decision to render `docs/**/*.md` as a separately-deployed
static site using VitePress (Vite-based, Node-only toolchain) with
`vitepress-plugin-mermaid` for the existing C4 + sequence diagrams.

Site mapping:
  docs/index.md (new, Hero layout) → /
  docs/development.md              → /development
  docs/architecture.md             → /architecture
  docs/decisions/README.md         → /decisions/
  docs/decisions/00NN-…md          → /decisions/00NN-…  (auto sidebar)
  docs/setup/0N-…md                → /setup/0N-…

`docs/README.md` stays as the git-side / IDE-preview index and is
excluded from the published site. `docs/decisions/template.md` is
likewise excluded — authoring scaffold, not reader content.

Audience is internal (RSSI, devs, ops). Surfacing inside portal-admin
was considered and rejected — ADR-0020 §"Audience is disjoint"
frames portal-admin around operational workloads, not architecture
review. A decoupled site keeps the boundary clean.

Toolchain alignment is the deciding factor over MkDocs Material:
adding a Python runtime to Gitea Actions is a permanent maintenance
tax we can avoid by reusing the Vite stack already in the workspace.
Docusaurus + Astro Starlight are documented in "Pros and cons" as
runner-up + revisit-later options.

Implementation lands as a separate chantier (`.vitepress/config.ts`,
`docs/index.md`, Gitea Actions workflow, `package.json` scripts).
This commit is contained in:
Julien Gautier
2026-05-15 18:45:26 +02:00
parent e1805f596d
commit eebe20bc6f
3 changed files with 163 additions and 1 deletions
+3 -1
View File
@@ -50,13 +50,14 @@ The structural, security, observability, and quality choices are recorded as ADR
- **Internationalisation:** `@angular/localize` in build-time mode, two locales (`fr` default served at `/`, `en`), source locale = English (project English-only rule). Path-based URLs always prefixed (`/fr/...`, `/en/...`); `/` smart-redirects via cookie → `Accept-Language``fr`. UI strings live in XLIFF (`messages.fr.xlf`); editorial / CMS content is BFF-served already localised (see admin app). Footer hosts the locale switcher; switching writes a `__Host-portal_locale` cookie and hard-refreshes — see [ADR-0019](docs/decisions/0019-internationalisation-angular-localize.md).
- **Admin application (`portal-admin`):** dedicated Angular SPA alongside `portal-shell`, sharing the same `portal-bff` via `/api/admin/*` routes guarded by an Entra `Portal.Admin` role + `@RequireMfa({ freshness: 600 })` at entry. Distinct origin / cookie / session from `portal-shell` (`__Host-portal_admin_session`). v1 modules: CMS for static pages (multilingual), menu management, user list (read-only), audit log viewer. Bundle budget relaxed to ≤ 500 KB gzip (vs 300 KB for `portal-shell`); same a11y + dark-mode baseline. Shared UI primitives (`Icon`, `LayoutStateService`, brand tokens) graduate to `libs/shared/*` as both apps need them — see [ADR-0020](docs/decisions/0020-portal-admin-app.md).
- **Local quality gates:** Husky + lint-staged + commitlint with Conventional Commits — see [ADR-0007](docs/decisions/0007-pre-commit-hooks-and-conventional-commits.md).
- **Documentation site:** `docs/**/*.md` rendered as a separate static site via **VitePress** (Vite-based, Node-only toolchain, Markdown-first). Mermaid diagrams via `vitepress-plugin-mermaid`. Deployed on its own hostname behind the shared reverse-proxy; CI hook on `docs/` changes rebuilds + publishes. Decoupled from the apps — content lives in `docs/`, no in-app Markdown viewer — see [ADR-0022](docs/decisions/0022-docs-site-vitepress.md).
- **Runtime:** Node.js latest LTS major.
## Repository status
The Nx workspace is **scaffolded and operational**. The three apps (`portal-shell`, `portal-admin`, `portal-bff`) and the four lib roots (`libs/feature/`, `libs/shared/state`, `libs/shared/tokens`, `libs/shared/ui`, `libs/shared/util`) are in place; CI runs `format:check / lint / test / build` on every PR.
ADRs 0001 → 0021 are accepted and cover the structural, security, observability, quality, i18n, and admin-app choices. **Shipped on `main`:**
ADRs 0001 → 0022 are accepted and cover the structural, security, observability, quality, i18n, admin-app, and docs-site choices. **Shipped on `main`:**
- **Phase-1 foundation** — Nx workspace, Angular `portal-shell`, NestJS `portal-bff`, Prisma + Postgres, Pino + OpenTelemetry, Husky/lint-staged/commitlint, Gitea Actions CI.
- **Phase-2 auth + audit + security** — OIDC Auth Code + PKCE via MSAL Node, Redis sessions with AES-256-GCM at rest, idle 30 min sliding + absolute 12 h hard ceiling, RP-initiated logout, double-submit CSRF, `audit.events` append-only schema with role-based grants, helmet + env-driven CORS allowlist + rate limiting + structured error envelope (see [ADR-0021](docs/decisions/0021-phase-2-security-baseline.md)).
@@ -66,6 +67,7 @@ ADRs 0001 → 0021 are accepted and cover the structural, security, observabilit
- `DownstreamApiClient` + OBO ([ADR-0014](docs/decisions/0014-downstream-api-access-obo-pattern.md)) — no v1 consumer yet; will land when the first business route needs an Entra-protected API.
- `@RequireMfa()` / `@RequireAdmin()` guards ([ADR-0011](docs/decisions/0011-mfa-enforcement-entra-conditional-access.md), [ADR-0020](docs/decisions/0020-portal-admin-app.md)) — designed-in, awaiting first consumer route.
- **Docs static-site implementation** ([ADR-0022](docs/decisions/0022-docs-site-vitepress.md)) — ADR accepted, chantier (VitePress install + `.vitepress/config.ts` + `docs-site.yml` workflow) lands next.
- **Strategic security baseline ADR** — separate from the implementation-level [ADR-0021](docs/decisions/0021-phase-2-security-baseline.md). Remains **paused** awaiting RSSI input on the OWASP ASVS reference level and adjacent frameworks (HDS, GDPR, possibly NIS 2). When it lands it will either confirm 0021 or supersede pieces of it.
## Commands once the workspace exists