rhythia-api 241.0.0 → 243.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/handleApi.ts CHANGED
@@ -1,21 +1,24 @@
1
- import { z } from "zod";
2
- let env = "development";
3
- import { profanity, CensorType } from "@2toad/profanity";
4
- export function setEnvironment(
5
- stage: "development" | "testing" | "production"
6
- ) {
7
- env = stage;
8
- }
9
- export function handleApi<
10
- T extends { url: string; input: z.ZodObject<any>; output: z.ZodObject<any> }
11
- >(apiSchema: T) {
12
- profanity.whitelist.addWords(["willy"]);
13
- return async (input: T["input"]["_type"]): Promise<T["output"]["_type"]> => {
14
- const response = await fetch(`https://${env}.rhythia.com${apiSchema.url}`, {
15
- method: "POST",
16
- body: JSON.stringify(input),
17
- });
18
- const output = await response.text();
19
- return JSON.parse(profanity.censor(output));
20
- };
21
- }
1
+ import { z } from "zod";
2
+ let endpoint = "https://development.rhythia.com";
3
+ import { profanity, CensorType } from "@2toad/profanity";
4
+ export function setEnvironment(
5
+ stage: "development" | "testing" | "production"
6
+ ) {
7
+ endpoint = `https://${stage}.rhythia.com`;
8
+ }
9
+ export function setEndpoint(value: string) {
10
+ endpoint = value;
11
+ }
12
+ export function handleApi<
13
+ T extends { url: string; input: z.ZodObject<any>; output: z.ZodObject<any> }
14
+ >(apiSchema: T) {
15
+ profanity.whitelist.addWords(["willy"]);
16
+ return async (input: T["input"]["_type"]): Promise<T["output"]["_type"]> => {
17
+ const response = await fetch(`${endpoint}${apiSchema.url}`, {
18
+ method: "POST",
19
+ body: JSON.stringify(input),
20
+ });
21
+ const output = await response.text();
22
+ return JSON.parse(profanity.censor(output));
23
+ };
24
+ }
package/index.ts CHANGED
@@ -321,6 +321,25 @@ import { Schema as EditProfile } from "./api/editProfile"
321
321
  export { Schema as SchemaEditProfile } from "./api/editProfile"
322
322
  export const editProfile = handleApi({url:"/api/editProfile",...EditProfile})
323
323
 
324
+ // ./api/editProfileFriend.ts API
325
+
326
+ /*
327
+ export const Schema = {
328
+ input: z.strictObject({
329
+ session: z.string(),
330
+ profileId: z.number(),
331
+ action: z.enum(["add", "remove"]),
332
+ }),
333
+ output: z.strictObject({
334
+ error: z.string().optional(),
335
+ friend_count: z.number().optional(),
336
+ friend_state: friendStateSchema.optional(),
337
+ }),
338
+ };*/
339
+ import { Schema as EditProfileFriend } from "./api/editProfileFriend"
340
+ export { Schema as SchemaEditProfileFriend } from "./api/editProfileFriend"
341
+ export const editProfileFriend = handleApi({url:"/api/editProfileFriend",...EditProfileFriend})
342
+
324
343
  // ./api/enhancedSearch.ts API
325
344
 
326
345
  /*
@@ -373,18 +392,18 @@ export const executeAdminOperation = handleApi({url:"/api/executeAdminOperation"
373
392
  // ./api/getAvatarUploadUrl.ts API
374
393
 
375
394
  /*
376
- export const Schema = {
377
- input: z.strictObject({
378
- session: z.string(),
379
- contentLength: z.number(),
380
- contentType: z.string(),
381
- intrinsicToken: z.string(),
382
- }),
383
- output: z.strictObject({
384
- error: z.string().optional(),
385
- url: z.string().optional(),
386
- objectKey: z.string().optional(),
387
- }),
395
+ export const Schema = {
396
+ input: z.strictObject({
397
+ session: z.string(),
398
+ contentLength: z.number(),
399
+ contentType: z.string(),
400
+ intrinsicToken: z.string(),
401
+ }),
402
+ output: z.strictObject({
403
+ error: z.string().optional(),
404
+ url: z.string().optional(),
405
+ objectKey: z.string().optional(),
406
+ }),
388
407
  };*/
