Made app more modular.
Fixed some bugs. Added some functionality.
This commit is contained in:
117
CTFContext.tsx
Normal file
117
CTFContext.tsx
Normal file
@@ -0,0 +1,117 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user