Files
HIPCTF2/.kilo/plans/893.md
T

38 lines
7.8 KiB
Markdown

# Implementation Plan: Admin Area General Settings and Categories 1.11
## 1. Architectural Reconnaissance
- **Codebase style & conventions:** Node.js npm-workspace monorepo with strict TypeScript. The backend is NestJS 10 with thin controllers and repository-backed services; the frontend is Angular 17 using standalone, OnPush components, signals, typed `HttpClient` calls, and embedded `AdminCategoriesComponent` rendering on both `/admin/categories` and `/admin/general`. Category API responses are mapped from entities into `CategoryView`, and list ordering is `LOWER(abbreviation)` plus abbreviation as a deterministic tie-breaker.
- **Data Layer:** TypeORM 0.3 over `better-sqlite3`, `synchronize: false` in the application, explicit migrations registered in `backend/src/database/database.module.ts`, and startup migration execution through `DatabaseInitService.init()` before listening. The persistent production database defaults to `/data/hipctf/db.sqlite`. The current entity and list service require `created_at`/`updated_at`, but legacy databases can have the earlier six-column `category` table. Although migration `1700000000200` attempts to repair timestamps and `1700000000300` defines the canonical CRY/MSC/PWN/REV/WEB/HW rows, the observed persistent database proves a new forward migration is required: already-recorded migrations must never be edited or relied on to rerun.
- **Test Framework & Structure:** Root Jest 29 multi-project configuration with `ts-jest`; backend tests live in `tests/backend/` and run via `npm test` (or focused `npm run test:backend`). Existing migration tests use an isolated in-memory SQLite database. Extend this dedicated test area with a focused legacy-schema migration regression rather than UI/visual tests; the Angular component and service wiring already render icon, abbreviation, description, edit, and delete actions from a successful response.
- **Required Tools & Dependencies:** No new system tools, global CLIs, npm packages, or `/data` fixtures are required. Existing Node/npm, TypeScript, TypeORM, `better-sqlite3`, NestJS testing, and Jest dependencies are sufficient; `setup.sh` should remain unchanged. Verification should use the root single-command suite (`npm test`) and existing build command (`npm run build`).
## 2. Impacted Files
- **To Modify:**
- `backend/src/database/database.module.ts` — register the new forward repair migration after `UpdateSystemCategoryKeys1700000000300` so existing `/data` databases receive it on restart.
- `tests/backend/migrations.spec.ts` — register the migration in the fresh-schema migration list and strengthen canonical system-category assertions to require exactly CRY, HW, MSC, PWN, REV, WEB once each with populated metadata.
- `tests/backend/database-init.spec.ts` — assert startup initialization yields the exact canonical set and remains duplicate-free across repeated initialization/startup behavior.
- `backend/src/config/env.schema.ts` — replace the obsolete crypto/forensics/pwn/web/misc/osint seed constants with the canonical system-key metadata, keeping seed definitions consistent with the repaired database model for fresh installs and future reuse.
- **To Create:**
- `backend/src/database/migrations/1700000000400-RepairCategorySchemaAndSystemCategories.ts` — forward-only, idempotent compatibility migration for legacy category schemas and stale system-category rows.
- `tests/backend/category-repair-migration.spec.ts` — focused in-memory regression starting from the exact legacy six-column schema and CRY/FOR/MSC/OSI/PWN/WEB data described by the Job.
## 3. Proposed Changes
1. **Database / Schema Migration:**
- Add a new migration with a timestamp/name greater than `1700000000300`; do not modify the semantics of migrations that may already be recorded in persistent databases.
- Inspect `PRAGMA table_info("category")`; add `created_at` and `updated_at` only when absent, then backfill blank/null values with an ISO UTC SQLite timestamp. This makes TypeORM `SELECT c.*` valid before any category rows are read through `CategoryEntity`.
- Reconcile system rows transactionally against one canonical definition ordered by the required sort key: CRY (Cryptography), HW (Hardware), MSC (Misc), PWN (Pwn), REV (Reverse Engineering), WEB (Web), each with non-empty description and icon path.
- Preserve user-created rows (`system_key IS NULL`). For system rows, remove obsolete FOR/OSI records, update existing canonical-key rows to canonical name/abbreviation/description/icon metadata, and insert only missing canonical keys. Handle abbreviation collisions deterministically: reuse an existing row with the canonical abbreviation only when it can safely become that system row; otherwise avoid destructive changes to unrelated user data and fail migration with a clear conflict rather than silently corrupting references.
- Ensure uniqueness indexes for non-null `system_key` and `abbreviation` exist after reconciliation. Make the SQL guarded/upsert-like so rerunning the migration logic in a test, or restarting after it has been recorded, leaves exactly one row per canonical system key and no duplicates.
- Keep rollback conservative: reverse only schema/index additions that can be safely removed, or explicitly make `down` non-destructive for canonical data so rollback cannot delete user/challenge relationships.
2. **Backend Logic & APIs:**
- Keep `AdminCategoriesService.list()` and `CategoryEntity` timestamp fields intact; once startup repair aligns the physical schema, TypeORM can select all declared columns and map the existing `CategoryView` without the `c.created_at` 500.
- Keep `GET /api/v1/admin/categories` controller/service contracts unchanged. Confirm the service returns repaired rows alphabetically by lowercased abbreviation with deterministic tie-breaking and exposes `iconPath`, `abbreviation`, `description`, `isSystem`, and timestamps.
- Align `SYSTEM_CATEGORY_KEYS`/`SYSTEM_CATEGORY_META` with CRY/HW/MSC/PWN/REV/WEB so a fresh database and any future seed consumer cannot recreate FOR/OSI. Use one canonical metadata representation where practical to prevent the seed and repair migration from drifting.
- Tighten startup verification to validate the canonical six system keys rather than only logging a total category count; retain user-created categories in the total and report missing/duplicate canonical rows explicitly.
3. **Frontend UI Integration:**
- No frontend code change is planned. `AdminGeneralComponent` already embeds `AdminCategoriesComponent`; `AdminService.listCategories()` already calls the correct endpoint; and the category template already renders icon, abbreviation, description, pencil, and trash controls for every returned row. Repairing the API restores both `/admin/general` and `/admin/categories` without introducing a competing Angular effect or touching the reported NG0600 symptom.
## 4. Test Strategy
- **Target Unit Test File:** `tests/backend/category-repair-migration.spec.ts`, plus minimal assertion updates in `tests/backend/migrations.spec.ts` and `tests/backend/database-init.spec.ts`.
- **Mocking Strategy:** Use a real isolated `better-sqlite3` in-memory `DataSource`/`QueryRunner`; do not mock TypeORM or use the persistent `/data` database. Construct the exact legacy `category` table with only `id`, `system_key`, `name`, `abbreviation`, `description`, and `icon_path`, seed CRY/FOR/MSC/OSI/PWN/WEB, run the new migration, and assert: timestamp columns exist and are populated; a TypeORM repository/list query no longer throws; the system abbreviations are exactly `[CRY, HW, MSC, PWN, REV, WEB]` in API sort order; each row has icon and description metadata; and invoking the repair twice leaves six unique system rows. Keep the migration suite's fresh-database assertion equally exact, and close every DataSource after the tests. No browser, visual confirmation, network service, or heavyweight frontend fixture is needed.