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.
@@ -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 && data.data.username.length === 0) {
38
- return NextResponse.json(
39
- {
40
- error: "Username can't be empty",
41
- },
42
- { status: 404 }
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
- if (data.data.username && data.data.username.length > 20) {
47
- return NextResponse.json(
48
- {
49
- error: "Username too long.",
50
- },
51
- { status: 404 }
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
- beatmapHash: score.beatmapHash,
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: `${user.user_metadata.full_name.slice(0, 20)}${Math.round(
102
- Math.random() * 900000 + 100000
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhythia-api",
3
- "version": "207.0.0",
3
+ "version": "209.0.0",
4
4
  "main": "index.ts",
5
5
  "author": "online-contributors-cunev",
6
6
  "scripts": {
@@ -1,8 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(yarn update)"
5
- ],
6
- "deny": []
7
- }
8
- }