feat: Admin Area Challenges: List, Import/Export and Add/Edit Modal

This commit is contained in:
OpenVelo Agent
2026-07-22 20:26:27 +00:00
parent 6c18603a7d
commit 216795b5db
33 changed files with 4256 additions and 105 deletions
@@ -1,4 +1,12 @@
import { Entity, PrimaryColumn, Column, ManyToOne, JoinColumn, Index } from 'typeorm';
import {
Entity,
PrimaryColumn,
Column,
CreateDateColumn,
ManyToOne,
JoinColumn,
Index,
} from 'typeorm';
import { ChallengeEntity } from './challenge.entity';
@Entity('challenge_file')
@@ -10,13 +18,23 @@ export class ChallengeFileEntity {
@Column('text', { name: 'challenge_id' })
challengeId!: string;
@ManyToOne(() => ChallengeEntity)
@ManyToOne(() => ChallengeEntity, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'challenge_id' })
challenge?: ChallengeEntity;
@Column('text', { name: 'original_filename' })
originalFilename!: string;
@Column('text', { name: 'stored_path' })
storedPath!: string;
@Index({ unique: true })
@Column('text', { name: 'stored_filename' })
storedFilename!: string;
@Column('text', { name: 'mime_type', default: 'application/octet-stream' })
mimeType!: string;
@Column('integer', { name: 'size_bytes', default: 0 })
sizeBytes!: number;
@CreateDateColumn({ type: 'text', name: 'created_at' })
createdAt!: string;
}
@@ -1,8 +1,17 @@
import { Entity, PrimaryColumn, Column, CreateDateColumn, ManyToOne, JoinColumn, Index } from 'typeorm';
import {
Entity,
PrimaryColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
ManyToOne,
JoinColumn,
Index,
} from 'typeorm';
import { CategoryEntity } from './category.entity';
export type Difficulty = 'low' | 'med' | 'high';
export type Protocol = 'nc' | 'web';
export type Difficulty = 'LOW' | 'MEDIUM' | 'HIGH';
export type Protocol = 'NC' | 'WEB';
@Entity('challenge')
export class ChallengeEntity {
@@ -23,22 +32,22 @@ export class ChallengeEntity {
@JoinColumn({ name: 'category_id' })
category?: CategoryEntity;
@Column('text', { default: 'low' })
@Column('text', { default: 'MEDIUM' })
difficulty!: Difficulty;
@Column('integer', { name: 'initial_points', default: 0 })
@Column('integer', { name: 'initial_points', default: 100 })
initialPoints!: number;
@Column('integer', { name: 'minimum_points', default: 0 })
@Column('integer', { name: 'minimum_points', default: 50 })
minimumPoints!: number;
@Column('integer', { name: 'decay_solves', default: 0 })
@Column('integer', { name: 'decay_solves', default: 10 })
decaySolves!: number;
@Column('text', { default: '' })
flag!: string;
@Column('text', { default: 'nc' })
@Column('text', { default: 'WEB' })
protocol!: Protocol;
@Column('integer', { nullable: true })
@@ -47,6 +56,12 @@ export class ChallengeEntity {
@Column('text', { name: 'ip_address', default: '' })
ipAddress!: string;
@Column('integer', { default: 1 })
enabled!: boolean;
@CreateDateColumn({ type: 'text', name: 'created_at' })
createdAt!: string;
@UpdateDateColumn({ type: 'text', name: 'updated_at' })
updatedAt!: string;
}
+23 -1
View File
@@ -1,4 +1,13 @@
import { Entity, PrimaryColumn, Column, Index, Unique } from 'typeorm';
import {
Entity,
PrimaryColumn,
Column,
Index,
Unique,
ManyToOne,
JoinColumn,
} from 'typeorm';
import { ChallengeEntity } from './challenge.entity';
@Entity('solve')
@Unique('uq_solve_challenge_user', ['challengeId', 'userId'])
@@ -11,6 +20,10 @@ export class SolveEntity {
@Column('text', { name: 'challenge_id' })
challengeId!: string;
@ManyToOne(() => ChallengeEntity, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'challenge_id' })
challenge?: ChallengeEntity;
@Column('text', { name: 'user_id' })
userId!: string;
@@ -25,4 +38,13 @@ export class SolveEntity {
@Column('integer', { name: 'rank_bonus', default: 0 })
rankBonus!: number;
@Column('integer', { name: 'is_first', default: 0 })
isFirst!: boolean;
@Column('integer', { name: 'is_second', default: 0 })
isSecond!: boolean;
@Column('integer', { name: 'is_third', default: 0 })
isThird!: boolean;
}