import React, { useState, useEffect, createContext, useContext, useMemo, useCallback } from 'react'; import { HashRouter, Routes, Route, Link, useNavigate, Navigate, useLocation } from 'react-router-dom'; import { LogIn, UserPlus, Trophy, Flag, Shield, LogOut, Settings, Clock, Plus, Trash2, Edit3, Download, ExternalLink, Menu, X, CheckCircle2, ChevronRight, Layers, Database, RefreshCw, Terminal, Crosshair, Radar, Zap, Box, User, Users, ShieldAlert, ShieldCheck, Heart, Sparkles, Coffee, Table, Paperclip, Newspaper, Image as ImageIcon, Type, Layout, Wand2, Monitor, Globe } from 'lucide-react'; import { Challenge, Team, Solve, CTFState, Difficulty, ChallengeFile, BlogPost } from './types'; import { CATEGORIES, DIFFICULTIES } from './constants'; import { calculateChallengeValue, calculateTeamTotalScore, getFirstBloodBonus } from './services/scoring'; import { api } from './services/api'; // --- Context & State Management --- interface CTFContextType { state: CTFState; currentUser: Team | null; login: (name: string, pass: string) => Promise; register: (name: string, pass: string) => Promise; logout: () => void; submitFlag: (challengeId: string, flag: string) => Promise; toggleCtf: () => Promise; upsertChallenge: (data: FormData, id?: string) => Promise; deleteChallenge: (id: string) => Promise; updateTeam: (id: string, name: string, isDisabled: boolean, isAdmin: boolean, password?: string) => Promise; updateProfile: (password?: string) => Promise; deleteTeam: (id: string) => Promise; createBlogPost: (data: { title: string, content: string }) => Promise; updateBlogPost: (id: string, data: { title: string, content: string }) => Promise; deleteBlogPost: (id: string) => Promise; updateConfig: (formData: FormData) => Promise; refreshState: () => Promise; } const CTFContext = createContext(null); const useCTF = () => { const context = useContext(CTFContext); if (!context) throw new Error('useCTF must be used within provider'); return context; }; // --- Helper Components --- const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => { const { currentUser } = useCTF(); const location = useLocation(); if (!currentUser) { return ; } return <>{children}; }; const CategoryIcon: React.FC<{ category: string; size?: number; color?: string }> = ({ category, size = 32, color = "currentColor" }) => { switch (category) { case 'WEB': return ; case 'PWN': return ; case 'REV': return ; case 'CRY': return ; default: return ; } }; const getDifficultyColorClass = (difficulty: Difficulty) => { switch (difficulty) { case 'Low': return 'text-green-500'; case 'Medium': return 'text-yellow-500'; case 'High': return 'text-red-500'; default: return 'text-slate-400'; } }; const Button: React.FC & { variant?: 'primary' | 'secondary' }> = ({ children, variant = 'primary', className = "", ...props }) => { const styles = variant === 'primary' ? "bg-[#ff0000] text-black border-2 border-[#ff0000] hover:bg-black hover:text-[#ff0000] disabled:opacity-50" : "bg-black text-[#bf00ff] border-2 border-[#bf00ff] hover:bg-[#bf00ff] hover:text-black disabled:opacity-50"; return ( ); }; const ChallengeModal: React.FC<{ challenge: Challenge; onClose: () => void; }> = ({ challenge, onClose }) => { const { state, currentUser, submitFlag, refreshState } = useCTF(); const [flagInput, setFlagInput] = useState(''); const [message, setMessage] = useState<{ text: string, type: 'success' | 'error' } | null>(null); const handleFlagSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { const result = await submitFlag(challenge.id, flagInput); if (result) { setMessage({ text: 'ACCESS GRANTED ✨', type: 'success' }); setFlagInput(''); setTimeout(() => { onClose(); setMessage(null); refreshState(); }, 1500); } else { setMessage({ text: 'RETRY SUGGESTED', type: 'error' }); } } catch (err) { setMessage({ text: 'COMMUNICATION ERROR', type: 'error' }); } }; const connectionDetails = useMemo(() => { const port = Number(challenge.port); const ip = state.config.dockerIp; const type = challenge.connectionType || 'nc'; if (port > 0) { if (!ip) { return currentUser?.isAdmin ? "ERROR: DOCKER_NODE_IP_NOT_CONFIGURED" : null; } return type === 'nc' ? `nc ${ip} ${port}` : `http://${ip}:${port}`; } return null; }, [challenge.port, challenge.connectionType, state.config.dockerIp, currentUser?.isAdmin]); return (

