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
@@ -0,0 +1,28 @@
import { Entity, PrimaryColumn, Column, CreateDateColumn, UpdateDateColumn, Index } from 'typeorm';
export type BlogStatus = 'draft' | 'published';
@Entity('blog_post')
export class BlogPostEntity {
@PrimaryColumn('text')
id!: string;
@Column('text')
title!: string;
@Column('text', { name: 'body_md', default: '' })
bodyMd!: string;
@Index('idx_blog_status_published', ['status', 'publishedAt'])
@Column('text', { default: 'draft' })
status!: BlogStatus;
@CreateDateColumn({ type: 'text', name: 'created_at' })
createdAt!: string;
@UpdateDateColumn({ type: 'text', name: 'updated_at' })
updatedAt!: string;
@Column('text', { name: 'published_at', nullable: true })
publishedAt!: string | null;
}
@@ -0,0 +1,23 @@
import { Entity, PrimaryColumn, Column, Index } from 'typeorm';
@Entity('category')
export class CategoryEntity {
@PrimaryColumn('text')
id!: string;
@Index({ unique: true, where: 'system_key IS NOT NULL' })
@Column('text', { name: 'system_key', nullable: true })
systemKey!: string | null;
@Column('text')
name!: string;
@Column('text')
abbreviation!: string;
@Column('text', { default: '' })
description!: string;
@Column('text', { name: 'icon_path', default: '' })
iconPath!: string;
}
@@ -0,0 +1,22 @@
import { Entity, PrimaryColumn, Column, ManyToOne, JoinColumn, Index } from 'typeorm';
import { ChallengeEntity } from './challenge.entity';
@Entity('challenge_file')
export class ChallengeFileEntity {
@PrimaryColumn('text')
id!: string;
@Index()
@Column('text', { name: 'challenge_id' })
challengeId!: string;
@ManyToOne(() => ChallengeEntity)
@JoinColumn({ name: 'challenge_id' })
challenge?: ChallengeEntity;
@Column('text', { name: 'original_filename' })
originalFilename!: string;
@Column('text', { name: 'stored_path' })
storedPath!: string;
}
@@ -0,0 +1,52 @@
import { Entity, PrimaryColumn, Column, CreateDateColumn, ManyToOne, JoinColumn, Index } from 'typeorm';
import { CategoryEntity } from './category.entity';
export type Difficulty = 'low' | 'med' | 'high';
export type Protocol = 'nc' | 'web';
@Entity('challenge')
export class ChallengeEntity {
@PrimaryColumn('text')
id!: string;
@Column('text')
name!: string;
@Column('text', { name: 'description_md', default: '' })
descriptionMd!: string;
@Index()
@Column('text', { name: 'category_id' })
categoryId!: string;
@ManyToOne(() => CategoryEntity)
@JoinColumn({ name: 'category_id' })
category?: CategoryEntity;
@Column('text', { default: 'low' })
difficulty!: Difficulty;
@Column('integer', { name: 'initial_points', default: 0 })
initialPoints!: number;
@Column('integer', { name: 'minimum_points', default: 0 })
minimumPoints!: number;
@Column('integer', { name: 'decay_solves', default: 0 })
decaySolves!: number;
@Column('text', { default: '' })
flag!: string;
@Column('text', { default: 'nc' })
protocol!: Protocol;
@Column('integer', { nullable: true })
port!: number | null;
@Column('text', { name: 'ip_address', default: '' })
ipAddress!: string;
@CreateDateColumn({ type: 'text', name: 'created_at' })
createdAt!: string;
}
@@ -0,0 +1,23 @@
import { Entity, PrimaryColumn, Column, Index } from 'typeorm';
@Entity('refresh_token')
export class RefreshTokenEntity {
@PrimaryColumn('text')
id!: string;
@Index()
@Column('text', { name: 'user_id' })
userId!: string;
@Column('text', { name: 'token_hash', unique: true })
tokenHash!: string;
@Column('text', { name: 'issued_at' })
issuedAt!: string;
@Column('text', { name: 'expires_at' })
expiresAt!: string;
@Column('text', { name: 'revoked_at', nullable: true })
revokedAt!: string | null;
}
@@ -0,0 +1,10 @@
import { Entity, PrimaryColumn, Column } from 'typeorm';
@Entity('setting')
export class SettingEntity {
@PrimaryColumn('text')
key!: string;
@Column('text', { default: '' })
value!: string;
}
@@ -0,0 +1,28 @@
import { Entity, PrimaryColumn, Column, Index, Unique } from 'typeorm';
@Entity('solve')
@Unique('uq_solve_challenge_user', ['challengeId', 'userId'])
@Index('idx_solve_user', ['userId'])
@Index('idx_solve_challenge', ['challengeId'])
export class SolveEntity {
@PrimaryColumn('text')
id!: string;
@Column('text', { name: 'challenge_id' })
challengeId!: string;
@Column('text', { name: 'user_id' })
userId!: string;
@Column('text', { name: 'solved_at' })
solvedAt!: string;
@Column('integer', { name: 'points_awarded', default: 0 })
pointsAwarded!: number;
@Column('integer', { name: 'base_points', default: 0 })
basePoints!: number;
@Column('integer', { name: 'rank_bonus', default: 0 })
rankBonus!: number;
}
@@ -0,0 +1,25 @@
import { Entity, PrimaryColumn, Column, CreateDateColumn } from 'typeorm';
export type UserRole = 'admin' | 'player';
export type UserStatus = 'enabled' | 'disabled';
@Entity('user')
export class UserEntity {
@PrimaryColumn('text')
id!: string;
@Column('text', { unique: true })
username!: string;
@Column('text', { name: 'password_hash' })
passwordHash!: string;
@Column('text', { default: 'player' })
role!: UserRole;
@Column('text', { default: 'enabled' })
status!: UserStatus;
@CreateDateColumn({ type: 'text', name: 'created_at' })
createdAt!: string;
}