From 6feaf08bdb5d8e72fb981ff13e337b0442842d35 Mon Sep 17 00:00:00 2001 From: m0rph3us1987 Date: Tue, 21 Jul 2026 15:32:51 +0000 Subject: [PATCH] AI Implementation feature(842): First-Start Create Admin Modal 1.01 (#4) --- .kilo/plans/841.md | 178 ----------- .kilo/plans/842.md | 294 ++++++++++++++++++ docs/architecture/frontend-structure.md | 29 +- docs/architecture/key-files.md | 14 +- docs/architecture/overview.md | 17 +- docs/guides/session-restoration.md | 126 ++++++++ docs/index.md | 6 +- frontend/src/app/app.component.ts | 3 + frontend/src/app/core/guards/admin.guard.ts | 36 ++- .../app/core/guards/auth.guard.decision.ts | 19 ++ frontend/src/app/core/guards/auth.guard.ts | 23 +- .../src/app/core/services/auth.service.ts | 79 ++++- .../app/core/services/auth.session-storage.ts | 43 +++ .../app/core/services/bootstrap.service.ts | 16 + .../setup/setup-create-admin.component.ts | 11 + tests/frontend/auth-restore.spec.ts | 118 +++++++ tests/frontend/guard-bootstrap-race.spec.ts | 39 +++ 17 files changed, 834 insertions(+), 217 deletions(-) delete mode 100644 .kilo/plans/841.md create mode 100644 .kilo/plans/842.md create mode 100644 docs/guides/session-restoration.md create mode 100644 frontend/src/app/core/guards/auth.guard.decision.ts create mode 100644 frontend/src/app/core/services/auth.session-storage.ts create mode 100644 tests/frontend/auth-restore.spec.ts create mode 100644 tests/frontend/guard-bootstrap-race.spec.ts diff --git a/.kilo/plans/841.md b/.kilo/plans/841.md deleted file mode 100644 index 2e612ec..0000000 --- a/.kilo/plans/841.md +++ /dev/null @@ -1,178 +0,0 @@ -# Implementation Plan: Job 841 — First-Start Create Admin Modal: Authenticated Shell & Admin Navigation - -## 0. Already-Implemented Check - -The job is **NOT** fully implemented. Specifically: - -- `frontend/src/app/features/setup/setup-create-admin.component.ts:105` already calls `this.router.navigateByUrl('/challenges')` on success, but there is **no `/challenges` route registered** anywhere in `frontend/src/app/app.routes.ts` (only `bootstrap`, `login`, and `''` → `HomeComponent`). -- `frontend/src/app/features/home/home.component.ts` renders a plain `
` with no nav bar, no sidebar, no menu, and no `routerLink` to any admin area. -- `frontend/src/app/app.component.ts` only renders ``; there is no shared shell/sidebar component. -- `frontend/src/app/app.routes.ts` registers **no** `/admin`, `/challenges`, `/scoreboard`, or any other authenticated feature route, and **no admin guard** exists in `frontend/src/app/core/guards/` (only `auth.guard.ts`). -- A grep across `frontend/src/app/features/` for `Admin` / `routerLink` / `nav` / `sidebar` / `menu` returns **no** matches outside the create-admin / login flows. -- The backend admin surface exists (`backend/src/modules/admin/admin.controller.ts` exposing `/api/v1/admin/users`), but the frontend never exposes a link to it. - -Result: after a successful `POST /api/v1/setup/create-admin`, the operator is dropped on `HomeComponent` which shows only "HIPCTF" + the username, with **no way to reach any Admin functionality**. - -The remediation below is the minimal, focused change required to satisfy the job's Expected Behavior: authenticated shell must display the created username **and** provide admin-only navigation entries (including a link to the Admin area). - -## 1. Architectural Reconnaissance - -- **Codebase style & conventions:** - - Frontend: Angular 20 standalone components, signals (`signal`/`computed`), `inject()`, functional `CanActivateFn` guards. No NgModules. Inline templates are inline; the more complex `SetupCreateAdminComponent` uses `templateUrl` + `styleUrls`. - - Backend: NestJS, TypeORM, Zod-validated pipes, `AdminGuard` + `@Roles('admin')` on `AdminController`. REST surface at `/api/v1/...`. - - Test style: Jest via `tests/jest.config.js` with two projects (`backend` Node, `frontend` jsdom). Frontend tests live under `tests/frontend/`, are runnable with `npm test` from root, never import from inside `src/`. -- **Data Layer:** TypeORM with SQLite (file DB at `DATABASE_PATH`, `:memory:` in tests). No schema change is required for this job — `user.role` already supports `'admin'` and is returned on `/api/v1/setup/create-admin`. -- **Test Framework & Structure:** Jest. Tests **MUST** live under `tests/` (never inside `frontend/src/`). Run with `npm test` from repo root. -- **Required Tools & Dependencies:** None new. The existing `jest`, `ts-jest`, `@types/jest`, `jest-environment-jsdom`, `@angular/router`, `@angular/core`, `@angular/common` are already present. No `setup.sh` change is required. - -## 2. Impacted Files - -### To Modify - -- `frontend/src/app/app.routes.ts` — register the new admin-protected child route(s) and the admin guard. -- `frontend/src/app/features/home/home.component.ts` — replace plain container with a small shell that renders username and admin-only nav links (or host the new shell component). -- `frontend/src/app/features/setup/setup-create-admin.component.ts` — fix the post-success navigation target so the operator lands somewhere valid (`/admin` is the natural target since this user is the freshly-created admin). - -### To Create - -- `frontend/src/app/core/guards/admin.guard.ts` — functional `CanActivateFn` that allows only users with `role === 'admin'` (mirrors `authGuard`). -- `frontend/src/app/core/services/admin.service.ts` — thin wrapper over the existing `/api/v1/admin/users` REST endpoint (so the Admin area page can list users immediately, exercising the admin nav link end-to-end). -- `frontend/src/app/features/admin/admin-users.component.ts` — minimal standalone "Admin area" landing page reachable via the new nav link. Renders a list of users by calling `AdminService.list()`. Lazy-loaded. -- `tests/frontend/admin-navigation.spec.ts` — focused unit tests for the new guard and the nav-link rendering logic. -- `tests/frontend/admin-shell.spec.ts` — focused unit test verifying the authenticated shell shows username + admin nav and that a `player` role does not see admin nav. - -## 3. Proposed Changes - -### 1. Schema / Data -No DB migration required. `user.role` already supports `'admin'`, and `CurrentUser.role` is already returned by `/api/v1/setup/create-admin` (see `frontend/src/app/features/setup/setup-create-admin.service.ts:15`). - -### 2. Backend Logic & APIs -No backend changes required. The existing `AdminController` (`backend/src/modules/admin/admin.controller.ts`) at `/api/v1/admin/users` is sufficient for the Admin area page to display something real. - -### 3. Frontend UI Integration - -**3.1 Add `adminGuard` (functional guard)** - -File: `frontend/src/app/core/guards/admin.guard.ts` - -```ts -import { inject } from '@angular/core'; -import { CanActivateFn, Router } from '@angular/router'; -import { AuthService } from '../services/auth.service'; -import { BootstrapService } from '../services/bootstrap.service'; - -export const adminGuard: CanActivateFn = () => { - const auth = inject(AuthService); - const bootstrap = inject(BootstrapService); - const router = inject(Router); - - if (!bootstrap.initialized()) return router.createUrlTree(['/bootstrap']); - if (!auth.isAuthenticated()) return router.createUrlTree(['/login']); - if (auth.currentUser()?.role !== 'admin') return router.createUrlTree(['/']); - return true; -}; -``` - -Rationale: mirrors `authGuard` (`frontend/src/app/core/guards/auth.guard.ts`) and reuses the same signal-driven `AuthService` + `BootstrapService`. - -**3.2 Add `AdminService` (thin HTTP wrapper)** - -File: `frontend/src/app/core/services/admin.service.ts` - -- `listUsers(): Promise>` calls `GET /api/v1/admin/users` via `HttpClient` with `withCredentials: true`. -- Single-purpose; no caching layer needed for this job. - -**3.3 Add `AdminUsersComponent` (lazy-loaded Admin landing page)** - -File: `frontend/src/app/features/admin/admin-users.component.ts` - -- Standalone, `ChangeDetectionStrategy.OnPush`, signals (`users = signal<...>([])`, `loading = signal(false)`, `error = signal(null)`). -- On `ngOnInit`, calls `AdminService.listUsers()` and stores the result. -- Renders a heading ("Admin area") plus a list of users with `username` and `role`. -- Uses inline template (no extra `.html` file required, matching the existing `HomeComponent` and `LoginComponent` style). - -**3.4 Wire routes** - -File: `frontend/src/app/app.routes.ts` - -Add an authenticated `admin` child under the `''` route (still `authGuard`-protected) so the admin nav link resolves: - -```ts -{ - path: '', - loadComponent: () => import('./features/home/home.component').then(m => m.HomeComponent), - canActivate: [authGuard], - children: [ - { - path: 'admin', - canActivate: [adminGuard], - loadComponent: () => import('./features/admin/admin-users.component').then(m => m.AdminUsersComponent), - }, - ], -}, -``` - -And add `` inside `HomeComponent`'s template so the child route renders below the nav. - -**3.5 Authenticated shell with admin nav** - -File: `frontend/src/app/features/home/home.component.ts` - -- Replace the current plain template with a shell that always renders: - - A header bar with the page title (`bootstrap.payload()?.pageTitle ?? 'HIPCTF'`) on the left. - - On the right: the signed-in username + role (`Signed in as {{ username }} ({{ role }})`). - - A `