Files
HIPCTF2/docs/architecture/overview.md
T

86 lines
3.9 KiB
Markdown

---
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)