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