0817520e49823a5021052b3b240708d967593853
Second PR of the portal-admin User-list chantier per ADR-0020 §"v1 scope — User list (read-only)". Ships the read side: paginated, filterable HTTP endpoint that queries the `public.users` directory populated at sign-in by PR #140. The SPA viewer screen lands in the final PR of the chantier. What lands - AdminUsersQueryDto (admin/users-query.dto.ts): mirrors AdminAuditQueryDto's posture — filters all optional, every unknown key rejected by `forbidNonWhitelisted`, limit capped at MAX_LIMIT (200) / default 50. Filters: username (exact prefix), displayName (case-insensitive contains), audience (workforce | customer enum), lastSeenAtFrom/To (ISO-8601). - AdminUsersReader (admin/admin-users-reader.service.ts): Prisma typed client against `public.users` — no `SET LOCAL ROLE` dance because `public.users` has no role-based privilege gate; the trust boundary is the controller's @RequireAdmin guard. Order: `last_seen_at DESC, oid ASC` (the second clause is a deterministic tie-breaker for pagination during sign-in bursts that share a timestamp). COUNT + SELECT run in a single Prisma transaction so the `total` reported to the SPA matches what's on the page even under a concurrent sign-in. - AdminUsersController (admin/admin-users.controller.ts): GET /api/admin/users, @RequireAdmin at the class level, forwards the validated DTO to AdminUsersReader, then emits admin.users.query with { filters, resultCount } as the fishing-expedition deterrent (mirror of admin.audit.query from PR #132). - AuditWriter.adminUsersQuery() typed method + AdminUsersQueryInput type. Same outcome=success / payload shape as adminAuditQuery — two distinct event types so a reviewer can pivot directly on eventType without parsing payload. Tests: +18 specs (DTO validation 9, reader 9 covering COUNT+SELECT ordering, filter forwarding, default/cap on limit, range filter composition; controller 6; audit typed method 2). All 391 BFF specs pass. Out of scope (next PR of the chantier): - portal-admin /users screen — the SPA viewer with filter form + table + pagination, mirroring the /audit page that the audit viewer PR shipped. - Sign-in counts joined from `audit.events` on `actor_id_hash` (computed via HashUserIdService on the fly). Deferred until the v1 list ships and the admin demand makes itself known.
Documentation index
This is the entry point to all project documentation. It is maintained automatically: any addition, rename, or removal of a .md file under docs/ must be reflected here in the same change.
Conventions
- Documentation is written in English.
- One topic per file. Group related files into a folder when there are three or more.
- Cross-reference with relative links so they keep working in GitHub, IDEs, and exported sites.
- For architectural decisions, do not add them here — they belong in decisions/ as MADR 4.0.0 ADRs.
Sections
Daily development
- development.md — repo layout, prerequisites, initial setup, daily commands, observability dev-loop (Jaeger UI, log ↔ trace correlation), dependency updates (Renovate), conventional commit cycle. Day-to-day reference for working on the project.
Architecture
- architecture.md — cross-cutting Mermaid diagrams: C4 system context, C4 containers, Nx module boundaries, CI/CD pipeline. Single-decision diagrams (auth sequence, ERD, etc.) live inline in their ADR.
Onboarding & environment
Setup guides for new contributors:
- setup/01-wsl-terminal-setup.md — modern WSL terminal (Zsh + Powerlevel10k + CLI tools)
- setup/02-dev-web-stack.md — Node via nvm, pnpm via corepack, Docker
- setup/03-angular-nx-monorepo.md — Angular + Nx monorepo bootstrap
Operations & runbooks
Empty — to be populated when we deploy.
Security, performance, accessibility
Empty — placeholders to be filled with rationale docs alongside their corresponding ADRs.
Description
Languages
TypeScript
85.3%
JavaScript
5.4%
SCSS
4.3%
HTML
3.9%
Shell
0.8%
Other
0.3%