8.8 KiB
8.8 KiB
type, title, description, tags, timestamp
| type | title | description | tags | timestamp | ||||
|---|---|---|---|---|---|---|---|---|
| architecture | Backend Module Map | NestJS modules, controllers, services, and how they are wired together. |
|
2026-07-21T22:19:08Z |
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/{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 |
Four 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*) plus their services (AdminService, AdminGeneralService, AdminCategoriesService, AdminChallengesService, ChallengeFilesService). Gated by AdminGuard + @Roles('admin'). Imports TypeOrmModule.forFeature([UserEntity, CategoryEntity, ChallengeEntity, ChallengeFileEntity, SolveEntity]), AuthModule, UsersModule, SettingsModule, and CommonModule (for SseHubService, ThemeLoaderService). |
UploadsModule |
backend/src/modules/uploads/uploads.module.ts |
UploadsController (/api/v1/uploads/*). Admin-only multipart. |
BlogModule |
backend/src/modules/blog/blog.module.ts |
BlogController (GET /api/v1/blog/posts) + BlogService (lists status='published' rows). |
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 |
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 |
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. |
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().