rhythia-api 235.0.0 → 236.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/getBeatmaps.ts +75 -74
- package/api/reportProfile.ts +94 -0
- package/index.ts +47 -29
- package/package.json +2 -1
- package/types/database.ts +39 -0
- package/worker.ts +2 -0
package/api/getBeatmaps.ts
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { NextResponse } from "../utils/response";
|
|
2
|
-
import z from "zod";
|
|
3
|
-
import { protectedApi } from "../utils/requestUtils";
|
|
4
|
-
import { supabase } from "../utils/supabase";
|
|
5
|
-
|
|
6
|
-
export const Schema = {
|
|
7
|
-
input: z.strictObject({
|
|
8
|
-
session: z.string(),
|
|
9
|
-
textFilter: z.string().optional(),
|
|
10
|
-
authorFilter: z.string().optional(),
|
|
11
|
-
tagsFilter: z.string().optional(),
|
|
12
|
-
page: z.number().default(1),
|
|
13
|
-
maxStars: z.number().optional(),
|
|
14
|
-
minLength: z.number().optional(),
|
|
15
|
-
maxLength: z.number().optional(),
|
|
16
|
-
minStars: z.number().optional(),
|
|
17
|
-
creator: z.number().optional(),
|
|
18
|
-
status: z.string().optional(),
|
|
19
|
-
}),
|
|
20
|
-
output: z.object({
|
|
21
|
-
error: z.string().optional(),
|
|
22
|
-
total: z.number(),
|
|
23
|
-
viewPerPage: z.number(),
|
|
24
|
-
currentPage: z.number(),
|
|
25
|
-
beatmaps: z
|
|
1
|
+
import { NextResponse } from "../utils/response";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
import { protectedApi } from "../utils/requestUtils";
|
|
4
|
+
import { supabase } from "../utils/supabase";
|
|
5
|
+
|
|
6
|
+
export const Schema = {
|
|
7
|
+
input: z.strictObject({
|
|
8
|
+
session: z.string(),
|
|
9
|
+
textFilter: z.string().optional(),
|
|
10
|
+
authorFilter: z.string().optional(),
|
|
11
|
+
tagsFilter: z.string().optional(),
|
|
12
|
+
page: z.number().default(1),
|
|
13
|
+
maxStars: z.number().optional(),
|
|
14
|
+
minLength: z.number().optional(),
|
|
15
|
+
maxLength: z.number().optional(),
|
|
16
|
+
minStars: z.number().optional(),
|
|
17
|
+
creator: z.number().optional(),
|
|
18
|
+
status: z.string().optional(),
|
|
19
|
+
}),
|
|
20
|
+
output: z.object({
|
|
21
|
+
error: z.string().optional(),
|
|
22
|
+
total: z.number(),
|
|
23
|
+
viewPerPage: z.number(),
|
|
24
|
+
currentPage: z.number(),
|
|
25
|
+
beatmaps: z
|
|
26
26
|
.array(
|
|
27
27
|
z.object({
|
|
28
28
|
id: z.number(),
|
|
29
29
|
playcount: z.number().nullable().optional(),
|
|
30
30
|
created_at: z.string().nullable().optional(),
|
|
31
|
-
difficulty: z.number().nullable().optional(),
|
|
32
|
-
noteCount: z.number().nullable().optional(),
|
|
33
|
-
length: z.number().nullable().optional(),
|
|
34
|
-
title: z.string().nullable().optional(),
|
|
35
|
-
ranked: z.boolean().nullable().optional(),
|
|
36
|
-
beatmapFile: z.string().nullable().optional(),
|
|
37
|
-
image: z.string().nullable().optional(),
|
|
38
|
-
starRating: z.number().nullable().optional(),
|
|
31
|
+
difficulty: z.number().nullable().optional(),
|
|
32
|
+
noteCount: z.number().nullable().optional(),
|
|
33
|
+
length: z.number().nullable().optional(),
|
|
34
|
+
title: z.string().nullable().optional(),
|
|
35
|
+
ranked: z.boolean().nullable().optional(),
|
|
36
|
+
beatmapFile: z.string().nullable().optional(),
|
|
37
|
+
image: z.string().nullable().optional(),
|
|
38
|
+
starRating: z.number().nullable().optional(),
|
|
39
39
|
owner: z.number().nullable().optional(),
|
|
40
40
|
ownerUsername: z.string().nullable().optional(),
|
|
41
41
|
ownerAvatar: z.string().nullable().optional(),
|
|
@@ -46,30 +46,31 @@ export const Schema = {
|
|
|
46
46
|
})
|
|
47
47
|
)
|
|
48
48
|
.optional(),
|
|
49
|
-
}),
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
export async function POST(request: Request): Promise<NextResponse> {
|
|
53
|
-
return protectedApi({
|
|
54
|
-
request,
|
|
55
|
-
schema: Schema,
|
|
56
|
-
authorization: () => {},
|
|
57
|
-
activity: handler,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export async function handler(
|
|
62
|
-
data: (typeof Schema)["input"]["_type"]
|
|
63
|
-
): Promise<NextResponse<(typeof Schema)["output"]["_type"]>> {
|
|
64
|
-
const result = await getBeatmaps(data);
|
|
65
|
-
return NextResponse.json(result);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const VIEW_PER_PAGE = 50;
|
|
69
|
-
|
|
49
|
+
}),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export async function POST(request: Request): Promise<NextResponse> {
|
|
53
|
+
return protectedApi({
|
|
54
|
+
request,
|
|
55
|
+
schema: Schema,
|
|
56
|
+
authorization: () => {},
|
|
57
|
+
activity: handler,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function handler(
|
|
62
|
+
data: (typeof Schema)["input"]["_type"]
|
|
63
|
+
): Promise<NextResponse<(typeof Schema)["output"]["_type"]>> {
|
|
64
|
+
const result = await getBeatmaps(data);
|
|
65
|
+
return NextResponse.json(result);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const VIEW_PER_PAGE = 50;
|
|
69
|
+
|
|
70
70
|
export async function getBeatmaps(data: (typeof Schema)["input"]["_type"]) {
|
|
71
71
|
const startPage = (data.page - 1) * VIEW_PER_PAGE;
|
|
72
72
|
const endPage = startPage + VIEW_PER_PAGE - 1;
|
|
73
|
+
// status filter is a bit uncanny, we store qualified as a field, but it acts as a status in the UI.
|
|
73
74
|
const statusFilter = data.status?.toUpperCase();
|
|
74
75
|
let countQry = supabase
|
|
75
76
|
.from("beatmapPages")
|
|
@@ -100,14 +101,14 @@ export async function getBeatmaps(data: (typeof Schema)["input"]["_type"]) {
|
|
|
100
101
|
video_url,
|
|
101
102
|
beatmaps!inner(
|
|
102
103
|
playcount,
|
|
103
|
-
ranked,
|
|
104
|
-
beatmapFile,
|
|
105
|
-
image,
|
|
106
|
-
starRating,
|
|
107
|
-
difficulty,
|
|
108
|
-
length,
|
|
109
|
-
title
|
|
110
|
-
),
|
|
104
|
+
ranked,
|
|
105
|
+
beatmapFile,
|
|
106
|
+
image,
|
|
107
|
+
starRating,
|
|
108
|
+
difficulty,
|
|
109
|
+
length,
|
|
110
|
+
title
|
|
111
|
+
),
|
|
111
112
|
profiles!inner(
|
|
112
113
|
username
|
|
113
114
|
)`
|
|
@@ -173,18 +174,18 @@ export async function getBeatmaps(data: (typeof Schema)["input"]["_type"]) {
|
|
|
173
174
|
return {
|
|
174
175
|
total: countQuery.count || 0,
|
|
175
176
|
viewPerPage: VIEW_PER_PAGE,
|
|
176
|
-
currentPage: data.page,
|
|
177
|
-
beatmaps: queryData.data?.map((beatmapPage) => ({
|
|
178
|
-
id: beatmapPage.id,
|
|
179
|
-
tags: beatmapPage.tags,
|
|
180
|
-
playcount: beatmapPage.beatmaps?.playcount,
|
|
181
|
-
created_at: beatmapPage.created_at,
|
|
182
|
-
difficulty: beatmapPage.beatmaps?.difficulty,
|
|
183
|
-
title: beatmapPage.beatmaps?.title,
|
|
184
|
-
ranked: beatmapPage.beatmaps?.ranked,
|
|
185
|
-
length: beatmapPage.beatmaps?.length,
|
|
186
|
-
beatmapFile: beatmapPage.beatmaps?.beatmapFile,
|
|
187
|
-
image: beatmapPage.beatmaps?.image,
|
|
177
|
+
currentPage: data.page,
|
|
178
|
+
beatmaps: queryData.data?.map((beatmapPage) => ({
|
|
179
|
+
id: beatmapPage.id,
|
|
180
|
+
tags: beatmapPage.tags,
|
|
181
|
+
playcount: beatmapPage.beatmaps?.playcount,
|
|
182
|
+
created_at: beatmapPage.created_at,
|
|
183
|
+
difficulty: beatmapPage.beatmaps?.difficulty,
|
|
184
|
+
title: beatmapPage.beatmaps?.title,
|
|
185
|
+
ranked: beatmapPage.beatmaps?.ranked,
|
|
186
|
+
length: beatmapPage.beatmaps?.length,
|
|
187
|
+
beatmapFile: beatmapPage.beatmaps?.beatmapFile,
|
|
188
|
+
image: beatmapPage.beatmaps?.image,
|
|
188
189
|
starRating: beatmapPage.beatmaps?.starRating,
|
|
189
190
|
owner: beatmapPage.owner,
|
|
190
191
|
status: beatmapPage.status,
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { NextResponse } from "../utils/response";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
import { protectedApi, validUser } from "../utils/requestUtils";
|
|
4
|
+
import { supabase } from "../utils/supabase";
|
|
5
|
+
import { getUserBySession } from "../utils/getUserBySession";
|
|
6
|
+
import { User } from "@supabase/supabase-js";
|
|
7
|
+
|
|
8
|
+
const MAX_DESCRIPTION_LENGTH = 1000;
|
|
9
|
+
|
|
10
|
+
export const Schema = {
|
|
11
|
+
input: z.strictObject({
|
|
12
|
+
session: z.string(),
|
|
13
|
+
profileId: z.number(),
|
|
14
|
+
description: z.string(),
|
|
15
|
+
}),
|
|
16
|
+
output: z.strictObject({
|
|
17
|
+
error: z.string().optional(),
|
|
18
|
+
id: z.number().optional(),
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export async function POST(request: Request): Promise<NextResponse> {
|
|
23
|
+
return protectedApi({
|
|
24
|
+
request,
|
|
25
|
+
schema: Schema,
|
|
26
|
+
authorization: validUser,
|
|
27
|
+
activity: handler,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function handler({
|
|
32
|
+
session,
|
|
33
|
+
profileId,
|
|
34
|
+
description,
|
|
35
|
+
}: (typeof Schema)["input"]["_type"]): Promise<
|
|
36
|
+
NextResponse<(typeof Schema)["output"]["_type"]>
|
|
37
|
+
> {
|
|
38
|
+
const trimmedDescription = description.trim();
|
|
39
|
+
|
|
40
|
+
if (!trimmedDescription.length) {
|
|
41
|
+
return NextResponse.json({ error: "Report description is required." });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (trimmedDescription.length > MAX_DESCRIPTION_LENGTH) {
|
|
45
|
+
return NextResponse.json({
|
|
46
|
+
error: `Report description exceeds ${MAX_DESCRIPTION_LENGTH} characters.`,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const user = (await getUserBySession(session)) as User;
|
|
51
|
+
const { data: reporterProfile } = await supabase
|
|
52
|
+
.from("profiles")
|
|
53
|
+
.select("id,ban")
|
|
54
|
+
.eq("uid", user.id)
|
|
55
|
+
.single();
|
|
56
|
+
|
|
57
|
+
if (!reporterProfile) {
|
|
58
|
+
return NextResponse.json({ error: "Can't find user" });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (reporterProfile.ban !== "cool") {
|
|
62
|
+
return NextResponse.json({ error: "Error" });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (reporterProfile.id === profileId) {
|
|
66
|
+
return NextResponse.json({ error: "You can't report yourself." });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { data: reportedProfile } = await supabase
|
|
70
|
+
.from("profiles")
|
|
71
|
+
.select("id")
|
|
72
|
+
.eq("id", profileId)
|
|
73
|
+
.single();
|
|
74
|
+
|
|
75
|
+
if (!reportedProfile) {
|
|
76
|
+
return NextResponse.json({ error: "Player not found." });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const insertResult = await supabase
|
|
80
|
+
.from("profileReports")
|
|
81
|
+
.insert({
|
|
82
|
+
reporter: reporterProfile.id,
|
|
83
|
+
reported: reportedProfile.id,
|
|
84
|
+
description: trimmedDescription,
|
|
85
|
+
})
|
|
86
|
+
.select("id")
|
|
87
|
+
.single();
|
|
88
|
+
|
|
89
|
+
if (insertResult.error) {
|
|
90
|
+
return NextResponse.json({ error: insertResult.error.message });
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return NextResponse.json({ id: insertResult.data?.id });
|
|
94
|
+
}
|
package/index.ts
CHANGED
|
@@ -624,39 +624,39 @@ export const getBeatmapPageById = handleApi({url:"/api/getBeatmapPageById",...Ge
|
|
|
624
624
|
// ./api/getBeatmaps.ts API
|
|
625
625
|
|
|
626
626
|
/*
|
|
627
|
-
export const Schema = {
|
|
628
|
-
input: z.strictObject({
|
|
629
|
-
session: z.string(),
|
|
630
|
-
textFilter: z.string().optional(),
|
|
631
|
-
authorFilter: z.string().optional(),
|
|
632
|
-
tagsFilter: z.string().optional(),
|
|
633
|
-
page: z.number().default(1),
|
|
634
|
-
maxStars: z.number().optional(),
|
|
635
|
-
minLength: z.number().optional(),
|
|
636
|
-
maxLength: z.number().optional(),
|
|
637
|
-
minStars: z.number().optional(),
|
|
638
|
-
creator: z.number().optional(),
|
|
639
|
-
status: z.string().optional(),
|
|
640
|
-
}),
|
|
641
|
-
output: z.object({
|
|
642
|
-
error: z.string().optional(),
|
|
643
|
-
total: z.number(),
|
|
644
|
-
viewPerPage: z.number(),
|
|
645
|
-
currentPage: z.number(),
|
|
646
|
-
beatmaps: z
|
|
627
|
+
export const Schema = {
|
|
628
|
+
input: z.strictObject({
|
|
629
|
+
session: z.string(),
|
|
630
|
+
textFilter: z.string().optional(),
|
|
631
|
+
authorFilter: z.string().optional(),
|
|
632
|
+
tagsFilter: z.string().optional(),
|
|
633
|
+
page: z.number().default(1),
|
|
634
|
+
maxStars: z.number().optional(),
|
|
635
|
+
minLength: z.number().optional(),
|
|
636
|
+
maxLength: z.number().optional(),
|
|
637
|
+
minStars: z.number().optional(),
|
|
638
|
+
creator: z.number().optional(),
|
|
639
|
+
status: z.string().optional(),
|
|
640
|
+
}),
|
|
641
|
+
output: z.object({
|
|
642
|
+
error: z.string().optional(),
|
|
643
|
+
total: z.number(),
|
|
644
|
+
viewPerPage: z.number(),
|
|
645
|
+
currentPage: z.number(),
|
|
646
|
+
beatmaps: z
|
|
647
647
|
.array(
|
|
648
648
|
z.object({
|
|
649
649
|
id: z.number(),
|
|
650
650
|
playcount: z.number().nullable().optional(),
|
|
651
651
|
created_at: z.string().nullable().optional(),
|
|
652
|
-
difficulty: z.number().nullable().optional(),
|
|
653
|
-
noteCount: z.number().nullable().optional(),
|
|
654
|
-
length: z.number().nullable().optional(),
|
|
655
|
-
title: z.string().nullable().optional(),
|
|
656
|
-
ranked: z.boolean().nullable().optional(),
|
|
657
|
-
beatmapFile: z.string().nullable().optional(),
|
|
658
|
-
image: z.string().nullable().optional(),
|
|
659
|
-
starRating: z.number().nullable().optional(),
|
|
652
|
+
difficulty: z.number().nullable().optional(),
|
|
653
|
+
noteCount: z.number().nullable().optional(),
|
|
654
|
+
length: z.number().nullable().optional(),
|
|
655
|
+
title: z.string().nullable().optional(),
|
|
656
|
+
ranked: z.boolean().nullable().optional(),
|
|
657
|
+
beatmapFile: z.string().nullable().optional(),
|
|
658
|
+
image: z.string().nullable().optional(),
|
|
659
|
+
starRating: z.number().nullable().optional(),
|
|
660
660
|
owner: z.number().nullable().optional(),
|
|
661
661
|
ownerUsername: z.string().nullable().optional(),
|
|
662
662
|
ownerAvatar: z.string().nullable().optional(),
|
|
@@ -667,7 +667,7 @@ export const Schema = {
|
|
|
667
667
|
})
|
|
668
668
|
)
|
|
669
669
|
.optional(),
|
|
670
|
-
}),
|
|
670
|
+
}),
|
|
671
671
|
};*/
|
|
672
672
|
import { Schema as GetBeatmaps } from "./api/getBeatmaps"
|
|
673
673
|
export { Schema as SchemaGetBeatmaps } from "./api/getBeatmaps"
|
|
@@ -1348,6 +1348,24 @@ import { Schema as RankMapsArchive } from "./api/rankMapsArchive"
|
|
|
1348
1348
|
export { Schema as SchemaRankMapsArchive } from "./api/rankMapsArchive"
|
|
1349
1349
|
export const rankMapsArchive = handleApi({url:"/api/rankMapsArchive",...RankMapsArchive})
|
|
1350
1350
|
|
|
1351
|
+
// ./api/reportProfile.ts API
|
|
1352
|
+
|
|
1353
|
+
/*
|
|
1354
|
+
export const Schema = {
|
|
1355
|
+
input: z.strictObject({
|
|
1356
|
+
session: z.string(),
|
|
1357
|
+
profileId: z.number(),
|
|
1358
|
+
description: z.string(),
|
|
1359
|
+
}),
|
|
1360
|
+
output: z.strictObject({
|
|
1361
|
+
error: z.string().optional(),
|
|
1362
|
+
id: z.number().optional(),
|
|
1363
|
+
}),
|
|
1364
|
+
};*/
|
|
1365
|
+
import { Schema as ReportProfile } from "./api/reportProfile"
|
|
1366
|
+
export { Schema as SchemaReportProfile } from "./api/reportProfile"
|
|
1367
|
+
export const reportProfile = handleApi({url:"/api/reportProfile",...ReportProfile})
|
|
1368
|
+
|
|
1351
1369
|
// ./api/searchUsers.ts API
|
|
1352
1370
|
|
|
1353
1371
|
/*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rhythia-api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "236.0.0",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"author": "online-contributors-cunev",
|
|
6
6
|
"scripts": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"test": "tsx ./scripts/test.ts",
|
|
11
11
|
"cache:clear-user-scores": "bun run scripts/clear-user-score-cache.ts",
|
|
12
12
|
"db:optimize-user-scores": "bun run scripts/optimize-user-scores-indexes.ts",
|
|
13
|
+
"db:create-profile-reports": "node scripts/create-profile-reports-table.ts",
|
|
13
14
|
"query-pull": "bun run scripts/pull-queries.ts",
|
|
14
15
|
"query-push": "bun run scripts/deploy-queries.ts",
|
|
15
16
|
"queries:pull": "bun run scripts/pull-queries.ts",
|
package/types/database.ts
CHANGED
|
@@ -519,6 +519,45 @@ export type Database = {
|
|
|
519
519
|
}
|
|
520
520
|
Relationships: []
|
|
521
521
|
}
|
|
522
|
+
profileReports: {
|
|
523
|
+
Row: {
|
|
524
|
+
created_at: string
|
|
525
|
+
description: string
|
|
526
|
+
id: number
|
|
527
|
+
reported: number
|
|
528
|
+
reporter: number
|
|
529
|
+
}
|
|
530
|
+
Insert: {
|
|
531
|
+
created_at?: string
|
|
532
|
+
description: string
|
|
533
|
+
id?: number
|
|
534
|
+
reported: number
|
|
535
|
+
reporter: number
|
|
536
|
+
}
|
|
537
|
+
Update: {
|
|
538
|
+
created_at?: string
|
|
539
|
+
description?: string
|
|
540
|
+
id?: number
|
|
541
|
+
reported?: number
|
|
542
|
+
reporter?: number
|
|
543
|
+
}
|
|
544
|
+
Relationships: [
|
|
545
|
+
{
|
|
546
|
+
foreignKeyName: "profileReports_reported_fkey"
|
|
547
|
+
columns: ["reported"]
|
|
548
|
+
isOneToOne: false
|
|
549
|
+
referencedRelation: "profiles"
|
|
550
|
+
referencedColumns: ["id"]
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
foreignKeyName: "profileReports_reporter_fkey"
|
|
554
|
+
columns: ["reporter"]
|
|
555
|
+
isOneToOne: false
|
|
556
|
+
referencedRelation: "profiles"
|
|
557
|
+
referencedColumns: ["id"]
|
|
558
|
+
},
|
|
559
|
+
]
|
|
560
|
+
}
|
|
522
561
|
profiles: {
|
|
523
562
|
Row: {
|
|
524
563
|
about_me: string | null
|
package/worker.ts
CHANGED
|
@@ -46,6 +46,7 @@ import { POST as getVideoUploadUrl } from "./api/getVideoUploadUrl";
|
|
|
46
46
|
import { POST as postBeatmapComment } from "./api/postBeatmapComment";
|
|
47
47
|
import { POST as qualifyMap } from "./api/qualifyMap";
|
|
48
48
|
import { POST as rankMapsArchive } from "./api/rankMapsArchive";
|
|
49
|
+
import { POST as reportProfile } from "./api/reportProfile";
|
|
49
50
|
import { POST as searchUsers } from "./api/searchUsers";
|
|
50
51
|
import { POST as setPasskey } from "./api/setPasskey";
|
|
51
52
|
import { POST as submitScore } from "./api/submitScore";
|
|
@@ -113,6 +114,7 @@ const apiRoutes: Record<string, RouteHandler> = {
|
|
|
113
114
|
"/api/postBeatmapComment": postBeatmapComment,
|
|
114
115
|
"/api/qualifyMap": qualifyMap,
|
|
115
116
|
"/api/rankMapsArchive": rankMapsArchive,
|
|
117
|
+
"/api/reportProfile": reportProfile,
|
|
116
118
|
"/api/searchUsers": searchUsers,
|
|
117
119
|
"/api/setPasskey": setPasskey,
|
|
118
120
|
"/api/submitScore": submitScore,
|