Scaffold HIPCTF platform (NestJS + Angular)
- Backend: NestJS 10 + TypeORM (better-sqlite3) feature-modular layout.
- Entities: user, setting, category, challenge, challenge_file, solve,
refresh_token, blog_post. Auto-run migrations + idempotent 6-system-
category seed on startup.
- Argon2id password hashing with policy check; JWT access + rotating
refresh tokens (HttpOnly cookie); CSRF middleware (SameSite + custom
X-CSRF-Token header); global JWT auth guard with @Public() opt-out;
per-IP login backoff + per-IP registration rate limit.
- Endpoints: auth (login/refresh/logout/csrf), users (first-admin
registration), system (bootstrap/event status/SSE), admin (guarded
user CRUD with last-admin invariant), frontend module (uploads +
SPA fallback).
- Security: helmet+CSP+HSTS-gated-by-TLS, CORS allowlist, structured
global exception filter, Zod request validation pipes, OpenAPI 3.1
served at /api/docs and /api/docs-json, 10 canonical themes under
backend/themes/.
- Frontend: Angular 17 standalone components, lazy-loaded feature routes,
signals, functional HttpInterceptorFn (csrf + auth), functional
CanActivateFn auth guard, HttpOnly-cookie-based auth service.
- Tests: Jest + supertest, 46 tests across 13 suites covering
migrations, env schema, theme loader, event status, login backoff,
registration rate limit, ApiError shape, bootstrap integration,
auth refresh rotation, admin guard, last-admin invariant, SSE flat
payloads. Single-command runner: `npm test`.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "classic",
|
||||
"name": "Classic",
|
||||
"tokens": {
|
||||
"primary": "#3b82f6",
|
||||
"secondary": "#64748b",
|
||||
"accent": "#f59e0b",
|
||||
"surface": "#ffffff",
|
||||
"text": "#0f172a",
|
||||
"success": "#10b981",
|
||||
"warning": "#f59e0b",
|
||||
"danger": "#ef4444",
|
||||
"fontFamily": "system-ui, sans-serif",
|
||||
"radii": { "sm": "4px", "md": "8px", "lg": "16px" },
|
||||
"spacingScale": [4, 8, 12, 16, 24, 32, 48]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "midnight",
|
||||
"name": "Midnight",
|
||||
"tokens": {
|
||||
"primary": "#6366f1",
|
||||
"secondary": "#1e293b",
|
||||
"accent": "#22d3ee",
|
||||
"surface": "#0f172a",
|
||||
"text": "#e2e8f0",
|
||||
"success": "#34d399",
|
||||
"warning": "#fbbf24",
|
||||
"danger": "#f87171",
|
||||
"fontFamily": "system-ui, sans-serif",
|
||||
"radii": { "sm": "4px", "md": "8px", "lg": "16px" },
|
||||
"spacingScale": [4, 8, 12, 16, 24, 32, 48]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "sunset",
|
||||
"name": "Sunset",
|
||||
"tokens": {
|
||||
"primary": "#f97316",
|
||||
"secondary": "#fb7185",
|
||||
"accent": "#facc15",
|
||||
"surface": "#fff7ed",
|
||||
"text": "#431407",
|
||||
"success": "#84cc16",
|
||||
"warning": "#f59e0b",
|
||||
"danger": "#dc2626",
|
||||
"fontFamily": "Georgia, serif",
|
||||
"radii": { "sm": "6px", "md": "12px", "lg": "24px" },
|
||||
"spacingScale": [4, 8, 12, 16, 24, 32, 48]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "forest",
|
||||
"name": "Forest",
|
||||
"tokens": {
|
||||
"primary": "#16a34a",
|
||||
"secondary": "#65a30d",
|
||||
"accent": "#ca8a04",
|
||||
"surface": "#f0fdf4",
|
||||
"text": "#14532d",
|
||||
"success": "#15803d",
|
||||
"warning": "#ca8a04",
|
||||
"danger": "#b91c1c",
|
||||
"fontFamily": "system-ui, sans-serif",
|
||||
"radii": { "sm": "4px", "md": "8px", "lg": "12px" },
|
||||
"spacingScale": [4, 8, 12, 16, 24, 32, 48]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "cyber",
|
||||
"name": "Cyber",
|
||||
"tokens": {
|
||||
"primary": "#00ffd5",
|
||||
"secondary": "#ff00aa",
|
||||
"accent": "#ffee00",
|
||||
"surface": "#000000",
|
||||
"text": "#00ffd5",
|
||||
"success": "#00ff88",
|
||||
"warning": "#ffee00",
|
||||
"danger": "#ff0066",
|
||||
"fontFamily": "Courier New, monospace",
|
||||
"radii": { "sm": "0", "md": "0", "lg": "0" },
|
||||
"spacingScale": [4, 8, 12, 16, 24, 32, 48]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "paper",
|
||||
"name": "Paper",
|
||||
"tokens": {
|
||||
"primary": "#1f2937",
|
||||
"secondary": "#6b7280",
|
||||
"accent": "#0ea5e9",
|
||||
"surface": "#fafaf9",
|
||||
"text": "#1c1917",
|
||||
"success": "#059669",
|
||||
"warning": "#d97706",
|
||||
"danger": "#b91c1c",
|
||||
"fontFamily": "Georgia, serif",
|
||||
"radii": { "sm": "2px", "md": "4px", "lg": "8px" },
|
||||
"spacingScale": [4, 8, 12, 16, 24, 32, 48]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "crimson",
|
||||
"name": "Crimson",
|
||||
"tokens": {
|
||||
"primary": "#dc2626",
|
||||
"secondary": "#991b1b",
|
||||
"accent": "#fbbf24",
|
||||
"surface": "#1f1f1f",
|
||||
"text": "#fef2f2",
|
||||
"success": "#22c55e",
|
||||
"warning": "#fbbf24",
|
||||
"danger": "#ef4444",
|
||||
"fontFamily": "system-ui, sans-serif",
|
||||
"radii": { "sm": "4px", "md": "8px", "lg": "12px" },
|
||||
"spacingScale": [4, 8, 12, 16, 24, 32, 48]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "ocean",
|
||||
"name": "Ocean",
|
||||
"tokens": {
|
||||
"primary": "#0ea5e9",
|
||||
"secondary": "#06b6d4",
|
||||
"accent": "#14b8a6",
|
||||
"surface": "#f0f9ff",
|
||||
"text": "#0c4a6e",
|
||||
"success": "#10b981",
|
||||
"warning": "#f59e0b",
|
||||
"danger": "#ef4444",
|
||||
"fontFamily": "system-ui, sans-serif",
|
||||
"radii": { "sm": "8px", "md": "16px", "lg": "24px" },
|
||||
"spacingScale": [4, 8, 12, 16, 24, 32, 48]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "neon",
|
||||
"name": "Neon",
|
||||
"tokens": {
|
||||
"primary": "#a855f7",
|
||||
"secondary": "#ec4899",
|
||||
"accent": "#22d3ee",
|
||||
"surface": "#0a0a0a",
|
||||
"text": "#f5f3ff",
|
||||
"success": "#4ade80",
|
||||
"warning": "#fbbf24",
|
||||
"danger": "#fb7185",
|
||||
"fontFamily": "system-ui, sans-serif",
|
||||
"radii": { "sm": "4px", "md": "12px", "lg": "20px" },
|
||||
"spacingScale": [4, 8, 12, 16, 24, 32, 48]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "monochrome",
|
||||
"name": "Monochrome",
|
||||
"tokens": {
|
||||
"primary": "#000000",
|
||||
"secondary": "#525252",
|
||||
"accent": "#a3a3a3",
|
||||
"surface": "#ffffff",
|
||||
"text": "#0a0a0a",
|
||||
"success": "#404040",
|
||||
"warning": "#737373",
|
||||
"danger": "#171717",
|
||||
"fontFamily": "system-ui, sans-serif",
|
||||
"radii": { "sm": "0", "md": "0", "lg": "0" },
|
||||
"spacingScale": [4, 8, 12, 16, 24, 32, 48]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user