diff --git a/app/challenges/page.tsx b/app/challenges/page.tsx
index ecbcd44..e038e03 100644
--- a/app/challenges/page.tsx
+++ b/app/challenges/page.tsx
@@ -11,6 +11,7 @@ import CTFNotStarted from '@/components/CTFNotStarted';
// Utils
import { getChallenges } from '@/util/challenges';
import { getMyProfile } from '@/util/profile';
+import { getAdminChallenges } from '@/util/admin';
import { AUTH_COOKIE_NAME } from '@/util/config';
@@ -27,20 +28,46 @@ export default async function ChallengesPage() {
if (profile.kind === 'badToken')
return redirect('/logout');
- return challenges.kind === 'goodChallenges' ? (
+ if (challenges.kind !== 'goodChallenges') return (
+
+ );
+
+ // Support non-standard properties by sourcing them from the admin endpoint.
+ const adminData = await getAdminChallData();
+ let challs = challenges.data;
+
+ if (adminData) {
+ // Filter out challs with prereqs that are not met yet
+ const solved = new Set(profile.data.solves.map((c) => c.id));
+ challs = challs.filter((c) => !adminData[c.id].prereqs || adminData[c.id].prereqs!.every((p) => solved.has(p)));
+
+ // Inject desired properties back into client challenges
+ for (const c of challs) {
+ c.difficulty = adminData[c.id].difficulty;
+ }
+ }
+
+ return (
- ) : (
-
- )
+ );
+}
+
+async function getAdminChallData() {
+ if (!process.env.ADMIN_TOKEN) return;
+
+ const res = await getAdminChallenges(process.env.ADMIN_TOKEN);
+ if (res.kind === 'badToken') return;
+
+ return Object.fromEntries(res.data.map((c) => [c.id, c]));
}
diff --git a/util/admin.ts b/util/admin.ts
index 2c8e2de..57b778b 100644
--- a/util/admin.ts
+++ b/util/admin.ts
@@ -2,9 +2,11 @@ import type { Challenge } from '@/util/challenges';
import type { BadTokenResponse } from '@/util/errors';
-export type AdminChallenge = Challenge & {
+export type AdminChallenge = Exclude & {
flag: string,
points: { min: number, max: number }
+
+ prereqs?: string[], // Non-standard
}
type AdminChallengesResponse = {
@@ -15,9 +17,7 @@ type AdminChallengesResponse = {
export async function getAdminChallenges(token: string): Promise {
const res = await fetch(`${process.env.API_BASE}/admin/challs`, {
- headers: {
- 'Authorization': `Bearer ${token}`
- }
+ headers: { 'Authorization': `Bearer ${token}` }
});
return res.json();
diff --git a/util/challenges.ts b/util/challenges.ts
index a8bf331..7816ede 100644
--- a/util/challenges.ts
+++ b/util/challenges.ts
@@ -11,6 +11,8 @@ export type Challenge = {
sortWeight: number,
solves: number,
points: number,
+
+ difficulty?: string, // Non-standard
}
type FileData = {