AI Implementation feature(820): Project Scaffold and Platform Foundations (#1)

This commit was merged in pull request #1.
This commit is contained in:
2026-07-21 14:23:53 +00:00
parent e55c9cda56
commit 03bcb6b156
131 changed files with 23657 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
import { z } from 'zod';
export const envSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
PORT: z.coerce.number().int().positive().default(3000),
DATABASE_PATH: z.string().min(1).default('/data/hipctf/db.sqlite'),
UPLOAD_DIR: z.string().min(1).default('/data/hipctf/uploads'),
THEMES_DIR: z.string().min(1).default('./themes'),
FRONTEND_DIST: z.string().min(1).default('../frontend/dist'),
JWT_ACCESS_SECRET: z.string().min(32).default('dev-access-secret-change-me-please-32chars'),
JWT_REFRESH_SECRET: z.string().min(32).default('dev-refresh-secret-change-me-please-32ch'),
CSRF_SECRET: z.string().min(32).default('dev-csrf-secret-change-me-please-32chars!!'),
JWT_ACCESS_TTL: z.string().default('15m'),
JWT_REFRESH_TTL: z.string().default('7d'),
ARGON2_MEMORY_COST: z.coerce.number().int().positive().default(65536),
ARGON2_TIME_COST: z.coerce.number().int().positive().default(3),
ARGON2_PARALLELISM: z.coerce.number().int().positive().default(1),
PASSWORD_MIN_LENGTH: z.coerce.number().int().positive().default(12),
PASSWORD_REQUIRE_MIXED: z.coerce.boolean().default(true),
CORS_ORIGINS: z.string().default('http://localhost:4200,http://localhost:3000'),
BODY_SIZE_LIMIT: z.string().default('1mb'),
UPLOAD_SIZE_LIMIT: z.string().default('50mb'),
TLS_ENABLED: z.coerce.boolean().default(false),
});
export type AppEnv = z.infer<typeof envSchema>;
export function validateEnv(raw: Record<string, unknown>): AppEnv {
const parsed = envSchema.safeParse(raw);
if (!parsed.success) {
const issues = parsed.error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join('; ');
throw new Error(`Invalid environment configuration: ${issues}`);
}
return parsed.data;
}
export const SETTINGS_KEYS = {
PAGE_TITLE: 'pageTitle',
LOGO: 'logo',
WELCOME_MARKDOWN: 'welcomeMarkdown',
THEME_KEY: 'themeKey',
DEFAULT_CHALLENGE_IP: 'defaultChallengeIp',
REGISTRATIONS_ENABLED: 'registrationsEnabled',
EVENT_START_UTC: 'eventStartUtc',
EVENT_END_UTC: 'eventEndUtc',
} as const;
export const DEFAULT_SETTINGS: Record<string, string> = {
[SETTINGS_KEYS.PAGE_TITLE]: 'HIPCTF',
[SETTINGS_KEYS.LOGO]: '',
[SETTINGS_KEYS.WELCOME_MARKDOWN]: '# Welcome\n\nCreate the first admin to get started.',
[SETTINGS_KEYS.THEME_KEY]: 'classic',
[SETTINGS_KEYS.DEFAULT_CHALLENGE_IP]: '127.0.0.1',
[SETTINGS_KEYS.REGISTRATIONS_ENABLED]: 'false',
[SETTINGS_KEYS.EVENT_START_UTC]: new Date(Date.now() + 60_000).toISOString(),
[SETTINGS_KEYS.EVENT_END_UTC]: new Date(Date.now() + 7 * 24 * 60 * 60_000).toISOString(),
};
export const SYSTEM_CATEGORY_KEYS = ['crypto', 'forensics', 'pwn', 'web', 'misc', 'osint'] as const;
export type SystemCategoryKey = (typeof SYSTEM_CATEGORY_KEYS)[number];
export const SYSTEM_CATEGORY_META: Record<SystemCategoryKey, { name: string; abbreviation: string; description: string; iconPath: string }> = {
crypto: { name: 'Cryptography', abbreviation: 'CRY', description: 'Crypto challenges', iconPath: '/uploads/icons/crypto.svg' },
forensics: { name: 'Forensics', abbreviation: 'FOR', description: 'Forensics challenges', iconPath: '/uploads/icons/forensics.svg' },
pwn: { name: 'Pwn', abbreviation: 'PWN', description: 'Binary exploitation', iconPath: '/uploads/icons/pwn.svg' },
web: { name: 'Web', abbreviation: 'WEB', description: 'Web exploitation', iconPath: '/uploads/icons/web.svg' },
misc: { name: 'Misc', abbreviation: 'MSC', description: 'Miscellaneous challenges', iconPath: '/uploads/icons/misc.svg' },
osint: { name: 'OSINT', abbreviation: 'OSI', description: 'Open-source intelligence', iconPath: '/uploads/icons/osint.svg' },
};