{challenge.title}

{challenge.category} | {challenge.difficulty} | {calculateChallengeValue(challenge.initialPoints, challenge.solves.length)} PTS
{challenge.description}
{connectionDetails && (

Connect to:

{connectionDetails} {connectionDetails.startsWith("ERROR") && (

ADMIN_NOTICE: Set Docker Node IP in General Config

)}
)} {challenge.files && challenge.files.length > 0 && (

Available Files

{challenge.files.map((file, idx) => ( {file.name} ))}
)} {currentUser ? ( !challenge.solves.includes(currentUser.id) ? (
setFlagInput(e.target.value)} autoFocus />
{message &&
{message.text}
}
) : (
CHALLENGE SOLVED ✨
) ) : (

PLEASE SIGN IN TO CONTRIBUTE

)}

SOLVER_LOG

{challenge.solves.length > 0 ? ( state.solves.filter(s => s.challengeId === challenge.id).sort((a, b) => a.timestamp - b.timestamp).map((solve, idx) => { const team = state.teams.find(t => t.id === solve.teamId); const bonus = getFirstBloodBonus(idx); return (
{idx + 1} {team?.name} {bonus > 0 && PWNY POWER 🦄 +{Math.round(bonus*100)}%}
{new Date(solve.timestamp).toLocaleTimeString()}
); }) ) : (
No solutions discovered yet.
)}
); }; const ProfileSettingsModal: React.FC<{ onClose: () => void }> = ({ onClose }) => { const { updateProfile } = useCTF(); const [password, setPassword] = useState(''); const [confirm, setConfirm] = useState(''); const [error, setError] = useState(''); const [success, setSuccess] = useState(false); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (password !== confirm) return setError('AUTH_MISMATCH: PASSWORDS_DO_NOT_MATCH'); try { await updateProfile(password); setSuccess(true); setTimeout(onClose, 2000); } catch (err: any) { setError(err.message || 'UPDATE_FAILED'); } }; return (

IDENTITY_SETTINGS

{success ? (
CREDENTIALS_UPDATED_SUCCESSFULLY
) : (
setPassword(e.target.value)} required />
setConfirm(e.target.value)} required />
{error &&

{error}

}
)}
); }; // --- Pages --- const Blog: React.FC = () => { const { state } = useCTF(); return (

BLOG_FEED

Protocol: Global Broadcasts & Updates

