rhythia-api 120.0.0 → 122.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/approveMap.ts +65 -0
- package/api/getBeatmaps.ts +6 -11
- package/api/getLeaderboard.ts +1 -1
- package/api/getScore.ts +2 -0
- package/api/getUserScores.ts +4 -0
- package/api/nominateMap.ts +59 -0
- package/index.ts +10 -0
- package/package.json +2 -2
- package/types/database.ts +6 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
import { protectedApi, validUser } from "../utils/requestUtils";
|
|
4
|
+
import { supabase } from "../utils/supabase";
|
|
5
|
+
|
|
6
|
+
export const Schema = {
|
|
7
|
+
input: z.strictObject({
|
|
8
|
+
session: z.string(),
|
|
9
|
+
mapId: z.number(),
|
|
10
|
+
}),
|
|
11
|
+
output: z.object({
|
|
12
|
+
error: z.string().optional(),
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export async function POST(request: Request) {
|
|
17
|
+
return protectedApi({
|
|
18
|
+
request,
|
|
19
|
+
schema: Schema,
|
|
20
|
+
authorization: validUser,
|
|
21
|
+
activity: handler,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function handler(data: (typeof Schema)["input"]["_type"]) {
|
|
26
|
+
const user = (await supabase.auth.getUser(data.session)).data.user!;
|
|
27
|
+
let { data: queryUserData, error: userError } = await supabase
|
|
28
|
+
.from("profiles")
|
|
29
|
+
.select("*")
|
|
30
|
+
.eq("uid", user.id)
|
|
31
|
+
.single();
|
|
32
|
+
|
|
33
|
+
if (!queryUserData) {
|
|
34
|
+
return NextResponse.json({ error: "Can't find user" });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const tags = (queryUserData?.badges || []) as string[];
|
|
38
|
+
|
|
39
|
+
if (!tags.includes("MMT")) {
|
|
40
|
+
return NextResponse.json({ error: "Only MMTs can approve maps!" });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { data: mapData, error } = await supabase
|
|
44
|
+
.from("beatmapPages")
|
|
45
|
+
.select("id,nominations")
|
|
46
|
+
.eq("id", data.mapId)
|
|
47
|
+
.single();
|
|
48
|
+
|
|
49
|
+
if (!mapData) {
|
|
50
|
+
return NextResponse.json({ error: "Bad map" });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (mapData.nominations! < 2) {
|
|
54
|
+
return NextResponse.json({
|
|
55
|
+
error: "Maps can get approved only if they have 2 approvals",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await supabase.from("beatmapPages").upsert({
|
|
60
|
+
id: data.mapId,
|
|
61
|
+
status: "RANKED",
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return NextResponse.json({});
|
|
65
|
+
}
|
package/api/getBeatmaps.ts
CHANGED
|
@@ -64,28 +64,26 @@ export async function getBeatmaps(data: (typeof Schema)["input"]["_type"]) {
|
|
|
64
64
|
const endPage = startPage + VIEW_PER_PAGE - 1;
|
|
65
65
|
const countQuery = await supabase
|
|
66
66
|
.from("beatmapPages")
|
|
67
|
-
.select("
|
|
67
|
+
.select("id", { count: "exact", head: true });
|
|
68
68
|
|
|
69
69
|
let qry = supabase
|
|
70
70
|
.from("beatmapPages")
|
|
71
71
|
.select(
|
|
72
72
|
`
|
|
73
|
-
|
|
73
|
+
owner,
|
|
74
|
+
created_at,
|
|
75
|
+
id,
|
|
74
76
|
beatmaps!inner(
|
|
75
|
-
created_at,
|
|
76
77
|
playcount,
|
|
77
|
-
length,
|
|
78
78
|
ranked,
|
|
79
79
|
beatmapFile,
|
|
80
80
|
image,
|
|
81
81
|
starRating,
|
|
82
82
|
difficulty,
|
|
83
|
-
noteCount,
|
|
84
83
|
title
|
|
85
84
|
),
|
|
86
85
|
profiles!inner(
|
|
87
|
-
username
|
|
88
|
-
avatar_url
|
|
86
|
+
username
|
|
89
87
|
)`
|
|
90
88
|
)
|
|
91
89
|
.order("created_at", { ascending: false });
|
|
@@ -105,7 +103,7 @@ export async function getBeatmaps(data: (typeof Schema)["input"]["_type"]) {
|
|
|
105
103
|
qry = qry.eq("status", data.status);
|
|
106
104
|
}
|
|
107
105
|
|
|
108
|
-
if (data.creator) {
|
|
106
|
+
if (data.creator !== undefined) {
|
|
109
107
|
qry = qry.eq("owner", data.creator);
|
|
110
108
|
}
|
|
111
109
|
|
|
@@ -120,8 +118,6 @@ export async function getBeatmaps(data: (typeof Schema)["input"]["_type"]) {
|
|
|
120
118
|
playcount: beatmapPage.beatmaps?.playcount,
|
|
121
119
|
created_at: beatmapPage.created_at,
|
|
122
120
|
difficulty: beatmapPage.beatmaps?.difficulty,
|
|
123
|
-
noteCount: beatmapPage.beatmaps?.noteCount,
|
|
124
|
-
length: beatmapPage.beatmaps?.length,
|
|
125
121
|
title: beatmapPage.beatmaps?.title,
|
|
126
122
|
ranked: beatmapPage.beatmaps?.ranked,
|
|
127
123
|
beatmapFile: beatmapPage.beatmaps?.beatmapFile,
|
|
@@ -129,7 +125,6 @@ export async function getBeatmaps(data: (typeof Schema)["input"]["_type"]) {
|
|
|
129
125
|
starRating: beatmapPage.beatmaps?.starRating,
|
|
130
126
|
owner: beatmapPage.owner,
|
|
131
127
|
ownerUsername: beatmapPage.profiles?.username,
|
|
132
|
-
ownerAvatar: beatmapPage.profiles?.avatar_url,
|
|
133
128
|
})),
|
|
134
129
|
};
|
|
135
130
|
}
|
package/api/getLeaderboard.ts
CHANGED
|
@@ -73,7 +73,7 @@ export async function getLeaderboard(page = 1, session: string) {
|
|
|
73
73
|
const endPage = startPage + VIEW_PER_PAGE - 1;
|
|
74
74
|
const countQuery = await supabase
|
|
75
75
|
.from("profiles")
|
|
76
|
-
.select("
|
|
76
|
+
.select("ban", { count: "exact", head: true })
|
|
77
77
|
.neq("ban", "excluded");
|
|
78
78
|
|
|
79
79
|
let { data: queryData, error } = await supabase
|
package/api/getScore.ts
CHANGED
|
@@ -24,6 +24,7 @@ export const Schema = {
|
|
|
24
24
|
beatmapNotes: z.number().optional().nullable(),
|
|
25
25
|
beatmapTitle: z.string().optional().nullable(),
|
|
26
26
|
username: z.string().optional().nullable(),
|
|
27
|
+
speed: z.number().optional().nullable(),
|
|
27
28
|
})
|
|
28
29
|
.optional(),
|
|
29
30
|
}),
|
|
@@ -76,6 +77,7 @@ export async function handler(
|
|
|
76
77
|
beatmapNotes: score.beatmaps?.noteCount,
|
|
77
78
|
beatmapTitle: score.beatmaps?.title,
|
|
78
79
|
username: score.profiles?.username,
|
|
80
|
+
speed: score.speed,
|
|
79
81
|
},
|
|
80
82
|
});
|
|
81
83
|
}
|
package/api/getUserScores.ts
CHANGED
|
@@ -24,6 +24,7 @@ export const Schema = {
|
|
|
24
24
|
beatmapDifficulty: z.number().optional().nullable(),
|
|
25
25
|
beatmapNotes: z.number().optional().nullable(),
|
|
26
26
|
beatmapTitle: z.string().optional().nullable(),
|
|
27
|
+
speed: z.number().optional().nullable(),
|
|
27
28
|
})
|
|
28
29
|
)
|
|
29
30
|
.optional(),
|
|
@@ -42,6 +43,7 @@ export const Schema = {
|
|
|
42
43
|
beatmapDifficulty: z.number().optional().nullable(),
|
|
43
44
|
beatmapNotes: z.number().optional().nullable(),
|
|
44
45
|
beatmapTitle: z.string().optional().nullable(),
|
|
46
|
+
speed: z.number().optional().nullable(),
|
|
45
47
|
})
|
|
46
48
|
)
|
|
47
49
|
.optional(),
|
|
@@ -128,6 +130,7 @@ export async function handler(
|
|
|
128
130
|
beatmapDifficulty: s.beatmaps?.difficulty,
|
|
129
131
|
beatmapNotes: s.beatmaps?.noteCount,
|
|
130
132
|
beatmapTitle: s.beatmaps?.title,
|
|
133
|
+
speed: s.speed,
|
|
131
134
|
})),
|
|
132
135
|
top: vals?.map((s) => ({
|
|
133
136
|
created_at: s.created_at,
|
|
@@ -142,6 +145,7 @@ export async function handler(
|
|
|
142
145
|
beatmapDifficulty: s.beatmaps?.difficulty,
|
|
143
146
|
beatmapNotes: s.beatmaps?.noteCount,
|
|
144
147
|
beatmapTitle: s.beatmaps?.title,
|
|
148
|
+
speed: s.speed,
|
|
145
149
|
})),
|
|
146
150
|
});
|
|
147
151
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
import { protectedApi, validUser } from "../utils/requestUtils";
|
|
4
|
+
import { supabase } from "../utils/supabase";
|
|
5
|
+
|
|
6
|
+
export const Schema = {
|
|
7
|
+
input: z.strictObject({
|
|
8
|
+
session: z.string(),
|
|
9
|
+
mapId: z.number(),
|
|
10
|
+
}),
|
|
11
|
+
output: z.object({
|
|
12
|
+
error: z.string().optional(),
|
|
13
|
+
}),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export async function POST(request: Request) {
|
|
17
|
+
return protectedApi({
|
|
18
|
+
request,
|
|
19
|
+
schema: Schema,
|
|
20
|
+
authorization: validUser,
|
|
21
|
+
activity: handler,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function handler(data: (typeof Schema)["input"]["_type"]) {
|
|
26
|
+
const user = (await supabase.auth.getUser(data.session)).data.user!;
|
|
27
|
+
let { data: queryUserData, error: userError } = await supabase
|
|
28
|
+
.from("profiles")
|
|
29
|
+
.select("*")
|
|
30
|
+
.eq("uid", user.id)
|
|
31
|
+
.single();
|
|
32
|
+
|
|
33
|
+
if (!queryUserData) {
|
|
34
|
+
return NextResponse.json({ error: "Can't find user" });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const tags = (queryUserData?.badges || []) as string[];
|
|
38
|
+
|
|
39
|
+
if (!tags.includes("RCT")) {
|
|
40
|
+
return NextResponse.json({ error: "Only RCTs can nominate maps!" });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const { data: mapData, error } = await supabase
|
|
44
|
+
.from("beatmapPages")
|
|
45
|
+
.select("id,nominations")
|
|
46
|
+
.eq("id", data.mapId)
|
|
47
|
+
.single();
|
|
48
|
+
|
|
49
|
+
if (!mapData) {
|
|
50
|
+
return NextResponse.json({ error: "Bad map" });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
await supabase.from("beatmapPages").upsert({
|
|
54
|
+
id: data.mapId,
|
|
55
|
+
nominations: mapData.nominations! + 1,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return NextResponse.json({});
|
|
59
|
+
}
|
package/index.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { handleApi } from "./handleApi"
|
|
2
2
|
|
|
3
|
+
// ./api/approveMap.ts API
|
|
4
|
+
import { Schema as ApproveMap } from "./api/approveMap"
|
|
5
|
+
export { Schema as SchemaApproveMap } from "./api/approveMap"
|
|
6
|
+
export const approveMap = handleApi({url:"/api/approveMap",...ApproveMap})
|
|
7
|
+
|
|
3
8
|
// ./api/createBeatmap.ts API
|
|
4
9
|
import { Schema as CreateBeatmap } from "./api/createBeatmap"
|
|
5
10
|
export { Schema as SchemaCreateBeatmap } from "./api/createBeatmap"
|
|
@@ -65,6 +70,11 @@ import { Schema as GetUserScores } from "./api/getUserScores"
|
|
|
65
70
|
export { Schema as SchemaGetUserScores } from "./api/getUserScores"
|
|
66
71
|
export const getUserScores = handleApi({url:"/api/getUserScores",...GetUserScores})
|
|
67
72
|
|
|
73
|
+
// ./api/nominateMap.ts API
|
|
74
|
+
import { Schema as NominateMap } from "./api/nominateMap"
|
|
75
|
+
export { Schema as SchemaNominateMap } from "./api/nominateMap"
|
|
76
|
+
export const nominateMap = handleApi({url:"/api/nominateMap",...NominateMap})
|
|
77
|
+
|
|
68
78
|
// ./api/searchUsers.ts API
|
|
69
79
|
import { Schema as SearchUsers } from "./api/searchUsers"
|
|
70
80
|
export { Schema as SchemaSearchUsers } from "./api/searchUsers"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rhythia-api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "122.0.0",
|
|
4
4
|
"main": "index.ts",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"update": "bun ./scripts/update.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"osu-parsers": "^4.1.7",
|
|
34
34
|
"osu-standard-stable": "^5.0.0",
|
|
35
35
|
"simple-git": "^3.25.0",
|
|
36
|
-
"supabase": "^1.
|
|
36
|
+
"supabase": "^1.204.3",
|
|
37
37
|
"tsx": "^4.17.0",
|
|
38
38
|
"utf-8-validate": "^6.0.4",
|
|
39
39
|
"zod": "^3.23.8"
|
package/types/database.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type Database = {
|
|
|
15
15
|
genre: string | null
|
|
16
16
|
id: number
|
|
17
17
|
latestBeatmapHash: string | null
|
|
18
|
+
nominations: number | null
|
|
18
19
|
owner: number | null
|
|
19
20
|
status: string | null
|
|
20
21
|
title: string | null
|
|
@@ -24,6 +25,7 @@ export type Database = {
|
|
|
24
25
|
genre?: string | null
|
|
25
26
|
id?: number
|
|
26
27
|
latestBeatmapHash?: string | null
|
|
28
|
+
nominations?: number | null
|
|
27
29
|
owner?: number | null
|
|
28
30
|
status?: string | null
|
|
29
31
|
title?: string | null
|
|
@@ -33,6 +35,7 @@ export type Database = {
|
|
|
33
35
|
genre?: string | null
|
|
34
36
|
id?: number
|
|
35
37
|
latestBeatmapHash?: string | null
|
|
38
|
+
nominations?: number | null
|
|
36
39
|
owner?: number | null
|
|
37
40
|
status?: string | null
|
|
38
41
|
title?: string | null
|
|
@@ -174,6 +177,7 @@ export type Database = {
|
|
|
174
177
|
passed: boolean | null
|
|
175
178
|
replayHwid: string | null
|
|
176
179
|
songId: string | null
|
|
180
|
+
speed: number | null
|
|
177
181
|
userId: number | null
|
|
178
182
|
}
|
|
179
183
|
Insert: {
|
|
@@ -185,6 +189,7 @@ export type Database = {
|
|
|
185
189
|
passed?: boolean | null
|
|
186
190
|
replayHwid?: string | null
|
|
187
191
|
songId?: string | null
|
|
192
|
+
speed?: number | null
|
|
188
193
|
userId?: number | null
|
|
189
194
|
}
|
|
190
195
|
Update: {
|
|
@@ -196,6 +201,7 @@ export type Database = {
|
|
|
196
201
|
passed?: boolean | null
|
|
197
202
|
replayHwid?: string | null
|
|
198
203
|
songId?: string | null
|
|
204
|
+
speed?: number | null
|
|
199
205
|
userId?: number | null
|
|
200
206
|
}
|
|
201
207
|
Relationships: [
|