docs: update documentation to OKF v0.1 format
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
---
|
||||
type: architecture
|
||||
title: Backend Module Map
|
||||
description: NestJS modules, controllers, services, and how they are wired together.
|
||||
tags: [architecture, backend, nestjs, modules]
|
||||
timestamp: 2026-07-21T14:18:00Z
|
||||
---
|
||||
|
||||
# Module Map
|
||||
|
||||
All modules are imported in `backend/src/app.module.ts`. The root module
|
||||
also registers two global providers:
|
||||
|
||||
| Global provider | Purpose |
|
||||
|---------------------------|--------------------------------------------------------|
|
||||
| `APP_GUARD = JwtAuthGuard` | Enforces JWT auth unless the handler is `@Public()` |
|
||||
| `APP_FILTER = GlobalExceptionFilter` | Normalizes errors into the standard envelope |
|
||||
|
||||
# Modules
|
||||
|
||||
| Module | Path | Responsibility |
|
||||
|------------------|-----------------------------------------------|-------------------------------------------------------------------------------------------------|
|
||||
| `DatabaseModule` | `backend/src/database/database.module.ts` | Configures TypeORM with `better-sqlite3`, registers entities, runs migrations, enforces WAL. |
|
||||
| `CommonModule` | `backend/src/common/common.module.ts` | Provides shared services (CSRF middleware class, backoff, registration rate limit, SSE hub, theme loader, etc.). |
|
||||
| `SettingsModule` | `backend/src/modules/settings/settings.module.ts` | Exposes `SettingsService` (get/set/getAll over `setting` table). |
|
||||
| `AuthModule` | `backend/src/modules/auth/auth.module.ts` | `AuthController` (`/api/v1/auth/*`) + `AuthService` (login/refresh/logout/register-first-admin).|
|
||||
| `UsersModule` | `backend/src/modules/users/users.module.ts` | `UsersController` (`/api/v1/auth/register-first-admin`) + `UsersService` (last-admin invariant). |
|
||||
| `SystemModule` | `backend/src/modules/system/system.module.ts` | `SystemController` (`/api/v1/bootstrap`, `/event/status`, SSE streams) + `SystemService`. |
|
||||
| `AdminModule` | `backend/src/modules/admin/admin.module.ts` | `AdminController` (`/api/v1/admin/users*`) + `AdminService`. Gated by `AdminGuard` + `@Roles('admin')`. |
|
||||
| `UploadsModule` | `backend/src/modules/uploads/uploads.module.ts` | `UploadsController` (`/api/v1/uploads/*`). Admin-only multipart. |
|
||||
| `FrontendModule` | `backend/src/frontend/frontend.module.ts` | Registers `SpaFallbackMiddleware` and the uploads static helper. |
|
||||
|
||||
# Controllers
|
||||
|
||||
| Controller | Path prefix | Auth | Source |
|
||||
|------------------------|-------------------------|--------------|--------------------------------------------------------------|
|
||||
| `AuthController` | `/api/v1/auth` | Mostly public (login, refresh, logout, csrf) | `backend/src/modules/auth/auth.controller.ts` |
|
||||
| `UsersController` | `/api/v1/auth/register-first-admin` | Public (bootstrap-only) | `backend/src/modules/users/users.controller.ts` |
|
||||
| `AdminController` | `/api/v1/admin` | Admin only | `backend/src/modules/admin/admin.controller.ts` |
|
||||
| `SystemController` | `/api/v1` | Public | `backend/src/modules/system/system.controller.ts` |
|
||||
| `UploadsController` | `/api/v1/uploads` | Admin only | `backend/src/modules/uploads/uploads.controller.ts` |
|
||||
|
||||
# Common services
|
||||
|
||||
| Service | Path | Purpose |
|
||||
|----------------------------------|---------------------------------------------------------------|--------------------------------------------------------|
|
||||
| `CsrfMiddleware` | `backend/src/common/middleware/csrf.middleware.ts` | Mints/validates CSRF tokens via cookie + header. |
|
||||
| `JwtAuthGuard` | `backend/src/common/guards/jwt-auth.guard.ts` | Global guard; honors `@Public()` decorator. |
|
||||
| `AdminGuard` | `backend/src/common/guards/admin.guard.ts` | Requires authenticated user with `role === 'admin'`. |
|
||||
| `RolesGuard` | `backend/src/common/guards/roles.guard.ts` | Enforces `@Roles(...)` metadata. |
|
||||
| `GlobalExceptionFilter` | `backend/src/common/filters/global-exception.filter.ts` | Converts exceptions to standard envelope. |
|
||||
| `EventStatusService` | `backend/src/common/services/event-status.service.ts` | Computes `Stopped`/`Running` and `countdownMs`. |
|
||||
| `LoginBackoffService` | `backend/src/common/services/login-backoff.service.ts` | Per-IP+username brute-force throttle. |
|
||||
| `RegistrationRateLimitService` | `backend/src/common/services/registration-rate-limit.service.ts` | Per-IP rate limit on first-admin registration. |
|
||||
| `SseHubService` | `backend/src/common/services/sse-hub.service.ts` | In-process pub/sub for SSE streams. |
|
||||
| `ThemeLoaderService` | `backend/src/common/utils/theme-loader.service.ts` | Loads + validates the 10 canonical themes. |
|
||||
| `ZodValidationPipe` | `backend/src/common/pipes/zod-validation.pipe.ts` | Replaces `ValidationPipe` for zod-validated DTOs. |
|
||||
| `TransformInterceptor` | `backend/src/common/interceptors/transform.interceptor.ts` | Optional response transformer. |
|
||||
|
||||
# Decorators
|
||||
|
||||
| Decorator | Path | Purpose |
|
||||
|-----------------|------------------------------------------------------------|----------------------------------|
|
||||
| `@Public()` | `backend/src/common/decorators/public.decorator.ts` | Mark a route as unauthenticated. |
|
||||
| `@Roles(...)` | `backend/src/common/decorators/roles.decorator.ts` | Required role(s). |
|
||||
| `@SkipCsrf()` | `backend/src/common/decorators/skip-csrf.decorator.ts` | Exempt a route from CSRF check. |
|
||||
|
||||
# Startup chain
|
||||
|
||||
`main.ts` → `AppModule` (loads `ConfigModule`, then all feature modules)
|
||||
→ `DatabaseInitService.init()` (runs migrations + WAL)
|
||||
→ `app.use(...)` middlewares (helmet, parsers, CSRF, static)
|
||||
→ `SwaggerModule.setup(...)` (OpenAPI 3.1)
|
||||
→ `SpaFallbackMiddleware` → `app.listen()`.
|
||||
|
||||
# See also
|
||||
|
||||
- [System Overview](/architecture/overview.md)
|
||||
- [Key Files Index](/architecture/key-files.md)
|
||||
- [REST API Overview](/api/rest-overview.md)
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
type: architecture
|
||||
title: Frontend Structure
|
||||
description: Angular routes, components, services, guards, and interceptors.
|
||||
tags: [architecture, frontend, angular]
|
||||
timestamp: 2026-07-21T14:18:00Z
|
||||
---
|
||||
|
||||
# Routes
|
||||
|
||||
Routes live in `frontend/src/app/app.routes.ts`:
|
||||
|
||||
| Path | Component | Guard | Notes |
|
||||
|--------------|----------------------------------|---------------|----------------------------------------|
|
||||
| `/bootstrap` | `CreateAdminComponent` | — | Rendered only when `initialized === false`. |
|
||||
| `/login` | `LoginComponent` | — | Standard login form. |
|
||||
| `/` | `HomeComponent` | `authGuard` | Requires auth + initialized. |
|
||||
| `**` | Redirect to `/` | — | Wildcard fallback. |
|
||||
|
||||
# Components
|
||||
|
||||
All components are standalone (no NgModules). Each component imports
|
||||
`FormsModule` directly and uses Angular signals for state.
|
||||
|
||||
| Component | Path | Purpose |
|
||||
|------------------------|-----------------------------------------------------------------|-------------------------------------------------|
|
||||
| `AppComponent` | `frontend/src/app/app.component.ts` | Root; renders `<router-outlet>` and calls `BootstrapService.load()`. |
|
||||
| `HomeComponent` | `frontend/src/app/features/home/home.component.ts` | Landing page shown after auth. |
|
||||
| `LoginComponent` | `frontend/src/app/features/auth/login.component.ts` | Username/password form. |
|
||||
| `CreateAdminComponent` | `frontend/src/app/features/bootstrap/create-admin.component.ts`| First-admin bootstrap form. |
|
||||
|
||||
# Services
|
||||
|
||||
| Service | Path | Purpose |
|
||||
|---------------------|---------------------------------------------------------------|-----------------------------------------------------------|
|
||||
| `AuthService` | `frontend/src/app/core/services/auth.service.ts` | Signal-backed access token + current user. |
|
||||
| `BootstrapService` | `frontend/src/app/core/services/bootstrap.service.ts` | Fetches `/api/v1/bootstrap`, applies theme tokens to CSS. |
|
||||
|
||||
# Guards and interceptors
|
||||
|
||||
| Symbol | Path | Purpose |
|
||||
|---------------------|---------------------------------------------------------------|-----------------------------------------------------------|
|
||||
| `authGuard` | `frontend/src/app/core/guards/auth.guard.ts` | Redirects to `/bootstrap` when uninitialized, `/login` when not authenticated. |
|
||||
| `authInterceptor` | `frontend/src/app/core/interceptors/auth.interceptor.ts` | Attaches `Authorization: Bearer <accessToken>` header. |
|
||||
| `csrfInterceptor` | `frontend/src/app/core/interceptors/csrf.interceptor.ts` | Attaches `X-CSRF-Token` header on POST/PUT/PATCH/DELETE. |
|
||||
|
||||
Both interceptors are wired in `frontend/src/main.ts` via
|
||||
`provideHttpClient(withInterceptors([csrfInterceptor, authInterceptor]))`
|
||||
(notably CSRF runs first so the token is attached before the auth header).
|
||||
|
||||
# Bootstrap flow
|
||||
|
||||
1. `AppComponent.ngOnInit()` → `BootstrapService.load()`.
|
||||
2. `BootstrapService` calls `GET /api/v1/bootstrap` (public, with
|
||||
`credentials: 'include'`).
|
||||
3. The payload sets `initialized` (true iff any admin user exists),
|
||||
`pageTitle`, `logo`, `welcomeMarkdown`, the active `theme`, and the
|
||||
default challenge IP.
|
||||
4. Theme tokens are written to CSS custom properties on
|
||||
`document.documentElement` (`--color-primary`, `--font-family`,
|
||||
`--radius-*`, etc.) consumed by `frontend/src/styles.css`.
|
||||
5. The `authGuard` reads `BootstrapService.initialized()` and either
|
||||
allows navigation or redirects to `/bootstrap`.
|
||||
|
||||
# See also
|
||||
|
||||
- [System Overview](/architecture/overview.md)
|
||||
- [Backend Module Map](/architecture/backend-modules.md)
|
||||
- [First-Run Bootstrap](/guides/bootstrap.md)
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
type: architecture
|
||||
title: Key Files Index
|
||||
description: One-line responsibility for every important source file in the repository.
|
||||
tags: [architecture, index, key-files]
|
||||
timestamp: 2026-07-21T14:18:00Z
|
||||
---
|
||||
|
||||
# Backend
|
||||
|
||||
## Entrypoint & config
|
||||
|
||||
| File | Responsibility |
|
||||
|--------------------------------------------------------------------------|--------------------------------------------------------------------------------|
|
||||
| `backend/src/main.ts` | Bootstraps Nest, registers middleware, builds OpenAPI 3.1, mounts SPA fallback. |
|
||||
| `backend/src/app.module.ts` | Root module; wires every feature module + global guards/filters. |
|
||||
| `backend/src/config/env.schema.ts` | Zod-validated env schema, `SETTINGS_KEYS`, system category metadata. |
|
||||
|
||||
## Database
|
||||
|
||||
| File | Responsibility |
|
||||
|--------------------------------------------------------------------------|--------------------------------------------------------------------------------|
|
||||
| `backend/src/database/database.module.ts` | TypeORM `better-sqlite3` config, entity + migration registration. |
|
||||
| `backend/src/database/database-init.service.ts` | Initializes the DataSource, enforces WAL PRAGMA, runs migrations, verifies seed. |
|
||||
| `backend/src/database/migrations/1700000000000-InitSchema.ts` | Creates all tables and sets WAL + foreign keys. |
|
||||
| `backend/src/database/migrations/1700000000100-SeedSystemData.ts` | Seeds the 6 system categories and default settings. |
|
||||
| `backend/src/database/entities/user.entity.ts` | `user` table (admin/player role, enabled/disabled status). |
|
||||
| `backend/src/database/entities/setting.entity.ts` | `setting` key/value table. |
|
||||
| `backend/src/database/entities/category.entity.ts` | `category` table with `system_key` unique index. |
|
||||
| `backend/src/database/entities/challenge.entity.ts` | `challenge` table (difficulty, decay, protocol, flag, port). |
|
||||
| `backend/src/database/entities/challenge-file.entity.ts` | `challenge_file` table for attachments. |
|
||||
| `backend/src/database/entities/solve.entity.ts` | `solve` table with unique `(challenge_id, user_id)`. |
|
||||
| `backend/src/database/entities/refresh-token.entity.ts` | `refresh_token` table (token_hash + revocation). |
|
||||
| `backend/src/database/entities/blog-post.entity.ts` | `blog_post` table (draft/published + publishedAt index). |
|
||||
|
||||
## Common
|
||||
|
||||
| File | Responsibility |
|
||||
|--------------------------------------------------------------------------|--------------------------------------------------------------------------------|
|
||||
| `backend/src/common/common.module.ts` | Aggregates common services, middleware, and providers. |
|
||||
| `backend/src/common/middleware/csrf.middleware.ts` | Double-submit CSRF check; mints/reads cookie, compares header. |
|
||||
| `backend/src/common/guards/jwt-auth.guard.ts` | Global JWT guard; honors `@Public()`. |
|
||||
| `backend/src/common/guards/admin.guard.ts` | Requires `req.user.role === 'admin'`. |
|
||||
| `backend/src/common/guards/roles.guard.ts` | Enforces `@Roles(...)` metadata. |
|
||||
| `backend/src/common/decorators/public.decorator.ts` | `@Public()` marker. |
|
||||
| `backend/src/common/decorators/roles.decorator.ts` | `@Roles(...)` metadata. |
|
||||
| `backend/src/common/decorators/skip-csrf.decorator.ts` | `@SkipCsrf()` marker. |
|
||||
| `backend/src/common/filters/global-exception.filter.ts` | Converts all errors to the standard envelope. |
|
||||
| `backend/src/common/pipes/zod-validation.pipe.ts` | Validates body/param/query with a zod schema. |
|
||||
| `backend/src/common/interceptors/transform.interceptor.ts` | Optional response transformer. |
|
||||
| `backend/src/common/services/event-status.service.ts` | Computes event `Stopped`/`Running` and `countdownMs`. |
|
||||
| `backend/src/common/services/login-backoff.service.ts` | Per-IP+username failed-login throttle. |
|
||||
| `backend/src/common/services/registration-rate-limit.service.ts` | Per-IP first-admin registration throttle. |
|
||||
| `backend/src/common/services/sse-hub.service.ts` | In-process pub/sub for event status and scoreboard streams. |
|
||||
| `backend/src/common/utils/openapi31.ts` | Converts the Nest-generated OpenAPI 3.0 doc to OpenAPI 3.1. |
|
||||
| `backend/src/common/utils/password-policy.ts` | Argon2 password validator (length, mixed-case policy). |
|
||||
| `backend/src/common/utils/theme-loader.service.ts` | Loads themes from disk, backfills built-ins, validates tokens. |
|
||||
| `backend/src/common/utils/upload.ts` | Upload size-limit parser + filename sanitizer. |
|
||||
| `backend/src/common/types/theme.ts`, `theme-ids.ts` | `Theme` interface and the 10 canonical `ThemeId`s. |
|
||||
| `backend/src/common/errors/api-error.ts` | `ApiError` class (status + machine code + message + details). |
|
||||
| `backend/src/common/errors/error-codes.ts` | Canonical `ERROR_CODES` enum. |
|
||||
|
||||
## Modules
|
||||
|
||||
| File | Responsibility |
|
||||
|--------------------------------------------------------------------------|--------------------------------------------------------------------------------|
|
||||
| `backend/src/modules/auth/auth.module.ts` | Wires `AuthController`, `AuthService`, `JwtStrategy`. |
|
||||
| `backend/src/modules/auth/auth.controller.ts` | `/api/v1/auth/{login,refresh,logout,csrf}`. |
|
||||
| `backend/src/modules/auth/auth.service.ts` | Login, refresh, logout, register-first-admin, mint JWT + refresh tokens. |
|
||||
| `backend/src/modules/auth/cookie.ts` | `setRefreshCookie` / `clearRefreshCookie` helpers. |
|
||||
| `backend/src/modules/auth/dto/auth.dto.ts` | Zod schemas for login + refresh. |
|
||||
| `backend/src/modules/auth/strategies/jwt.strategy.ts` | Passport JWT strategy. |
|
||||
| `backend/src/modules/users/users.module.ts` | Wires `UsersController` + `UsersService`. |
|
||||
| `backend/src/modules/users/users.controller.ts` | `/api/v1/auth/register-first-admin`. |
|
||||
| `backend/src/modules/users/users.service.ts` | `enforceLastAdminInvariant` helper. |
|
||||
| `backend/src/modules/users/dto/create-first-admin.dto.ts` | Zod schema for first-admin registration. |
|
||||
| `backend/src/modules/admin/admin.module.ts` | Wires `AdminController` + `AdminService`. |
|
||||
| `backend/src/modules/admin/admin.controller.ts` | `/api/v1/admin/users[/{id}]`. |
|
||||
| `backend/src/modules/admin/admin.service.ts` | list / create / update role / delete user with last-admin guard. |
|
||||
| `backend/src/modules/admin/dto/admin.dto.ts` | Zod schemas for admin endpoints. |
|
||||
| `backend/src/modules/system/system.module.ts` | Wires `SystemController` + `SystemService` + status + hub services. |
|
||||
| `backend/src/modules/system/system.controller.ts` | `/api/v1/{bootstrap,event/status}` + SSE `/event/stream`, `/scoreboard/stream`. |
|
||||
| `backend/src/modules/system/system.service.ts` | Builds the public bootstrap payload from settings + theme + admin count. |
|
||||
| `backend/src/modules/uploads/uploads.module.ts` | Wires `UploadsController`. |
|
||||
| `backend/src/modules/uploads/uploads.controller.ts` | `/api/v1/uploads/{category-icon,challenge-file}`. |
|
||||
| `backend/src/modules/settings/settings.module.ts` | `SettingsService` (get/set/getAll over the `setting` table). |
|
||||
| `backend/src/frontend/frontend.module.ts` | Wires SPA fallback + uploads static middleware. |
|
||||
| `backend/src/frontend/spa.controller.ts` | `SpaFallbackMiddleware` (returns `index.html` for client routes). |
|
||||
| `backend/src/frontend/uploads-static.middleware.ts` | Mounts `/uploads` static helper. |
|
||||
|
||||
## Themes
|
||||
|
||||
| File | Responsibility |
|
||||
|----------------------------------------------|-------------------------------------------------------------|
|
||||
| `backend/themes/01-classic.json` | `classic` theme tokens. |
|
||||
| `backend/themes/02-midnight.json` … `10-monochrome.json` | The other 9 canonical themes. |
|
||||
|
||||
# Frontend
|
||||
|
||||
| File | Responsibility |
|
||||
|-----------------------------------------------------|--------------------------------------------------------------------------------|
|
||||
| `frontend/src/main.ts` | Bootstraps the standalone Angular app + registers interceptors. |
|
||||
| `frontend/src/index.html` | Root HTML shell. |
|
||||
| `frontend/src/styles.css` | Global styles consuming theme CSS custom properties. |
|
||||
| `frontend/src/app/app.component.ts` | Root component; triggers bootstrap on init. |
|
||||
| `frontend/src/app/app.routes.ts` | Angular route table. |
|
||||
| `frontend/src/app/core/services/auth.service.ts` | Signal store for access token + current user. |
|
||||
| `frontend/src/app/core/services/bootstrap.service.ts` | Fetches `/api/v1/bootstrap` and applies theme tokens to CSS. |
|
||||
| `frontend/src/app/core/guards/auth.guard.ts` | `CanActivateFn` checking init + auth. |
|
||||
| `frontend/src/app/core/interceptors/auth.interceptor.ts` | Attaches Bearer header. |
|
||||
| `frontend/src/app/core/interceptors/csrf.interceptor.ts` | Attaches `X-CSRF-Token` on unsafe methods. |
|
||||
| `frontend/src/app/features/auth/login.component.ts` | Login form. |
|
||||
| `frontend/src/app/features/bootstrap/create-admin.component.ts` | First-admin creation form. |
|
||||
| `frontend/src/app/features/home/home.component.ts` | Landing page after auth. |
|
||||
|
||||
# Tests
|
||||
|
||||
| File | Responsibility |
|
||||
|------------------------------------------------|--------------------------------------------------------------------------------|
|
||||
| `tests/backend/*.spec.ts` (18 suites) | Jest tests for backend modules, CSRF, OpenAPI 3.1, migrations, rate limits. |
|
||||
| `tests/frontend/theme.spec.ts` | Jest test for theme token application. |
|
||||
| `tests/backend/csrf-client.ts` | Test helper for CSRF flow. |
|
||||
| `tests/backend/db-helper.ts` | Test helper for spinning up an isolated DB. |
|
||||
|
||||
# See also
|
||||
|
||||
- [System Overview](/architecture/overview.md)
|
||||
- [Backend Module Map](/architecture/backend-modules.md)
|
||||
- [Frontend Structure](/architecture/frontend-structure.md)
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
type: architecture
|
||||
title: System Overview
|
||||
description: High-level layout of the HIPCTF monorepo (NestJS API + Angular SPA) and how they communicate.
|
||||
tags: [architecture, backend, frontend, overview]
|
||||
timestamp: 2026-07-21T14:18:00Z
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
HIPCTF is delivered as a Node.js npm workspace containing two packages:
|
||||
|
||||
| Package | Path | Stack |
|
||||
|-----------|-------------|----------------------------------|
|
||||
| Backend | `backend/` | NestJS 10 + TypeORM + better-sqlite3 |
|
||||
| Frontend | `frontend/` | Angular 17 standalone components |
|
||||
|
||||
The backend also serves the built Angular SPA as static files and falls
|
||||
back to `index.html` for client-side routes, so a single Node process
|
||||
hosts the whole application.
|
||||
|
||||
# Topology
|
||||
|
||||
```
|
||||
Browser ──HTTPS──▶ NestJS process (PORT, default 3000)
|
||||
│
|
||||
├── /api/v1/* ── controllers → services → TypeORM
|
||||
│ │
|
||||
│ ▼
|
||||
│ better-sqlite3
|
||||
│ (DATABASE_PATH)
|
||||
│
|
||||
├── /api/docs, /api/docs-json ── Swagger UI (OpenAPI 3.1)
|
||||
│
|
||||
├── /uploads/* ── static file server (UPLOAD_DIR)
|
||||
│
|
||||
└── /* ── SPA fallback (FRONTEND_DIST/index.html)
|
||||
```
|
||||
|
||||
# Backend entrypoint flow
|
||||
|
||||
`backend/src/main.ts` orchestrates startup in this order:
|
||||
|
||||
1. Create the Nest application.
|
||||
2. Read configuration via `ConfigService` (validated against
|
||||
`backend/src/config/env.schema.ts`).
|
||||
3. Call `DatabaseInitService.init()` so migrations + WAL PRAGMA run
|
||||
before HTTP traffic is accepted.
|
||||
4. Register global middleware: `helmet`, `cookieParser`, JSON / URL-encoded
|
||||
parsers, then `CsrfMiddleware` (registered explicitly so it can read
|
||||
the parsed body).
|
||||
5. Mount `/uploads` static and Angular static (`FRONTEND_DIST`).
|
||||
6. Build the OpenAPI 3.1 document via `toOpenApi31()` and expose Swagger
|
||||
UI at `/api/docs` and JSON at `/api/docs-json`.
|
||||
7. Install `SpaFallbackMiddleware` so non-`/api`, non-`/uploads`,
|
||||
extensionless `GET` requests return `index.html`.
|
||||
8. `app.listen(port)`.
|
||||
|
||||
# Frontend entrypoint flow
|
||||
|
||||
`frontend/src/main.ts` bootstraps a standalone Angular app:
|
||||
|
||||
1. `provideHttpClient(withInterceptors([csrfInterceptor, authInterceptor]))`
|
||||
attaches the CSRF header on unsafe methods and the Bearer access
|
||||
token on every request.
|
||||
2. `provideRouter(APP_ROUTES, withComponentInputBinding())`.
|
||||
3. `AppComponent.ngOnInit()` calls `BootstrapService.load()` which
|
||||
fetches `GET /api/v1/bootstrap` and applies the returned theme tokens
|
||||
to CSS custom properties on `document.documentElement`.
|
||||
|
||||
# Cross-cutting concepts
|
||||
|
||||
| Concern | Backend | Frontend |
|
||||
|----------------|------------------------------------------------------|---------------------------------------------|
|
||||
| Authentication | `JwtAuthGuard` (global) + `AuthGuard('jwt')` strategy | `AuthService` signal + `authInterceptor` |
|
||||
| Authorization | `AdminGuard` + `@Roles('admin')` decorator | `authGuard` route guard |
|
||||
| CSRF | `CsrfMiddleware` (skips `/api/v1/auth/login` and `/api/v1/auth/register-first-admin`) | `csrfInterceptor` reads `csrf` cookie |
|
||||
| Errors | `ApiError` → `GlobalExceptionFilter` returns `{code, message, details, path, timestamp}` | Components display `error?.error?.message` |
|
||||
| Config | `envSchema` (zod) + `validateEnv` | None (consumed via bootstrap) |
|
||||
|
||||
# See also
|
||||
|
||||
- [Backend Module Map](/architecture/backend-modules.md)
|
||||
- [Frontend Structure](/architecture/frontend-structure.md)
|
||||
- [REST API Overview](/api/rest-overview.md)
|
||||
Reference in New Issue
Block a user