117 lines
6.0 KiB
TypeScript
117 lines
6.0 KiB
TypeScript
import React, { createContext, useContext, useState, useEffect, useCallback, useRef } from 'react';
|
|
import { Challenge, Team, Solve, CTFState, BlogPost } from './types';
|
|
import { api } from './services/api';
|
|
|
|
interface CTFContextType {
|
|
state: CTFState;
|
|
currentUser: Team | null;
|
|
login: (name: string, pass: string) => Promise<boolean>;
|
|
register: (name: string, pass: string) => Promise<void>;
|
|
logout: () => void;
|
|
submitFlag: (challengeId: string, flag: string) => Promise<boolean>;
|
|
toggleCtf: () => Promise<void>;
|
|
resetScores: () => Promise<void>;
|
|
upsertChallenge: (data: FormData, id?: string) => Promise<void>;
|
|
deleteChallenge: (id: string) => Promise<void>;
|
|
deleteAllChallenges: () => Promise<void>;
|
|
exportChallenges: () => Promise<{ challenges: any[] }>;
|
|
importChallenges: (file: File) => Promise<void>;
|
|
backupDatabase: () => Promise<any>;
|
|
restoreDatabase: (file: File) => Promise<void>;
|
|
updateTeam: (id: string, data: any) => Promise<void>;
|
|
updateProfile: (password?: string) => Promise<void>;
|
|
deleteTeam: (id: string) => Promise<void>;
|
|
createBlogPost: (data: { title: string, content: string }) => Promise<void>;
|
|
updateBlogPost: (id: string, data: { title: string, content: string }) => Promise<void>;
|
|
deleteBlogPost: (id: string) => Promise<void>;
|
|
updateConfig: (formData: FormData) => Promise<void>;
|
|
refreshState: () => Promise<void>;
|
|
loading: boolean;
|
|
loadError: string | null;
|
|
}
|
|
|
|
const CTFContext = createContext<CTFContextType | null>(null);
|
|
|
|
export const useCTF = () => {
|
|
const context = useContext(CTFContext);
|
|
if (!context) throw new Error('useCTF must be used within provider');
|
|
return context;
|
|
};
|
|
|
|
export const CTFProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
const [state, setState] = useState<CTFState>({ isStarted: false, startTime: null, teams: [], challenges: [], solves: [], blogs: [], config: {} });
|
|
const [currentUser, setCurrentUser] = useState<Team | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [loadError, setLoadError] = useState<string | null>(null);
|
|
|
|
// Fix: Removed return value to match refreshState: () => Promise<void> interface
|
|
const refreshState = useCallback(async () => {
|
|
try {
|
|
const newState = await api.getState();
|
|
setState(newState);
|
|
} catch (err: any) {
|
|
console.error("State refresh failed:", err);
|
|
throw err;
|
|
}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const init = async () => {
|
|
const session = localStorage.getItem('hip6_session');
|
|
if (session) { try { const { team } = JSON.parse(session); setCurrentUser(team); } catch (e) {} }
|
|
|
|
const safetyTimeout = setTimeout(() => {
|
|
setLoading(false);
|
|
setLoadError("CONNECTION_TIMED_OUT: RETRYING...");
|
|
}, 6000);
|
|
|
|
try {
|
|
await refreshState();
|
|
setLoadError(null);
|
|
} catch (err: any) {
|
|
setLoadError("COMMUNICATION_FAULT: RECONNECTING...");
|
|
} finally {
|
|
clearTimeout(safetyTimeout);
|
|
setLoading(false);
|
|
}
|
|
};
|
|
init();
|
|
const interval = setInterval(() => {
|
|
refreshState().catch(() => {});
|
|
}, 30000);
|
|
return () => clearInterval(interval);
|
|
}, [refreshState]);
|
|
|
|
const login = async (n: string, p: string) => { try { const { team, token } = await api.login(n, p); localStorage.setItem('hip6_session', JSON.stringify({ team, token })); setCurrentUser(team); await refreshState(); return true; } catch (e) { return false; } };
|
|
const register = async (n: string, p: string) => { const { team, token } = await api.register(n, p); localStorage.setItem('hip6_session', JSON.stringify({ team, token })); setCurrentUser(team); await refreshState(); };
|
|
const logout = () => { localStorage.removeItem('hip6_session'); setCurrentUser(null); };
|
|
const submitFlag = async (cid: string, f: string) => { const res = await api.submitFlag(cid, f); await refreshState(); return res.success; };
|
|
const toggleCtf = async () => { await api.toggleCtf(); await refreshState(); };
|
|
const resetScores = async () => { if (window.confirm("Reset all scores to 0?")) { await api.resetScores(); await refreshState(); }};
|
|
const upsertChallenge = async (d: FormData, id?: string) => { await api.upsertChallenge(d, id); await refreshState(); };
|
|
const deleteChallenge = async (id: string) => { if (window.confirm("DELETE_CHALLENGE?")) { await api.deleteChallenge(id); await refreshState(); } };
|
|
const deleteAllChallenges = async () => { if (window.confirm("Delete all challenges?")) {await api.deleteAllChallenges(); await refreshState(); }};
|
|
const exportChallenges = async () => await api.exportChallenges();
|
|
const importChallenges = async (f: File) => { await api.importChallenges(f); await refreshState(); };
|
|
const backupDatabase = async () => await api.backupDatabase();
|
|
const restoreDatabase = async (f: File) => await api.restoreDatabase(f);
|
|
const updateTeam = async (id: string, d: any) => { await api.updateTeam(id, d); await refreshState(); };
|
|
const updateProfile = async (p?: string) => { await api.updateProfile({ password: p }); await refreshState(); };
|
|
const deleteTeam = async (id: string) => { if (window.confirm("EXPEL_OPERATOR?")) { await api.deleteTeam(id); await refreshState(); } };
|
|
const createBlogPost = async (d: any) => { await api.createBlogPost(d); await refreshState(); };
|
|
const updateBlogPost = async (id: string, d: any) => { await api.updateBlogPost(id, d); await refreshState(); };
|
|
const deleteBlogPost = async (id: string) => { await api.deleteBlogPost(id); await refreshState(); };
|
|
const updateConfig = async (d: FormData) => { await api.updateConfig(d); await refreshState(); };
|
|
|
|
return (
|
|
<CTFContext.Provider value={{
|
|
state, currentUser, login, register, logout, submitFlag, toggleCtf, resetScores,
|
|
upsertChallenge, deleteChallenge, deleteAllChallenges, exportChallenges,
|
|
importChallenges, backupDatabase, restoreDatabase, updateTeam, updateProfile,
|
|
deleteTeam, createBlogPost, updateBlogPost, deleteBlogPost, updateConfig,
|
|
refreshState, loading, loadError
|
|
}}>
|
|
{children}
|
|
</CTFContext.Provider>
|
|
);
|
|
}; |