7.8 KiB
7.8 KiB
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
HttpClientcalls, and embeddedAdminCategoriesComponentrendering on both/admin/categoriesand/admin/general. Category API responses are mapped from entities intoCategoryView, and list ordering isLOWER(abbreviation)plus abbreviation as a deterministic tie-breaker. - Data Layer: TypeORM 0.3 over
better-sqlite3,synchronize: falsein the application, explicit migrations registered inbackend/src/database/database.module.ts, and startup migration execution throughDatabaseInitService.init()before listening. The persistent production database defaults to/data/hipctf/db.sqlite. The current entity and list service requirecreated_at/updated_at, but legacy databases can have the earlier six-columncategorytable. Although migration1700000000200attempts to repair timestamps and1700000000300defines 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 intests/backend/and run vianpm test(or focusednpm 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
/datafixtures are required. Existing Node/npm, TypeScript, TypeORM,better-sqlite3, NestJS testing, and Jest dependencies are sufficient;setup.shshould 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 afterUpdateSystemCategoryKeys1700000000300so existing/datadatabases 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
- 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"); addcreated_atandupdated_atonly when absent, then backfill blank/null values with an ISO UTC SQLite timestamp. This makes TypeORMSELECT c.*valid before any category rows are read throughCategoryEntity. - 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_keyandabbreviationexist 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
downnon-destructive for canonical data so rollback cannot delete user/challenge relationships.
- Add a new migration with a timestamp/name greater than
- Backend Logic & APIs:
- Keep
AdminCategoriesService.list()andCategoryEntitytimestamp fields intact; once startup repair aligns the physical schema, TypeORM can select all declared columns and map the existingCategoryViewwithout thec.created_at500. - Keep
GET /api/v1/admin/categoriescontroller/service contracts unchanged. Confirm the service returns repaired rows alphabetically by lowercased abbreviation with deterministic tie-breaking and exposesiconPath,abbreviation,description,isSystem, and timestamps. - Align
SYSTEM_CATEGORY_KEYS/SYSTEM_CATEGORY_METAwith 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.
- Keep
- Frontend UI Integration:
- No frontend code change is planned.
AdminGeneralComponentalready embedsAdminCategoriesComponent;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/generaland/admin/categorieswithout introducing a competing Angular effect or touching the reported NG0600 symptom.
- No frontend code change is planned.
4. Test Strategy
- Target Unit Test File:
tests/backend/category-repair-migration.spec.ts, plus minimal assertion updates intests/backend/migrations.spec.tsandtests/backend/database-init.spec.ts. - Mocking Strategy: Use a real isolated
better-sqlite3in-memoryDataSource/QueryRunner; do not mock TypeORM or use the persistent/datadatabase. Construct the exact legacycategorytable with onlyid,system_key,name,abbreviation,description, andicon_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.