{state.blogs.length > 0 ? ( state.blogs.map((post) => (
{new Date(post.timestamp).toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' })}

{post.title}

{post.content}
)) ) : (

Awaiting broadcasts from headquarters...

)}
); }; const Home: React.FC = () => { const { currentUser, state } = useCTF(); return (
{/* Logo or fallback generic icon - Replaced text with logo as requested */}
{state.config.logoUrl ? ( Logo ) : ( )}

{state.config.conferenceName}

{state.config.landingText}

{!currentUser ? ( <> ) : (
)}
); }; const ChallengeList: React.FC = () => { const { state, currentUser } = useCTF(); const [selectedChallenge, setSelectedChallenge] = useState(null); const difficultyWeight: Record = { 'Low': 1, 'Medium': 2, 'High': 3 }; // Admins can see challenges even if competition is not started/paused if (!state.isStarted && !currentUser?.isAdmin) { return (

BOARD_PAUSED

Waiting for the opening ceremony...

); } return (
{!state.isStarted && currentUser?.isAdmin && (
Competition is currently paused. Preview mode active for Admins.
)}
{CATEGORIES.map(category => { const categoryChallenges = (state.challenges || []) .filter(c => c.category === category) .sort((a, b) => difficultyWeight[a.difficulty] - difficultyWeight[b.difficulty]); if (categoryChallenges.length === 0) return null; return (

{category}

{categoryChallenges.map(c => { const isSolved = currentUser && (c.solves || []).includes(currentUser.id); const currentPoints = calculateChallengeValue(c.initialPoints, (c.solves || []).length); return (
setSelectedChallenge(c)} >
{c.difficulty}

{c.title}

{isSolved && } {currentPoints}
{(c.solves || []).length}
); })}
); })}
{selectedChallenge && ( setSelectedChallenge(null)} /> )}
); }; const Scoreboard: React.FC = () => { const { state } = useCTF(); const rankings = useMemo(() => { return (state.teams || []) .filter(t => !t.isAdmin && !t.isDisabled) .map(team => ({ ...team, score: calculateTeamTotalScore(team.id, state.challenges, state.solves), solveCount: state.solves.filter(s => s.teamId === team.id).length })) .sort((a, b) => b.score - a.score || a.name.localeCompare(b.name)); }, [state]); return (

LEADERBOARD

Protocol: Global Standings

View_Matrix
{rankings.map((team, idx) => ( ))}
RANK TEAM_IDENTIFIER SOLVES TOTAL_POINTS
{idx + 1}
{team.name} {idx === 0 && }
{team.solveCount} {team.score}
{rankings.length === 0 && (
No team activity detected yet.
)}
); }; const ScoreMatrix: React.FC = () => { const { state } = useCTF(); const sortedTeams = useMemo(() => { return state.teams .filter(t => !t.isAdmin && !t.isDisabled) .map(t => ({ ...t, score: calculateTeamTotalScore(t.id, state.challenges, state.solves) })) .sort((a, b) => b.score - a.score); }, [state]); return (

SCORE_MATRIX

Protocol: Operational Overview

{state.challenges.map(c => ( ))} {sortedTeams.map(team => ( {state.challenges.map(c => { const isSolved = c.solves.includes(team.id); return ( ); })} ))}
Team / Challenge
{c.title}
{team.name}
{isSolved && }
); }; const Login: React.FC = () => { const [name, setName] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); const { login } = useCTF(); const navigate = useNavigate(); const location = useLocation(); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); const success = await login(name, password); if (success) { const from = (location.state as any)?.from?.pathname || '/'; navigate(from, { replace: true }); } else { setError('ACCESS_DENIED: INVALID_CREDENTIALS'); } }; return (
AUTH_GATE_01

SIGN_IN

setName(e.target.value)} required />
setPassword(e.target.value)} required />
{error &&

{error}

}

