rhythia-api 207.0.0 → 209.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/editProfile.ts +26 -15
- package/api/executeAdminOperation.ts +61 -42
- package/api/getProfile.ts +2 -7
- package/package.json +1 -1
- package/.claude/settings.local.json +0 -8
package/api/editProfile.ts
CHANGED
|
@@ -34,22 +34,33 @@ export async function POST(request: Request): Promise<NextResponse> {
|
|
|
34
34
|
export async function handler(
|
|
35
35
|
data: (typeof Schema)["input"]["_type"]
|
|
36
36
|
): Promise<NextResponse<(typeof Schema)["output"]["_type"]>> {
|
|
37
|
-
if (data.data.username !== undefined
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
if (data.data.username !== undefined) {
|
|
38
|
+
if (data.data.username.length < 3) {
|
|
39
|
+
return NextResponse.json(
|
|
40
|
+
{
|
|
41
|
+
error: "Username must be at least 3 characters long",
|
|
42
|
+
},
|
|
43
|
+
{ status: 404 }
|
|
44
|
+
);
|
|
45
|
+
}
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
if (data.data.username.length > 20) {
|
|
48
|
+
return NextResponse.json(
|
|
49
|
+
{
|
|
50
|
+
error: "Username too long.",
|
|
51
|
+
},
|
|
52
|
+
{ status: 404 }
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (!/^[a-z0-9]+$/i.test(data.data.username)) {
|
|
57
|
+
return NextResponse.json(
|
|
58
|
+
{
|
|
59
|
+
error: "Username can only contain letters (a-z) and numbers (0-9)",
|
|
60
|
+
},
|
|
61
|
+
{ status: 404 }
|
|
62
|
+
);
|
|
63
|
+
}
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
if (validator.trim(data.data.username || "") !== (data.data.username || "")) {
|
|
@@ -11,6 +11,8 @@ const adminOperations = {
|
|
|
11
11
|
deleteUser: z.object({ userId: z.number() }),
|
|
12
12
|
changeFlag: z.object({ userId: z.number(), flag: z.string() }),
|
|
13
13
|
changeBadges: z.object({ userId: z.number(), badges: z.string() }),
|
|
14
|
+
addBadge: z.object({ userId: z.number(), badge: z.string() }),
|
|
15
|
+
removeBadge: z.object({ userId: z.number(), badge: z.string() }),
|
|
14
16
|
excludeUser: z.object({ userId: z.number() }),
|
|
15
17
|
restrictUser: z.object({ userId: z.number() }),
|
|
16
18
|
silenceUser: z.object({ userId: z.number() }),
|
|
@@ -73,6 +75,14 @@ const OperationParam = z.discriminatedUnion("operation", [
|
|
|
73
75
|
operation: z.literal("changeBadges"),
|
|
74
76
|
params: adminOperations.changeBadges,
|
|
75
77
|
}),
|
|
78
|
+
z.object({
|
|
79
|
+
operation: z.literal("addBadge"),
|
|
80
|
+
params: adminOperations.addBadge,
|
|
81
|
+
}),
|
|
82
|
+
z.object({
|
|
83
|
+
operation: z.literal("removeBadge"),
|
|
84
|
+
params: adminOperations.removeBadge,
|
|
85
|
+
}),
|
|
76
86
|
z.object({
|
|
77
87
|
operation: z.literal("getScoresPaginated"),
|
|
78
88
|
params: adminOperations.getScoresPaginated,
|
|
@@ -217,7 +227,55 @@ export async function handler(
|
|
|
217
227
|
})
|
|
218
228
|
.select();
|
|
219
229
|
}
|
|
230
|
+
break;
|
|
231
|
+
|
|
232
|
+
case "addBadge":
|
|
233
|
+
// Allow only developers to modify badges.
|
|
234
|
+
if ((queryUserData.badges as string[]).includes("Developer")) {
|
|
235
|
+
// Get current badges
|
|
236
|
+
const { data: targetUser } = await supabase
|
|
237
|
+
.from("profiles")
|
|
238
|
+
.select("badges")
|
|
239
|
+
.eq("id", params.userId)
|
|
240
|
+
.single();
|
|
241
|
+
|
|
242
|
+
const currentBadges = (targetUser?.badges || []) as string[];
|
|
243
|
+
if (!currentBadges.includes(params.badge)) {
|
|
244
|
+
currentBadges.push(params.badge);
|
|
245
|
+
result = await supabase
|
|
246
|
+
.from("profiles")
|
|
247
|
+
.upsert({
|
|
248
|
+
id: params.userId,
|
|
249
|
+
badges: currentBadges,
|
|
250
|
+
})
|
|
251
|
+
.select();
|
|
252
|
+
} else {
|
|
253
|
+
result = { data: targetUser, error: null };
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
break;
|
|
220
257
|
|
|
258
|
+
case "removeBadge":
|
|
259
|
+
// Allow only developers to modify badges.
|
|
260
|
+
if ((queryUserData.badges as string[]).includes("Developer")) {
|
|
261
|
+
// Get current badges
|
|
262
|
+
const { data: targetUser } = await supabase
|
|
263
|
+
.from("profiles")
|
|
264
|
+
.select("badges")
|
|
265
|
+
.eq("id", params.userId)
|
|
266
|
+
.single();
|
|
267
|
+
|
|
268
|
+
const currentBadges = (targetUser?.badges || []) as string[];
|
|
269
|
+
const updatedBadges = currentBadges.filter(b => b !== params.badge);
|
|
270
|
+
|
|
271
|
+
result = await supabase
|
|
272
|
+
.from("profiles")
|
|
273
|
+
.upsert({
|
|
274
|
+
id: params.userId,
|
|
275
|
+
badges: updatedBadges,
|
|
276
|
+
})
|
|
277
|
+
.select();
|
|
278
|
+
}
|
|
221
279
|
break;
|
|
222
280
|
|
|
223
281
|
case "getScoresPaginated":
|
|
@@ -228,30 +286,14 @@ export async function handler(
|
|
|
228
286
|
`
|
|
229
287
|
id,
|
|
230
288
|
awarded_sp,
|
|
231
|
-
created_at,
|
|
232
|
-
misses,
|
|
233
|
-
mods,
|
|
234
|
-
passed,
|
|
235
|
-
songId,
|
|
236
|
-
speed,
|
|
237
|
-
spin,
|
|
238
289
|
userId,
|
|
239
|
-
beatmapHash,
|
|
240
290
|
additional_data,
|
|
241
|
-
beatmaps (
|
|
242
|
-
difficulty,
|
|
243
|
-
noteCount,
|
|
244
|
-
title,
|
|
245
|
-
starRating,
|
|
246
|
-
image,
|
|
247
|
-
imageLarge
|
|
248
|
-
),
|
|
249
291
|
profiles (
|
|
250
|
-
username
|
|
251
|
-
avatar_url
|
|
292
|
+
username
|
|
252
293
|
)
|
|
253
294
|
`
|
|
254
295
|
)
|
|
296
|
+
.eq("passed", true)
|
|
255
297
|
.order("created_at", { ascending: false })
|
|
256
298
|
.range(offset, offset + params.limit - 1);
|
|
257
299
|
|
|
@@ -268,31 +310,8 @@ export async function handler(
|
|
|
268
310
|
const transformed: any = {
|
|
269
311
|
id: score.id,
|
|
270
312
|
awarded_sp: score.awarded_sp,
|
|
271
|
-
created_at: score.created_at,
|
|
272
|
-
misses: score.misses,
|
|
273
|
-
mods: score.mods,
|
|
274
|
-
passed: score.passed,
|
|
275
|
-
songId: score.songId,
|
|
276
|
-
speed: score.speed,
|
|
277
|
-
spin: score.spin,
|
|
278
313
|
userId: score.userId,
|
|
279
|
-
|
|
280
|
-
beatmap: score.beatmaps
|
|
281
|
-
? {
|
|
282
|
-
difficulty: score.beatmaps.difficulty,
|
|
283
|
-
noteCount: score.beatmaps.noteCount,
|
|
284
|
-
title: score.beatmaps.title,
|
|
285
|
-
starRating: score.beatmaps.starRating,
|
|
286
|
-
image: score.beatmaps.image,
|
|
287
|
-
imageLarge: score.beatmaps.imageLarge,
|
|
288
|
-
}
|
|
289
|
-
: null,
|
|
290
|
-
profile: score.profiles
|
|
291
|
-
? {
|
|
292
|
-
username: score.profiles.username,
|
|
293
|
-
avatar_url: score.profiles.avatar_url,
|
|
294
|
-
}
|
|
295
|
-
: null,
|
|
314
|
+
username: score.profiles?.username || null,
|
|
296
315
|
};
|
|
297
316
|
|
|
298
317
|
if (params.includeAdditionalData) {
|
package/api/getProfile.ts
CHANGED
|
@@ -98,13 +98,8 @@ export async function handler(
|
|
|
98
98
|
avatar_url:
|
|
99
99
|
"https://rhthia-avatars.s3.eu-central-003.backblazeb2.com/user-avatar-1725309193296-72002e6b-321c-4f60-a692-568e0e75147d",
|
|
100
100
|
badges: [],
|
|
101
|
-
username:
|
|
102
|
-
|
|
103
|
-
)}`,
|
|
104
|
-
computedUsername: `${user.user_metadata.full_name.slice(
|
|
105
|
-
0,
|
|
106
|
-
20
|
|
107
|
-
)}${Math.round(Math.random() * 900000 + 100000)}`.toLowerCase(),
|
|
101
|
+
username: `user${Math.round(Math.random() * 900000 + 100000)}`,
|
|
102
|
+
computedUsername: `user${Math.round(Math.random() * 900000 + 100000)}`.toLowerCase(),
|
|
108
103
|
flag: (geo.country || "US").toUpperCase(),
|
|
109
104
|
created_at: Date.now(),
|
|
110
105
|
}).select(`
|
package/package.json
CHANGED