389
408
  import { Schema as GetAvatarUploadUrl } from "./api/getAvatarUploadUrl"
390
409
  export { Schema as SchemaGetAvatarUploadUrl } from "./api/getAvatarUploadUrl"
@@ -522,6 +541,7 @@ export const Schema = {
522
541
  description: z.string().nullable().optional(),
523
542
  tags: z.string().nullable().optional(),
524
543
  videoUrl: z.string().nullable().optional(),
544
+ mapHash: z.string().nullable().optional(),
525
545
  vetos: z
526
546
  .array(
527
547
  z.object({
@@ -594,6 +614,7 @@ export const Schema = {
594
614
  qualified: z.boolean().nullable().optional(),
595
615
  qualifiedAt: z.string().nullable().optional(),
596
616
  videoUrl: z.string().nullable(),
617
+ mapHash: z.string().nullable().optional(),
597
618
  vetos: z
598
619
  .array(
599
620
  z.object({
@@ -617,50 +638,51 @@ export const getBeatmapPageById = handleApi({url:"/api/getBeatmapPageById",...Ge
617
638
  // ./api/getBeatmaps.ts API
618
639
 
619
640
  /*
620
- export const Schema = {
621
- input: z.strictObject({
622
- session: z.string(),
623
- textFilter: z.string().optional(),
624
- authorFilter: z.string().optional(),
625
- tagsFilter: z.string().optional(),
626
- page: z.number().default(1),
627
- maxStars: z.number().optional(),
628
- minLength: z.number().optional(),
629
- maxLength: z.number().optional(),
630
- minStars: z.number().optional(),
631
- creator: z.number().optional(),
632
- status: z.string().optional(),
633
- }),
634
- output: z.object({
635
- error: z.string().optional(),
636
- total: z.number(),
637
- viewPerPage: z.number(),
638
- currentPage: z.number(),
639
- beatmaps: z
640
- .array(
641
- z.object({
642
- id: z.number(),
643
- playcount: z.number().nullable().optional(),
644
- created_at: z.string().nullable().optional(),
645
- difficulty: z.number().nullable().optional(),
646
- noteCount: z.number().nullable().optional(),
647
- length: z.number().nullable().optional(),
648
- title: z.string().nullable().optional(),
649
- ranked: z.boolean().nullable().optional(),
650
- beatmapFile: z.string().nullable().optional(),
651
- image: z.string().nullable().optional(),
652
- starRating: z.number().nullable().optional(),
653
- owner: z.number().nullable().optional(),
654
- ownerUsername: z.string().nullable().optional(),
655
- ownerAvatar: z.string().nullable().optional(),
656
- status: z.string().nullable().optional(),
657
- qualified: z.boolean().nullable().optional(),
658
- tags: z.string().nullable().optional(),
659
- videoUrl: z.string().nullable().optional(),
660
- })
661
- )
662
- .optional(),
663
- }),
641
+ export const Schema = {
642
+ input: z.strictObject({
643
+ session: z.string(),
644
+ textFilter: z.string().optional(),
645
+ authorFilter: z.string().optional(),
646
+ tagsFilter: z.string().optional(),
647
+ page: z.number().default(1),
648
+ maxStars: z.number().optional(),
649
+ minLength: z.number().optional(),
650
+ maxLength: z.number().optional(),
651
+ minStars: z.number().optional(),
652
+ creator: z.number().optional(),
653
+ status: z.string().optional(),
654
+ }),
655
+ output: z.object({
656
+ error: z.string().optional(),
657
+ total: z.number(),
658
+ viewPerPage: z.number(),
659
+ currentPage: z.number(),
660
+ beatmaps: z
661
+ .array(
662
+ z.object({
663
+ id: z.number(),
664
+ playcount: z.number().nullable().optional(),
665
+ created_at: z.string().nullable().optional(),
666
+ difficulty: z.number().nullable().optional(),
667
+ noteCount: z.number().nullable().optional(),
668
+ length: z.number().nullable().optional(),
669
+ title: z.string().nullable().optional(),
670
+ ranked: z.boolean().nullable().optional(),
671
+ beatmapFile: z.string().nullable().optional(),
672
+ image: z.string().nullable().optional(),
673
+ starRating: z.number().nullable().optional(),
674
+ owner: z.number().nullable().optional(),
675
+ ownerUsername: z.string().nullable().optional(),
676
+ ownerAvatar: z.string().nullable().optional(),
677
+ status: z.string().nullable().optional(),
678
+ qualified: z.boolean().nullable().optional(),
679
+ tags: z.string().nullable().optional(),
680
+ videoUrl: z.string().nullable().optional(),
681
+ mapHash: z.string().nullable().optional(),
682
+ })
683
+ )
684
+ .optional(),
685
+ }),
664
686
  };*/
665
687
  import { Schema as GetBeatmaps } from "./api/getBeatmaps"
666
688
  export { Schema as SchemaGetBeatmaps } from "./api/getBeatmaps"
@@ -764,19 +786,22 @@ export const Schema = {
764
786
  input: z.strictObject({
765
787
  session: z.string(),
766
788
  collection: z.number(),
789
+ page: z.number().optional().default(1),
790
+ itemsPerPage: z.number().optional().default(30),
767
791
  }),
768
792
  output: z.object({
769
- collection: z.object({
770
- title: z.string(),
771
- description: z.string(),
772
- owner: z.object({
773
- id: z.number(),
774
- username: z.string(),
775
- avatar_url: z.string().nullable(),
776
- }),
777
- isList: z.boolean(),
778
- beatmaps: z.array(
779
- z.object({
793
+ collection: z.object({
794
+ title: z.string(),
795
+ description: z.string(),
796
+ owner: z.object({
797
+ id: z.number(),
798
+ username: z.string(),
799
+ avatar_url: z.string().nullable(),
800
+ }),
801
+ isList: z.boolean(),
802
+ beatmapCount: z.number(),
803
+ beatmaps: z.array(
804
+ z.object({
780
805
  id: z.number(),
781
806
  playcount: z.number().nullable().optional(),
782
807
  created_at: z.string().nullable().optional(),
@@ -794,6 +819,7 @@ export const Schema = {
794
819
  })
795
820
  ),
796
821
  }),
822
+ totalPages: z.number(),
797
823
  error: z.string().optional(),
798
824
  }),
799
825
  };*/
@@ -840,6 +866,22 @@ import { Schema as GetCollections } from "./api/getCollections"
840
866
  export { Schema as SchemaGetCollections } from "./api/getCollections"
841
867
  export const getCollections = handleApi({url:"/api/getCollections",...GetCollections})
842
868
 
869
+ // ./api/getFriends.ts API
870
+
871
+ /*
872
+ export const Schema = {
873
+ input: z.strictObject({
874
+ session: z.string(),
875
+ }),
876
+ output: z.strictObject({
877
+ error: z.string().optional(),
878
+ friends: z.array(friendUserSchema),
879
+ }),
880
+ };*/
881
+ import { Schema as GetFriends } from "./api/getFriends"
882
+ export { Schema as SchemaGetFriends } from "./api/getFriends"
883
+ export const getFriends = handleApi({url:"/api/getFriends",...GetFriends})
884
+
843
885
  // ./api/getInventory.ts API
844
886
 
845
887
  /*
@@ -904,19 +946,19 @@ export const getLeaderboard = handleApi({url:"/api/getLeaderboard",...GetLeaderb
904
946
  // ./api/getMapUploadUrl.ts API
905
947
 
906
948
  /*
907
- export const Schema = {
908
- input: z.strictObject({
909
- mapName: z.string().optional(),
910
- session: z.string(),
911
- contentLength: z.number(),
912
- contentType: z.string(),
913
- intrinsicToken: z.string(),
914
- }),
915
- output: z.strictObject({
916
- error: z.string().optional(),
917
- url: z.string().optional(),
918
- objectKey: z.string().optional(),
919
- }),
949
+ export const Schema = {
950
+ input: z.strictObject({
951
+ mapName: z.string().optional(),
952
+ session: z.string(),
953
+ contentLength: z.number(),
954
+ contentType: z.string(),
955
+ intrinsicToken: z.string(),
956
+ }),
957
+ output: z.strictObject({
958
+ error: z.string().optional(),
959
+ url: z.string().optional(),
960
+ objectKey: z.string().optional(),
961
+ }),
920
962
  };*/
921
963
  import { Schema as GetMapUploadUrl } from "./api/getMapUploadUrl"
922
964
  export { Schema as SchemaGetMapUploadUrl } from "./api/getMapUploadUrl"
@@ -967,26 +1009,26 @@ export const getPassToken = handleApi({url:"/api/getPassToken",...GetPassToken})
967
1009
  /*
968
1010
  export const Schema = {
969
1011
  input: z.strictObject({
970
- session: z.string(),
971
- id: z.number().nullable().optional(),
972
- }),
973
- output: z.object({
974
- error: z.string().optional(),
975
- user: z
976
- .object({
977
- about_me: z.string().nullable(),
978
- avatar_url: z.string().nullable(),
979
- profile_image: z.string().nullable(),
980
- badges: z.any().nullable(),
981
- created_at: z.number().nullable(),
982
- flag: z.string().nullable(),
983
- id: z.number(),
984
- uid: z.string().nullable(),
985
- ban: z.string().nullable(),
986
- username: z.string().nullable(),
987
- verified: z.boolean().nullable(),
988
- verificationDeadline: z.number().nullable(),
989
- play_count: z.number().nullable(),
1012
+ session: z.string(),
1013
+ id: z.number().nullable().optional(),
1014
+ }),
1015
+ output: z.object({
1016
+ error: z.string().optional(),
1017
+ user: z
1018
+ .object({
1019
+ about_me: z.string().nullable(),
1020
+ avatar_url: z.string().nullable(),
1021
+ profile_image: z.string().nullable(),
1022
+ badges: z.any().nullable(),
1023
+ created_at: z.number().nullable(),
1024
+ flag: z.string().nullable(),
1025
+ id: z.number(),
1026
+ uid: z.string().nullable(),
1027
+ ban: z.string().nullable(),
1028
+ username: z.string().nullable(),
1029
+ verified: z.boolean().nullable(),
1030
+ verificationDeadline: z.number().nullable(),
1031
+ play_count: z.number().nullable(),
990
1032
  skill_points: z.number().nullable(),
991
1033
  squares_hit: z.number().nullable(),
992
1034
  total_score: z.number().nullable(),
@@ -995,6 +1037,8 @@ export const Schema = {
995
1037
  activity_status: z.enum(["active", "inactive"]),
996
1038
  is_online: z.boolean(),
997
1039
  last_active_timestamp: z.number().nullable(),
1040
+ friend_count: z.number(),
1041
+ friend_state: friendStateSchema,
998
1042
  can_change_flag: z.boolean(),
999
1043
  next_username_change_at: z.string().nullable(),
1000
1044
  previous_usernames: z.array(
@@ -1008,11 +1052,11 @@ export const Schema = {
1008
1052
  id: z.number(),
1009
1053
  acronym: z.string(),
1010
1054
  })
1011
- .optional()
1012
- .nullable(),
1013
- })
1014
- .optional(),
1015
- }),
1055
+ .optional()
1056
+ .nullable(),
1057
+ })
1058
+ .optional(),
1059
+ }),
1016
1060
  };*/
1017
1061
  import { Schema as GetProfile } from "./api/getProfile"
1018
1062
  export { Schema as SchemaGetProfile } from "./api/getProfile"
@@ -1124,6 +1168,7 @@ export const Schema = {
1124
1168
  username: z.string().optional().nullable(),
1125
1169
  speed: z.number().optional().nullable(),
1126
1170
  spin: z.boolean().optional().nullable(),
1171
+ replay_url: z.string().optional().nullable(),
1127
1172
  })
1128
1173
  .optional(),
1129
1174
  }),
@@ -1284,18 +1329,18 @@ export const getVerified = handleApi({url:"/api/getVerified",...GetVerified})
1284
1329
  // ./api/getVideoUploadUrl.ts API
1285
1330
 
1286
1331
  /*
1287
- export const Schema = {
1288
- input: z.strictObject({
1289
- session: z.string(),
1290
- contentLength: z.number(),
1291
- contentType: z.string(),
1292
- intrinsicToken: z.string(),
1293
- }),
1294
- output: z.strictObject({
1295
- error: z.string().optional(),
1296
- url: z.string().optional(),
1297
- objectKey: z.string().optional(),
1298
- }),
1332
+ export const Schema = {
1333
+ input: z.strictObject({
1334
+ session: z.string(),
1335
+ contentLength: z.number(),
1336
+ contentType: z.string(),
1337
+ intrinsicToken: z.string(),
1338
+ }),
1339
+ output: z.strictObject({
1340
+ error: z.string().optional(),
1341
+ url: z.string().optional(),
1342
+ objectKey: z.string().optional(),
1343
+ }),
1299
1344
  };*/
1300
1345
  import { Schema as GetVideoUploadUrl } from "./api/getVideoUploadUrl"
1301
1346
  export { Schema as SchemaGetVideoUploadUrl } from "./api/getVideoUploadUrl"
@@ -1442,26 +1487,27 @@ export const submitScore = handleApi({url:"/api/submitScore",...SubmitScore})
1442
1487
  // ./api/submitScoreInternal.ts API
1443
1488
 
1444
1489
  /*
1445
- export const Schema = {
1446
- input: z.strictObject({
1447
- session: z.string(),
1448
- secret: z.string(),
1449
- token: z.string(),
1450
- data: z.strictObject({
1451
- onlineMapId: z.number(),
1452
- misses: z.number(),
1453
- hits: z.number(),
1454
- speed: z.number(),
1455
- mods: z.array(z.string()),
1456
- spin: z.boolean(),
1457
- replayBytes: z.string().nullable().optional(),
1458
- pauses: z.number().nullable().optional(),
1459
- failTime: z.number().nullable().optional(),
1460
- }),
1461
- }),
1462
- output: z.object({
1463
- error: z.string().optional(),
1464
- }),
1490
+ export const Schema = {
1491
+ input: z.strictObject({
1492
+ session: z.string(),
1493
+ secret: z.string(),
1494
+ token: z.string(),
1495
+ data: z.strictObject({
1496
+ beatmapHash: z.string().optional().nullable(),
1497
+ onlineMapId: z.number(),
1498
+ misses: z.number(),
1499
+ hits: z.number(),
1500
+ speed: z.number(),
1501
+ mods: z.array(z.string()),
1502
+ spin: z.boolean(),
1503
+ replayBytes: z.string().nullable().optional(),
1504
+ pauses: z.number().nullable().optional(),
1505
+ failTime: z.number().nullable().optional(),
1506
+ }),
1507
+ }),
1508
+ output: z.object({
1509
+ error: z.string().optional(),
1510
+ }),
1465
1511
  };*/
1466
1512
  import { Schema as SubmitScoreInternal } from "./api/submitScoreInternal"
1467
1513
  export { Schema as SchemaSubmitScoreInternal } from "./api/submitScoreInternal"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhythia-api",
3
- "version": "241.0.0",
3
+ "version": "243.0.0",
4
4
  "main": "index.ts",
5
5
  "author": "online-contributors-cunev",
6
6
  "scripts": {
@@ -10,7 +10,10 @@
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:add-beatmap-page-map-hash": "node --experimental-transform-types scripts/add-beatmap-page-map-hash.ts",
14
+ "db:backfill-beatmap-page-map-hashes": "node --experimental-transform-types scripts/backfill-beatmap-page-map-hashes.ts",
13
15
  "db:create-profile-flags": "node scripts/create-profile-flags-table.ts",
16
+ "db:create-profile-friends": "node scripts/create-profile-friends-table.ts",
14
17
  "db:create-profile-reports": "node scripts/create-profile-reports-table.ts",
15
18
  "query-pull": "bun run scripts/pull-queries.ts",
16
19
  "query-push": "bun run scripts/deploy-queries.ts",
@@ -27,7 +30,7 @@
27
30
  "@supabase/supabase-js": "^2.45.1",
28
31
  "@types/bun": "^1.1.6",
29
32
  "@types/lodash": "^4.17.7",
30
- "@types/node": "^22.2.0",
33
+ "@types/node": "^25.6.0",
31
34
  "@types/pg": "^8.15.5",
32
35
  "@types/validator": "^13.12.2",
33
36
  "bad-words": "^4.0.0",
@@ -1,39 +1,42 @@
1
- CREATE OR REPLACE FUNCTION public.admin_delete_user(user_id integer)
2
- RETURNS boolean
3
- LANGUAGE plpgsql
4
- SECURITY DEFINER
5
- AS $function$
6
- DECLARE
7
- success BOOLEAN;
8
- BEGIN
9
- -- Delete scores first due to foreign key constraints
10
- DELETE FROM public.scores WHERE "userId" = user_id;
11
-
12
- -- Delete beatmapPageComments
13
- DELETE FROM public."beatmapPageComments" WHERE owner = user_id;
14
-
15
- -- Delete beatmapPages owned by user
16
- DELETE FROM public."beatmapPages" WHERE owner = user_id;
17
-
18
- -- Delete collections owned by user
19
- DELETE FROM public."beatmapCollections" WHERE owner = user_id;
20
-
21
- -- Delete collection relations related to user's collections
22
- -- (this is handled by cascade if you have it set up)
23
-
24
- -- Delete passkeys
25
- DELETE FROM public.passkeys WHERE id = user_id;
26
-
27
- -- Delete profile activity
28
- DELETE FROM public."profileActivities"
29
- WHERE uid = (SELECT uid FROM public.profiles WHERE id = user_id);
30
-
31
- -- Finally, delete the profile
32
- DELETE FROM public.profiles WHERE id = user_id;
33
-
34
- -- Check if deletion was successful
35
- success := NOT EXISTS (SELECT 1 FROM public.profiles WHERE id = user_id);
36
-
37
- RETURN success;
38
- END;
39
- $function$
1
+ CREATE OR REPLACE FUNCTION public.admin_delete_user(user_id integer)
2
+ RETURNS boolean
3
+ LANGUAGE plpgsql
4
+ SECURITY DEFINER
5
+ AS $function$
6
+ DECLARE
7
+ success BOOLEAN;
8
+ BEGIN
9
+ -- Delete scores first due to foreign key constraints
10
+ DELETE FROM public.scores WHERE "userId" = user_id;
11
+
12
+ -- Delete beatmapPageComments
13
+ DELETE FROM public."beatmapPageComments" WHERE owner = user_id;
14
+
15
+ -- Delete beatmapPages owned by user
16
+ DELETE FROM public."beatmapPages" WHERE owner = user_id;
17
+
18
+ -- Delete collections owned by user
19
+ DELETE FROM public."beatmapCollections" WHERE owner = user_id;
20
+
21
+ -- Delete collection relations related to user's collections
22
+ -- (this is handled by cascade if you have it set up)
23
+
24
+ -- Delete passkeys
25
+ DELETE FROM public.passkeys WHERE id = user_id;
26
+
27
+ -- Delete user HWID records
28
+ DELETE FROM public.user_hwids WHERE id = user_id;
29
+
30
+ -- Delete profile activity
31
+ DELETE FROM public."profileActivities"
32
+ WHERE uid = (SELECT uid FROM public.profiles WHERE id = user_id);
33
+
34
+ -- Finally, delete the profile
35
+ DELETE FROM public.profiles WHERE id = user_id;
36
+
37
+ -- Check if deletion was successful
38
+ success := NOT EXISTS (SELECT 1 FROM public.profiles WHERE id = user_id);
39
+
40
+ RETURN success;
41
+ END;
42
+ $function$
@@ -5,9 +5,12 @@ CREATE OR REPLACE FUNCTION public.admin_remove_all_scores(user_id integer)
5
5
  AS $function$
6
6
  DECLARE
7
7
  deleted_count INTEGER;
8
- BEGIN
9
- -- Delete all scores for this user and count them
10
- WITH deleted AS (
8
+ BEGIN
9
+ DELETE FROM public.leaderboard_map_user
10
+ WHERE "userId" = user_id;
11
+
12
+ -- Delete all scores for this user and count them
13
+ WITH deleted AS (
11
14
  DELETE FROM public.scores
12
15
  WHERE "userId" = user_id
13
16
  RETURNING *