Skip to content

Commit a7d7018

Browse files
committed
Initial start of rewrite.
1 parent 1c56a68 commit a7d7018

24 files changed

+364
-238
lines changed

lib/auth/webhookClientAuthPerms.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { getConfig } from "metadata/server";
55
const addClientPerms = async (input: InputAuth): Promise<Auth> => {
66
if (input.__type === "oauth") return {
77
...input,
8-
trustedClientAuth: (await getConfig()).webhook.clientOauthAllowToken,
8+
oauth_allow_token: (await getConfig()).webhook.clientOauthAllowToken,
99
};
1010
else return input;
1111
};

lib/cache/challs.ts

+13
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ export interface CachedChall {
3232
export const CHALLENGE_HASH_KEY = "chall";
3333

3434
export const parseChallenge = (challJson: string): CachedChall | null => {
35+
console.log("tp0");
3536
const parsed: unknown = JSON.parse(challJson);
37+
console.log(parsed);
3638

3739
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return null;
3840

@@ -48,11 +50,14 @@ export const parseChallenge = (challJson: string): CachedChall | null => {
4850
if (!vis || !chall) return null;
4951
}
5052

53+
console.log("tp1");
54+
5155
const challId = challIdFromStr(challRaw);
5256
if (!challId) return null;
5357

5458
if (typeof csmRaw !== 'object' || csmRaw === null) return null;
5559

60+
console.log("tp2");
5661

5762
const {
5863
name: nameRaw,
@@ -83,6 +88,9 @@ export const parseChallenge = (challJson: string): CachedChall | null => {
8388
!hint || !tag
8489
) return null;
8590
}
91+
92+
console.log("tp3");
93+
8694
{
8795
const cats = catRaw.every((s) => typeof s === 'string');
8896
const auths = authRaw.every((s) => typeof s === 'string');
@@ -92,6 +100,8 @@ export const parseChallenge = (challJson: string): CachedChall | null => {
92100
if (!cats || !auths || !hints || !tags) return null;
93101
if (typeof linksRaw !== 'object' || linksRaw === null) return null;
94102
}
103+
console.log("tp4");
104+
95105
const { web, nc, admin, static: staticLinks } = linksRaw as Record<string, unknown>;
96106
{
97107
const webArr = Array.isArray(web);
@@ -108,6 +118,7 @@ export const parseChallenge = (challJson: string): CachedChall | null => {
108118

109119
if (!webValid || !ncValid || !adminValid || !staticValid) return null;
110120
}
121+
111122
const links = { nc, web, admin, static: staticLinks };
112123

113124
const visible = visRaw;
@@ -125,6 +136,8 @@ export const parseChallenge = (challJson: string): CachedChall | null => {
125136
name, points, desc, solveCount, categories, authors, hints, tags, links, id: challId
126137
};
127138

139+
console.log("tp5");
140+
128141
return { visible, id: challId, clientSideMetadata };
129142
}
130143

lib/cache/teams.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -97,22 +97,23 @@ export const removeStale = async (notStale: TeamId[]): Promise<TeamId[]> => {
9797
return removeIds.map(teamIdFromStr).flatMap(id => id ? [id] : []);
9898
};
9999

100-
// export const sortBy = (teams: CachedTeamMeta[]) => [...teams].sort((a, b) => a.score - b.score);
100+
101+
101102

102103
export enum SortingCriteria {
103-
POINTS,
104+
POINTS_HIGH_FIRST,
104105
LASTSOLVE_REV,
105106
NAME,
106107
ELIG,
107108
}
108109

109-
const { POINTS, LASTSOLVE_REV, NAME, ELIG } = SortingCriteria;
110+
const { POINTS_HIGH_FIRST, LASTSOLVE_REV, NAME, ELIG } = SortingCriteria;
110111

111112

112113
const compareCriteria = (a: CachedTeamMeta, b: CachedTeamMeta, criteria: SortingCriteria) => {
113114
switch (criteria) {
114-
case POINTS:
115-
return a.score - b.score;
115+
case POINTS_HIGH_FIRST:
116+
return b.score - a.score;
116117
case LASTSOLVE_REV:
117118
return (b.lastSolve ?? -1) - (a.lastSolve ?? -1);
118119
case NAME:
@@ -123,7 +124,7 @@ const compareCriteria = (a: CachedTeamMeta, b: CachedTeamMeta, criteria: Sorting
123124
}
124125

125126

126-
export const sortBy = (challs: CachedTeamMeta[], criteria: SortingCriteria[] = [POINTS, LASTSOLVE_REV, NAME, ELIG]) => {
127+
export const sortBy = (challs: CachedTeamMeta[], criteria: SortingCriteria[] = [POINTS_HIGH_FIRST, LASTSOLVE_REV, NAME, ELIG]) => {
127128
const compareFn = (a: CachedTeamMeta, b: CachedTeamMeta) => criteria
128129
.map(c => compareCriteria(a, b, c))
129130
.find(v => v !== 0) ?? 0;

lib/components/challenges/drop/parts/ChallDropBody.tsx

+21-11
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,15 @@
22
import ReactMarkdown from "react-markdown";
33

44
// Hooks
5-
5+
import { useState } from "react"
66

77
// Types
8-
98
import React, { CSSProperties, FC } from "react"
10-
11-
12-
// Styles
139
import { ChallDropProps } from "./ChallDrop";
14-
10+
import ChallDropFlagInput from "./ChallDropFlagInput";
1511

1612
// Utils
1713
import remarkGfm from "remark-gfm";
18-
import ChallDropFlagInput from "./ChallDropFlagInput";
1914

2015

2116
const ListItem = ({ item, className, style }: { item: string, style?: CSSProperties, className?: string }) => (
@@ -39,10 +34,21 @@ const EnglishList = ({ items: list, style, className }: { items: string[], style
3934
</>;
4035
}
4136

37+
const Hint = ({ value }: { value: string }) => {
38+
const [revealed, setRevealed] = useState(false);
39+
return <div
40+
className={`
41+
block transition-all py-1 px-2 rounded-lg mb-2 select-none
42+
${revealed ? "text-white bg-slate-800 cursor-default" : "text-transparent bg-slate-900 cursor-pointer"}`}
43+
onClick={() => setRevealed(true)}>
44+
{value}
45+
</div>;
46+
};
47+
4248
const BodyMeta: FC<Pick<
4349
ChallDropProps["metadata"],
44-
"solveCount" | "categories" | "points" | "tags" | "authors"
45-
>> = ({ solveCount, categories, points, authors }) => (
50+
"solveCount" | "categories" | "points" | "tags" | "authors" | "hints"
51+
>> = ({ solveCount, categories, points, authors, hints }) => (
4652
<div className="w-full p-5 pt-2 pl-0 ml-auto flex flex-col"
4753
style={{ gridArea: "meta" }}>
4854
<div className="py-3">
@@ -53,6 +59,10 @@ const BodyMeta: FC<Pick<
5359
<span className="py-1.5">
5460
{solveCount.toLocaleString('en-US', {maximumFractionDigits: 0})} {solveCount === 1 ? "solve" : "solves"}
5561
</span>
62+
<div className="py-3">
63+
<h4 className="font-medium text-base mb-1">Hints:</h4>
64+
{hints.map(hint => <Hint value={hint} />)}
65+
</div>
5666
<div className="py-1.5 mb-auto">
5767
By <EnglishList className="font-mono text-chall-author-name-color" items={authors}/>
5868
</div>
@@ -106,7 +116,7 @@ const Links: FC<{ urls: string[], type: LinkType }> = ({ urls, type }) => {
106116
};
107117

108118
const ChallDropBody: FC<ChallDropProps & { open: boolean }> = ({
109-
metadata: { solveCount, categories, points, tags, desc, links, authors },
119+
metadata: { solveCount, categories, points, tags, desc, links, authors, hints },
110120
solved,
111121
submission: { challId, userId, teamId }
112122
}) => (
@@ -139,7 +149,7 @@ const ChallDropBody: FC<ChallDropProps & { open: boolean }> = ({
139149
</div>
140150
</div>
141151

142-
<BodyMeta {...{ solveCount, solved, points, tags, categories, links, authors }}/>
152+
<BodyMeta {...{ solveCount, solved, points, tags, categories, links, authors, hints }}/>
143153

144154
<ChallDropFlagInput {...{ challId, teamId, userId }} />
145155

lib/components/challenges/drop/parts/ChallDropFlagInput.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,14 @@ const ChallDropFlagInput: FC<ChallDropFlagInputProps> = ({challId, userId, teamI
5454
method: "POST",
5555
body: JSON.stringify({ challId, userId, teamId, flag: inputValue }),
5656
});
57-
if (res.ok && await res.text() === "true") location.reload();
58-
else alert("Invalid Flag");
57+
if (res.ok) {
58+
if (await res.text() === "failed") {
59+
alert("Incorrect flag. Please try again.");
60+
}
61+
location.reload();
62+
} else {
63+
alert("Server error");
64+
}
5965
}
6066
}}/>
6167
<div

lib/components/filter-view/FilterView.tsx

+10-2
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,20 @@ import { FilterStateHook } from "hooks/useFilter";
2121

2222
interface FilterViewProps {
2323
filterState: FilterStateHook;
24-
challs: ClientSideMeta[];
24+
challs: (ClientSideMeta & { solved: boolean })[];
2525
}
2626

2727
const FilterView: FC<FilterViewProps> = ({ filterState, challs }) => {
2828
const categories = useMemo(
29-
() => [...new Set(challs.flatMap(c => c.categories).map(s => s.toLowerCase())).values()].sort(),
29+
() => [
30+
...new Set(challs.flatMap(c => c.categories).map(s => s.toLowerCase())).values()
31+
].sort().map(name => [
32+
name,
33+
challs
34+
.filter(c => c.categories.includes(name))
35+
.map(c => c.solved)
36+
.reduce(([solved, total], challSolved) => [solved + Number(challSolved), total + 1], [0, 0] as [number, number]),
37+
] as [string, [number, number]]),
3038
[challs],
3139
);
3240
const tags = useMemo(

lib/components/filter-view/parts/ChallCheckFilter.tsx

+11-7
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ const CheckBox: FC<CheckBoxProps> = ({ checked, set, label: inner }) => (
5858
);
5959

6060
interface ChallCheckFilterProps {
61-
list: string[];
61+
list: ([string, [number, number]] | string)[];
6262
heading: string;
6363
has: (item: string) => boolean,
6464
set: (item: string, value: boolean) => void,
@@ -69,13 +69,17 @@ const ChallCheckFilter: FC<ChallCheckFilterProps> = ({ list, heading, has, set }
6969
<div className="flex flex-col justify-start items-start w-full">
7070
<h4 className="border-b border-b-play-selector-line-divider-color w-full mb-4 text-2xl pb-1">{heading}</h4>
7171
{list.map(
72-
(item, idx) => (
73-
<CheckBox
72+
(input, idx) => typeof input === "string"
73+
? <CheckBox
7474
key={idx}
75-
checked={has(item)}
76-
set={v => set(item, v)}
77-
label={item}/>
78-
)
75+
checked={has(input)}
76+
set={v => set(input, v)}
77+
label={input}/>
78+
: <CheckBox
79+
key={idx}
80+
checked={has(input[0])}
81+
set={v => set(input[0], v)}
82+
label={`${input[0]} (${input[1][0]}/${input[1][1]})`}/>
7983
)}
8084
</div>
8185
);

lib/database/challs.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import { apiLogger } from "logging";
88

99
const syncAllChalls = async () => {
1010
try {
11-
const allChalls = await makeWebhookRequest<DbChallengeMeta[]>({
12-
section: "challenge",
13-
query: { __tag: "get_all" },
11+
const allChalls = await makeWebhookRequest("chall_arr", {
12+
__type: "chall",
13+
query_name: "get_all",
1414
});
1515
const challs = allChalls.map(dbToCacheChall).flatMap(c => c ? [c] : []);
1616
const usedIds = challs.map(c => c.id);
@@ -28,9 +28,10 @@ const syncChall = async ({ id }: { id: ChallId }) => {
2828
try {
2929
apiLogger.trace`Runnning syncChall on chall ${id}.`;
3030

31-
const challData = await makeWebhookRequest<DbChallengeMeta>({
32-
section: "challenge",
33-
query: { __tag: "get", id: challIdToStr(id), },
31+
const challData = await makeWebhookRequest("chall", {
32+
__type: "chall",
33+
query_name: "get",
34+
id: challIdToStr(id),
3435
});
3536

3637
apiLogger.trace`Challenge identified as ${challData.name}.`;
@@ -47,7 +48,7 @@ const syncChall = async ({ id }: { id: ChallId }) => {
4748
apiLogger.trace`Adding challenge to redis cache...`;
4849

4950
const cachedChallenge = await updateChall(chall);
50-
if (cachedChallenge) apiLogger.error`Replaced chall ${cachedChallenge}`;
51+
if (cachedChallenge) apiLogger.warn`Replaced chall ${cachedChallenge}`;
5152

5253
apiLogger.info`${challData.name} sync successful.`;
5354

lib/database/makeWebhookReq.ts

+32-7
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,49 @@
11
import { getConfig } from "metadata/server";
22
import WebhookDbQuery from "./queries";
3+
import { DbChallengeMeta, DbTeamMeta, DbUserMeta } from "./db-types";
34

4-
const makeWebhookDbRequest = async <T>(query: WebhookDbQuery): Promise<T> => {
5-
const { webhook: { url: webhookUrl }, frontendAuthToken } = await getConfig();
5+
type ReturnTypes = {
6+
chall: DbChallengeMeta,
7+
chall_arr: DbChallengeMeta[],
8+
9+
team: DbTeamMeta,
10+
team_arr: DbTeamMeta[],
611

12+
user: DbUserMeta,
13+
user_arr: DbUserMeta[],
14+
15+
availability: boolean,
16+
auth_status: boolean,
17+
};
18+
19+
const makeWebhookDbRequest = async <RetType extends keyof ReturnTypes>(type: RetType, query: WebhookDbQuery): Promise<ReturnTypes[RetType]> => {
20+
const { webhook: { url: webhookUrl }, frontendAuthToken } = await getConfig();
721
const init: RequestInit = {
822
method: "POST",
923
headers: {
1024
"authorization": `Bearer ${frontendAuthToken}`,
1125
"Content-Type": "application/json",
1226
},
1327
body: JSON.stringify({
14-
_type: "sqlquery",
15-
targets: { sql: query },
28+
sql: query,
1629
}),
1730
};
1831
const fetchReturn = await fetch(webhookUrl, init);
19-
const jsonVal = await fetchReturn.json();
20-
if (fetchReturn.ok) return jsonVal.sql;
21-
else throw jsonVal.sql
32+
const returnVal = await fetchReturn.text();
33+
const jsonVal = (() => {
34+
try {
35+
return JSON.parse(returnVal)
36+
} catch (e) {
37+
throw returnVal;
38+
}
39+
})();
40+
41+
if (!fetchReturn.ok) throw jsonVal.sql;
42+
43+
const { __type, data } = jsonVal.sql;
44+
if (__type !== type) throw jsonVal.sql;
45+
46+
return data;
2247
};
2348

2449
export default makeWebhookDbRequest;

lib/database/queries/challs.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
export type GetChallenge = {
2-
__tag: "get";
2+
query_name: "get";
33
id: string;
44
};
55
export type GetAllChallenges = {
6-
__tag: "get_all";
6+
query_name: "get_all";
77
};
88

99
type InnerChallengeQuery = GetChallenge | GetAllChallenges;
1010

1111
type ChallengeQuery = {
12-
section: "challenge";
13-
query: InnerChallengeQuery;
14-
};
12+
__type: "chall";
13+
} & InnerChallengeQuery;
1514

1615
export default ChallengeQuery;

0 commit comments

Comments
 (0)