93 lines
4.5 KiB
Markdown
93 lines
4.5 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-21T15:05: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()` then
|
|
`AuthService.restoreSession()`. Bootstrap fetches `GET /api/v1/bootstrap`
|
|
and applies the returned theme tokens to CSS custom properties on
|
|
`document.documentElement`. Session restore rehydrates the in-memory
|
|
auth signals from `sessionStorage` and posts `POST /api/v1/auth/refresh`
|
|
with `withCredentials: true` so a hard refresh does not log the user out.
|
|
4. Guards (`authGuard`, `adminGuard`) are async and `await`
|
|
`BootstrapService.ready()` + `AuthService.waitUntilHydrated()` before
|
|
reading state, so refreshing on a deep link never flashes `/bootstrap`
|
|
or `/login`.
|
|
|
|
# Cross-cutting concepts
|
|
|
|
| Concern | Backend | Frontend |
|
|
|----------------|------------------------------------------------------|---------------------------------------------|
|
|
| Authentication | `JwtAuthGuard` (global) + `AuthGuard('jwt')` strategy | `AuthService` signal + `authInterceptor`; session is mirrored to `sessionStorage` and rehydrated on boot via `/api/v1/auth/refresh` |
|
|
| Authorization | `AdminGuard` + `@Roles('admin')` decorator | `authGuard` for `/`, `adminGuard` for `/admin` (both async, both delegate to pure decision functions: `decideAuthRedirect` / `decideAdminGuard`) |
|
|
| 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)
|