New operator? Register_Identity

); }; const Register: React.FC = () => { const [name, setName] = useState(''); const [password, setPassword] = useState(''); const [confirm, setConfirm] = useState(''); const [userCaptcha, setUserCaptcha] = useState(''); const [captchaValue, setCaptchaValue] = useState(''); const [error, setError] = useState(''); const { register } = useCTF(); const navigate = useNavigate(); const generateCaptcha = useCallback(() => { const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; let result = ''; for (let i = 0; i < 6; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } setCaptchaValue(result); }, []); useEffect(() => { generateCaptcha(); }, [generateCaptcha]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (password !== confirm) return setError('AUTH_MISMATCH: PASSWORDS_DO_NOT_MATCH'); if (userCaptcha.toUpperCase() !== captchaValue) { setError('AUTH_FAILURE: INVALID_CAPTCHA'); generateCaptcha(); setUserCaptcha(''); return; } try { await register(name, password); navigate('/challenges'); } catch (err: any) { setError(err.message || 'REGISTRATION_FAILED'); generateCaptcha(); } }; return (
IDENTITY_GEN_01

REGISTER

setName(e.target.value)} required />
setPassword(e.target.value)} required />
setConfirm(e.target.value)} required />
{captchaValue}
setUserCaptcha(e.target.value)} required />
{error &&

{error}

}
); }; const Admin: React.FC = () => { const { state, toggleCtf, upsertChallenge, deleteChallenge, updateTeam, deleteTeam, createBlogPost, updateBlogPost, deleteBlogPost, updateConfig, refreshState } = useCTF(); const [editingChallenge, setEditingChallenge] = useState | null>(null); const [newFiles, setNewFiles] = useState([]); const [currentFiles, setCurrentFiles] = useState([]); const [editingTeam, setEditingTeam] = useState & { newPassword?: string } | null>(null); const [editingBlogPost, setEditingBlogPost] = useState | null>(null); // General Config State const [confName, setConfName] = useState(state.config.conferenceName || ''); const [confLanding, setConfLanding] = useState(state.config.landingText || ''); const [confBgType, setConfBgType] = useState(state.config.bgType || 'color'); const [confBgColor, setConfBgColor] = useState(state.config.bgColor || '#000000'); const [confBgOpacity, setConfBgOpacity] = useState(state.config.bgOpacity || '0.5'); const [confBgBrightness, setConfBgBrightness] = useState(state.config.bgBrightness || '1.0'); const [confBgContrast, setConfBgContrast] = useState(state.config.bgContrast || '1.0'); const [confDockerIp, setConfDockerIp] = useState(state.config.dockerIp || ''); const [newLogo, setNewLogo] = useState(null); const [newBgImage, setNewBgImage] = useState(null); useEffect(() => { setConfName(state.config.conferenceName || ''); setConfLanding(state.config.landingText || ''); setConfBgType(state.config.bgType || 'color'); setConfBgColor(state.config.bgColor || '#000000'); setConfBgOpacity(state.config.bgOpacity || '0.5'); setConfBgBrightness(state.config.bgBrightness || '1.0'); setConfBgContrast(state.config.bgContrast || '1.0'); setConfDockerIp(state.config.dockerIp || ''); }, [state.config]); const handleConfigSubmit = async (e: React.FormEvent) => { e.preventDefault(); const fd = new FormData(); fd.append('conferenceName', confName); fd.append('landingText', confLanding); fd.append('bgType', confBgType); fd.append('bgColor', confBgColor); fd.append('bgOpacity', confBgOpacity); fd.append('bgBrightness', confBgBrightness); fd.append('bgContrast', confBgContrast); fd.append('dockerIp', confDockerIp); if (newLogo) fd.append('logo', newLogo); if (newBgImage) fd.append('bgImage', newBgImage); try { await updateConfig(fd); alert('CONFIGURATION_UPDATED'); } catch (err) { alert('CONFIG_UPDATE_FAILED'); } }; const handleChallengeSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!editingChallenge) return; try { const formData = new FormData(); Object.entries(editingChallenge).forEach(([key, value]) => { if (key !== 'files' && key !== 'solves' && value !== undefined && value !== null) { formData.append(key, value.toString()); } }); formData.append('existingFiles', JSON.stringify(currentFiles)); newFiles.forEach(file => { formData.append('files', file); }); await upsertChallenge(formData, editingChallenge.id); setEditingChallenge(null); setNewFiles([]); } catch (err: any) { alert("Failed to update challenge."); } }; const handleTeamUpdate = async (e: React.FormEvent) => { e.preventDefault(); if (!editingTeam || !editingTeam.id) return; await updateTeam(editingTeam.id, editingTeam.name || '', !!editingTeam.isDisabled, !!editingTeam.isAdmin, editingTeam.newPassword); setEditingTeam(null); }; const handleBlogSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (!editingBlogPost) return; try { if (editingBlogPost.id) { await updateBlogPost(editingBlogPost.id, { title: editingBlogPost.title || '', content: editingBlogPost.content || '' }); } else { await createBlogPost({ title: editingBlogPost.title || '', content: editingBlogPost.content || '' }); } setEditingBlogPost(null); } catch (err) { alert('BLOG_SAVE_FAILED'); } }; const removeExistingFile = (idx: number) => { setCurrentFiles(prev => prev.filter((_, i) => i !== idx)); }; return (

ADMIN_OVERRIDE

{/* General Settings */}

GENERAL_CONFIG

setConfName(e.target.value)} />
setConfDockerIp(e.target.value)} />

Stored in DB: {state.config.dockerIp || 'MISSING'}