100 lines
11 KiB
Markdown
100 lines
11 KiB
Markdown
---
|
|
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-23T15:46:55Z
|
|
---
|
|
|
|
# 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, and imports `FilesystemTransactionModule` so startup recovery runs before the database is opened. |
|
|
| `FilesystemTransactionModule` | `backend/src/modules/admin/system/filesystem-transaction.module.ts` | Provides the shared durable filesystem swap service to both `DatabaseModule` startup recovery and `AdminSystemModule` restore operations without a circular dependency. |
|
|
| `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/{register,login,refresh,logout,me,change-password,csrf}`) + `AuthService` (login/refresh/logout/register-first-admin + public `register` + `getMe` + `changePassword`). Imports `SettingsModule` so the public-register flow can read the `registrationsEnabled` flag, and `UsersModule` (forwardRef) for `UsersRankService`. |
|
|
| `UsersModule` | `backend/src/modules/users/users.module.ts` | `UsersController` (`/api/v1/auth/register-first-admin`) + `UsersService` (last-admin invariant) + `UsersRankService` (`rankOfUser(manager, userId) -> {rank, points}` over the `solve` table). |
|
|
| `SetupModule` | `backend/src/modules/setup/setup.module.ts` | `SetupController` (`POST /api/v1/setup/create-admin`) + `SetupService`. Returns 409 `SYSTEM_INITIALIZED` once any admin exists; serializes concurrent first-admin attempts via an in-process promise chain. |
|
|
| `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` | Five controllers (`AdminController` for `/api/v1/admin/users*`, `AdminGeneralController` for `/api/v1/admin/general/*`, `AdminCategoriesController` for `/api/v1/admin/categories*`, `AdminChallengesController` for `/api/v1/admin/challenges*`, `AdminSystemController` for `/api/v1/admin/system/*`) plus their services (`AdminService`, `AdminGeneralService`, `AdminCategoriesService`, `AdminChallengesService`, `ChallengeFilesService`, `BackupService`, `RestoreService`, `DangerZoneService`, `FilesystemTransactionService`, `ConfirmationTokenService`). Gated by `AdminGuard` + `@Roles('admin')`. Imports `TypeOrmModule.forFeature([UserEntity, CategoryEntity, ChallengeEntity, ChallengeFileEntity, SolveEntity])`, `AuthModule`, `UsersModule`, `SettingsModule`, `CommonModule` (for `SseHubService`, `ThemeLoaderService`), and `AdminSystemModule` (which adds `AdminOperationTokenEntity` + `AuthModule`). |
|
|
| `AdminSystemModule` | `backend/src/modules/admin/system/admin-system.module.ts` | Hosts the destructive System operations: `AdminSystemController` + `BackupService` + `RestoreService` + `DangerZoneService` + `ConfirmationTokenService`. Imports shared `FilesystemTransactionModule`, `TypeOrmModule.forFeature([AdminOperationTokenEntity])`, and `AuthModule` (for `reauthenticateAdmin`/`revokeAllRefreshSessions`). |
|
|
| `UploadsModule` | `backend/src/modules/uploads/uploads.module.ts` | `UploadsController` (`/api/v1/uploads/*`). Admin-only multipart. |
|
|
| `BlogModule` | `backend/src/modules/blog/blog.module.ts` | `BlogController`/`BlogService` expose published posts at `GET /api/v1/blog/posts`; `AdminBlogController`/`AdminBlogService` provide guarded CRUD at `/api/v1/admin/blog/posts` using `BlogPostEntity`. |
|
|
| `ChallengesModule` | `backend/src/modules/challenges/challenges.module.ts` | `ChallengesController` (`/api/v1/challenges/{board,status,:id,:id/solves}`) + `ChallengesEventsController` (authenticated SSE `/api/v1/events`) + `ChallengesService` (board, detail, submit, scoring util). |
|
|
| `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` |
|
|
| `SetupController` | `/api/v1/setup/create-admin` | Public (bootstrap-only; 409 `SYSTEM_INITIALIZED` once an admin exists) | `backend/src/modules/setup/setup.controller.ts` |
|
|
| `AdminController` | `/api/v1/admin` | Admin only | `backend/src/modules/admin/admin.controller.ts` |
|
|
| `AdminGeneralController` | `/api/v1/admin/general` | Admin only | `backend/src/modules/admin/admin-general.controller.ts` |
|
|
| `AdminCategoriesController` | `/api/v1/admin/categories` | Admin only | `backend/src/modules/admin/admin-categories.controller.ts` |
|
|
| `AdminChallengesController` | `/api/v1/admin/challenges` | Admin only | `backend/src/modules/admin/admin-challenges.controller.ts` |
|
|
| `AdminSystemController` | `/api/v1/admin/system` | Admin only (plus confirmation tokens for destructive ops) | `backend/src/modules/admin/system/admin-system.controller.ts` |
|
|
| `SystemController` | `/api/v1` | Mixed (bootstrap/event/scoreboard/settings are public; `events/status` SSE is JWT-protected) | `backend/src/modules/system/system.controller.ts` |
|
|
| `UploadsController` | `/api/v1/uploads` | Admin only | `backend/src/modules/uploads/uploads.controller.ts` |
|
|
| `BlogController` | `/api/v1/blog` | Public | `backend/src/modules/blog/blog.controller.ts` |
|
|
| `AdminBlogController` | `/api/v1/admin/blog/posts` | Admin only | `backend/src/modules/blog/admin-blog.controller.ts` |
|
|
| `ChallengesController` | `/api/v1/challenges` | Authenticated | `backend/src/modules/challenges/challenges.controller.ts` |
|
|
| `ChallengesEventsController` | `/api/v1/events` | Authenticated (SSE) | `backend/src/modules/challenges/events.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 the 4-state event state machine (`countdown`/`running`/`stopped`/`unconfigured`) via `getState()` plus the legacy `getStatus()` shape (`Stopped`/`Running` + `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. |
|
|
| `scoring.util` | `backend/src/modules/challenges/scoring.util.ts` | `computeLivePoints`, `rankBonusForPosition`, `compareDifficulty`, and `safeEqualString` (constant-time flag compare). |
|
|
|
|
# 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()` → `FilesystemTransactionService.recoverTransactions()`
|
|
(resolves interrupted restore manifests before TypeORM opens the live SQLite file;
|
|
ambiguous generations abort startup) → stale unowned swap-artifact sweep → 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)
|
|
- [Blog API](/api/blog.md)
|
|
- [Admin System API](/api/admin-system.md)
|