# Implementation Plan: Job 848 — First-Start Create Admin Modal 1.07 ## Status **[ALREADY_IMPLEMENTED]** The First-Start Create Admin Modal feature is fully implemented end-to-end in this repository. The user-facing symptom reported in the job ("HIPCTF heading + 'Frontend not built.' paragraph, no bootstrap dialog, no form controls, no submit button") is produced by the SPA fallback in `backend/src/frontend/spa.controller.ts:27` whenever `frontend/dist/browser/index.html` does not exist on disk at the moment the request is served. That `dist/browser/index.html` is now present (rebuilt during the current session), so navigating to `http://localhost:3000/` will serve the Angular SPA, which redirects to `/bootstrap` and renders the create-admin modal. No source code changes, no schema changes, and no new tests are required. ## 1. Architectural Reconnaissance - **Codebase style & conventions:** - Monorepo with npm workspaces (`backend/`, `frontend/`). - Backend: NestJS controllers + services, TypeORM repositories, `@nestjs/config`, argon2 for password hashing, class-validator / `ZodValidationPipe` for input, `ApiError` + `GlobalExceptionFilter` for the uniform `{ code, message }` error envelope. - Frontend: Angular 17+ standalone components, `ChangeDetectionStrategy.OnPush`, Angular Signals, reactive forms (`ReactiveFormsModule`), HTTP via `HttpClient` with `withCredentials: true`, separate `BootstrapService` (initialised flag) and `AuthService` (session token). - Persistence: SQLite via TypeORM, file path comes from `process.env.DATABASE_PATH` (default `/data/hipctf/hipctf.db`), `FRONTEND_DIST` env var names the built SPA directory. - **Data Layer:** SQLite (TypeORM). No schema change is needed. - **Test Framework & Structure:** - Jest with two projects (`backend`, `frontend`) configured in `tests/jest.config.js`. - Backend tests use `ts-jest` against an in-memory SQLite DB (`process.env.DATABASE_PATH = ':memory:'`) via `tests/backend/db-helper.ts`. - Frontend tests use `jest-environment-jsdom` with `tests/frontend/jest.setup.ts` providing Angular `TestBed` + `provideHttpClientTesting`. - All tests live under `tests/` and run via a single root command: `npm test` (alias for `jest --config tests/jest.config.js`). - **Required Tools & Dependencies:** No new tools. Existing `npm run build` in `setup.sh` rebuilds `frontend/dist/browser/index.html`, which is the actual fix for the symptom. No new packages are required. ## 2. Impacted Files - **To Modify:** None. - **To Create:** None. ### Where the feature already lives (for reference / verification) Backend: - `backend/src/modules/setup/setup.module.ts` - `backend/src/modules/setup/setup.controller.ts` — `POST /api/v1/setup/create-admin`. - `backend/src/modules/setup/setup.service.ts:39-97` — `createAdmin()` inside a TypeORM transaction with serialized bootstrap via `runBootstrap()` (lines 99-130) — guarantees that two concurrent callers cannot both observe an empty admin table; the loser gets `409 SYSTEM_INITIALIZED`. - `backend/src/modules/setup/dto/create-admin.dto.ts` - `backend/src/common/errors/error-codes.ts` — `SYSTEM_INITIALIZED`, `USERNAME_TAKEN`, `WEAK_PASSWORD`, `VALIDATION_FAILED`, `RATE_LIMITED`. - `backend/src/frontend/spa.controller.ts:11-28` — SPA fallback that emits the placeholder HTML **only** when no built `index.html` is found. Frontend: - `frontend/src/app/features/setup/setup-create-admin.component.ts` — the modal component (`data-testid="setup-submit"`, `setup-retry`, etc.). - `frontend/src/app/features/setup/setup-create-admin.component.html` - `frontend/src/app/features/setup/setup-create-admin.component.css` - `frontend/src/app/features/setup/setup-create-admin.service.ts` — `POST /api/v1/setup/create-admin` with CSRF preflight + error mapping. - `frontend/src/app/features/setup/setup-create-admin.validators.ts` — `passwordMatchValidator`. - `frontend/src/app/features/setup/setup-create-admin.field-errors.ts` — pure helpers `usernameError`, `passwordError`, `confirmError`. - `frontend/src/app/core/services/bootstrap.service.ts` — exposes `initialized()`, `markInitialized()`, and `passwordPolicy()`. - `frontend/src/app/core/services/auth.service.ts` — `setSession()` used on success. - `frontend/src/app/app.routes.ts` — `/bootstrap` route, guarded by `authGuard` which redirects to `/bootstrap` while `initialized() === false`. Existing tests that already cover this Job: - `tests/backend/setup-create-admin.spec.ts` - `tests/backend/bootstrap.integration.spec.ts` - `tests/backend/registration-rate-limit.spec.ts` - `tests/backend/api-error.spec.ts` - `tests/frontend/setup-create-admin.spec.ts` - `tests/frontend/guard-bootstrap-race.spec.ts` - `tests/frontend/auth-restore.spec.ts` ## 3. Proposed Changes No code changes. The only operational fix is to keep `frontend/dist/browser/index.html` in place. If a fresh container runs without it, `setup.sh` already invokes `npm --workspace frontend run build` (line 11) which produces the Angular bundle under `frontend/dist/browser/index.html`. The `SpaFallbackMiddleware` looks for the file in both `frontend/dist/index.html` and `frontend/dist/browser/index.html`, so the standard build output is resolved without any extra wiring. ## 4. Test Strategy No new tests. The relevant behaviour is already covered: - **Backend:** `tests/backend/setup-create-admin.spec.ts` exercises `POST /api/v1/setup/create-admin` happy path + each documented error (`SYSTEM_INITIALIZED`, `USERNAME_TAKEN`, `WEAK_PASSWORD`, `VALIDATION_FAILED`, `RATE_LIMITED`). - **Backend concurrency:** the `runBootstrap()` chain in `SetupService` is what serializes concurrent first-admin requests; `tests/backend/bootstrap.integration.spec.ts` exercises the bootstrap endpoint end-to-end. - **Frontend:** `tests/frontend/setup-create-admin.spec.ts` covers the modal's reactive form, server-error states, retry button, and the post-success navigation. `tests/frontend/guard-bootstrap-race.spec.ts` covers the `authGuard` redirect race with `BootstrapService`. Run the existing suite from the repo root with: ``` npm test ``` ## 5. Why this is reported as "not built" `SpaFallbackMiddleware` (`backend/src/frontend/spa.controller.ts:27`) sends: ```
Frontend not built.
``` when neither `frontend/dist/index.html` nor `frontend/dist/browser/index.html` exists. The Angular CLI's `application` builder writes to `dist/browser/index.html`, so after `npm --workspace frontend run build` the file is present and the SPA is served. The reported symptom was a snapshot taken before the build artefact existed; rebuilding resolves it.