rhythia-api 183.0.0 → 184.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.
Files changed (58) hide show
  1. package/.prettierrc.json +6 -6
  2. package/api/addCollectionMap.ts +82 -82
  3. package/api/approveMap.ts +78 -78
  4. package/api/chartPublicStats.ts +32 -32
  5. package/api/createBeatmap.ts +168 -168
  6. package/api/createBeatmapPage.ts +64 -64
  7. package/api/createClan.ts +81 -81
  8. package/api/createCollection.ts +58 -58
  9. package/api/deleteBeatmapPage.ts +77 -77
  10. package/api/deleteCollection.ts +59 -59
  11. package/api/deleteCollectionMap.ts +71 -71
  12. package/api/editAboutMe.ts +91 -91
  13. package/api/editClan.ts +90 -90
  14. package/api/editCollection.ts +77 -77
  15. package/api/editProfile.ts +123 -123
  16. package/api/getAvatarUploadUrl.ts +85 -85
  17. package/api/getBadgedUsers.ts +56 -56
  18. package/api/getBeatmapComments.ts +57 -57
  19. package/api/getBeatmapPage.ts +106 -106
  20. package/api/getBeatmapPageById.ts +99 -99
  21. package/api/getBeatmapStarRating.ts +53 -53
  22. package/api/getBeatmaps.ts +159 -159
  23. package/api/getClan.ts +77 -77
  24. package/api/getCollection.ts +130 -130
  25. package/api/getCollections.ts +126 -128
  26. package/api/getLeaderboard.ts +136 -136
  27. package/api/getMapUploadUrl.ts +93 -93
  28. package/api/getPassToken.ts +55 -55
  29. package/api/getProfile.ts +146 -146
  30. package/api/getPublicStats.ts +180 -180
  31. package/api/getRawStarRating.ts +57 -57
  32. package/api/getScore.ts +85 -85
  33. package/api/getTimestamp.ts +23 -23
  34. package/api/getUserScores.ts +175 -175
  35. package/api/nominateMap.ts +82 -82
  36. package/api/postBeatmapComment.ts +59 -59
  37. package/api/rankMapsArchive.ts +64 -64
  38. package/api/searchUsers.ts +56 -56
  39. package/api/setPasskey.ts +59 -59
  40. package/api/submitScore.ts +433 -433
  41. package/api/updateBeatmapPage.ts +229 -229
  42. package/handleApi.ts +22 -20
  43. package/index.html +2 -2
  44. package/index.ts +864 -865
  45. package/package-lock.json +8913 -0
  46. package/package.json +2 -2
  47. package/types/database.ts +0 -39
  48. package/utils/getUserBySession.ts +48 -48
  49. package/utils/requestUtils.ts +87 -87
  50. package/utils/security.ts +20 -20
  51. package/utils/star-calc/index.ts +72 -72
  52. package/utils/star-calc/osuUtils.ts +53 -53
  53. package/utils/star-calc/sspmParser.ts +398 -398
  54. package/utils/star-calc/sspmv1Parser.ts +165 -165
  55. package/utils/supabase.ts +13 -13
  56. package/utils/test +4 -4
  57. package/utils/validateToken.ts +7 -7
  58. package/vercel.json +12 -12
@@ -1,130 +1,130 @@
1
- import { NextResponse } from "next/server";
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
- collection: z.number(),
10
- }),
11
- output: z.object({
12
- collection: z.object({
13
- title: z.string(),
14
- description: z.string(),
15
- owner: z.object({
16
- id: z.number(),
17
- username: z.string(),
18
- }),
19
- isList: z.boolean(),
20
- beatmaps: z.array(
21
- z.object({
22
- id: z.number(),
23
- playcount: z.number().nullable().optional(),
24
- created_at: z.string().nullable().optional(),
25
- difficulty: z.number().nullable().optional(),
26
- length: z.number().nullable().optional(),
27
- title: z.string().nullable().optional(),
28
- ranked: z.boolean().nullable().optional(),
29
- beatmapFile: z.string().nullable().optional(),
30
- image: z.string().nullable().optional(),
31
- starRating: z.number().nullable().optional(),
32
- owner: z.number().nullable().optional(),
33
- ownerUsername: z.string().nullable().optional(),
34
- status: z.string().nullable().optional(),
35
- tags: z.string().nullable().optional(),
36
- })
37
- ),
38
- }),
39
- error: z.string().optional(),
40
- }),
41
- };
42
-
43
- export async function POST(request: Request) {
44
- return protectedApi({
45
- request,
46
- schema: Schema,
47
- authorization: () => {},
48
- activity: handler,
49
- });
50
- }
51
-
52
- export async function handler(data: (typeof Schema)["input"]["_type"]) {
53
- let { data: queryCollectionData, error: collectionError } = await supabase
54
- .from("beatmapCollections")
55
- .select(
56
- `
57
- *,
58
- profiles!inner(
59
- id,
60
- username
61
- )
62
- `
63
- )
64
- .eq("id", data.collection)
65
- .single();
66
-
67
- if (!queryCollectionData) {
68
- return NextResponse.json({ error: "Can't find collection" });
69
- }
70
-
71
- let { data: queryBeatmaps, error: beatmapsError } = await supabase
72
- .from("collectionRelations")
73
- .select(
74
- `
75
- *,
76
- beatmapPages!inner(
77
- owner,
78
- created_at,
79
- id,
80
- status,
81
- tags,
82
- beatmaps!inner(
83
- playcount,
84
- ranked,
85
- beatmapFile,
86
- image,
87
- starRating,
88
- difficulty,
89
- length,
90
- title
91
- ),
92
- profiles!inner(
93
- username
94
- )
95
- )
96
- `
97
- )
98
- .eq("collection", data.collection);
99
-
100
- const formattedBeatmaps =
101
- queryBeatmaps?.map((relation) => ({
102
- id: relation.beatmapPages.id,
103
- playcount: relation.beatmapPages.beatmaps.playcount,
104
- created_at: relation.beatmapPages.created_at,
105
- difficulty: relation.beatmapPages.beatmaps.difficulty,
106
- length: relation.beatmapPages.beatmaps.length,
107
- title: relation.beatmapPages.beatmaps.title,
108
- ranked: relation.beatmapPages.beatmaps.ranked,
109
- beatmapFile: relation.beatmapPages.beatmaps.beatmapFile,
110
- image: relation.beatmapPages.beatmaps.image,
111
- starRating: relation.beatmapPages.beatmaps.starRating,
112
- owner: relation.beatmapPages.owner,
113
- ownerUsername: relation.beatmapPages.profiles.username,
114
- status: relation.beatmapPages.status,
115
- tags: relation.beatmapPages.tags,
116
- })) || [];
117
-
118
- return NextResponse.json({
119
- collection: {
120
- owner: {
121
- username: queryCollectionData.profiles.username,
122
- id: queryCollectionData.profiles.id,
123
- },
124
- isList: queryCollectionData.is_list,
125
- title: queryCollectionData.title,
126
- description: queryCollectionData.description,
127
- beatmaps: formattedBeatmaps,
128
- },
129
- });
130
- }
1
+ import { NextResponse } from "next/server";
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
+ collection: z.number(),
10
+ }),
11
+ output: z.object({
12
+ collection: z.object({
13
+ title: z.string(),
14
+ description: z.string(),
15
+ owner: z.object({
16
+ id: z.number(),
17
+ username: z.string(),
18
+ }),
19
+ isList: z.boolean(),
20
+ beatmaps: z.array(
21
+ z.object({
22
+ id: z.number(),
23
+ playcount: z.number().nullable().optional(),
24
+ created_at: z.string().nullable().optional(),
25
+ difficulty: z.number().nullable().optional(),
26
+ length: z.number().nullable().optional(),
27
+ title: z.string().nullable().optional(),
28
+ ranked: z.boolean().nullable().optional(),
29
+ beatmapFile: z.string().nullable().optional(),
30
+ image: z.string().nullable().optional(),
31
+ starRating: z.number().nullable().optional(),
32
+ owner: z.number().nullable().optional(),
33
+ ownerUsername: z.string().nullable().optional(),
34
+ status: z.string().nullable().optional(),
35
+ tags: z.string().nullable().optional(),
36
+ })
37
+ ),
38
+ }),
39
+ error: z.string().optional(),
40
+ }),
41
+ };
42
+
43
+ export async function POST(request: Request) {
44
+ return protectedApi({
45
+ request,
46
+ schema: Schema,
47
+ authorization: () => {},
48
+ activity: handler,
49
+ });
50
+ }
51
+
52
+ export async function handler(data: (typeof Schema)["input"]["_type"]) {
53
+ let { data: queryCollectionData, error: collectionError } = await supabase
54
+ .from("beatmapCollections")
55
+ .select(
56
+ `
57
+ *,
58
+ profiles!inner(
59
+ id,
60
+ username
61
+ )
62
+ `
63
+ )
64
+ .eq("id", data.collection)
65
+ .single();
66
+
67
+ if (!queryCollectionData) {
68
+ return NextResponse.json({ error: "Can't find collection" });
69
+ }
70
+
71
+ let { data: queryBeatmaps, error: beatmapsError } = await supabase
72
+ .from("collectionRelations")
73
+ .select(
74
+ `
75
+ *,
76
+ beatmapPages!inner(
77
+ owner,
78
+ created_at,
79
+ id,
80
+ status,
81
+ tags,
82
+ beatmaps!inner(
83
+ playcount,
84
+ ranked,
85
+ beatmapFile,
86
+ image,
87
+ starRating,
88
+ difficulty,
89
+ length,
90
+ title
91
+ ),
92
+ profiles!inner(
93
+ username
94
+ )
95
+ )
96
+ `
97
+ )
98
+ .eq("collection", data.collection);
99
+
100
+ const formattedBeatmaps =
101
+ queryBeatmaps?.map((relation) => ({
102
+ id: relation.beatmapPages.id,
103
+ playcount: relation.beatmapPages.beatmaps.playcount,
104
+ created_at: relation.beatmapPages.created_at,
105
+ difficulty: relation.beatmapPages.beatmaps.difficulty,
106
+ length: relation.beatmapPages.beatmaps.length,
107
+ title: relation.beatmapPages.beatmaps.title,
108
+ ranked: relation.beatmapPages.beatmaps.ranked,
109
+ beatmapFile: relation.beatmapPages.beatmaps.beatmapFile,
110
+ image: relation.beatmapPages.beatmaps.image,
111
+ starRating: relation.beatmapPages.beatmaps.starRating,
112
+ owner: relation.beatmapPages.owner,
113
+ ownerUsername: relation.beatmapPages.profiles.username,
114
+ status: relation.beatmapPages.status,
115
+ tags: relation.beatmapPages.tags,
116
+ })) || [];
117
+
118
+ return NextResponse.json({
119
+ collection: {
120
+ owner: {
121
+ username: queryCollectionData.profiles.username,
122
+ id: queryCollectionData.profiles.id,
123
+ },
124
+ isList: queryCollectionData.is_list,
125
+ title: queryCollectionData.title,
126
+ description: queryCollectionData.description,
127
+ beatmaps: formattedBeatmaps,
128
+ },
129
+ });
130
+ }
@@ -1,128 +1,126 @@
1
- import { NextResponse } from "next/server";
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
- page: z.number().optional().default(1),
10
- itemsPerPage: z.number().optional().default(10),
11
- owner: z.number().optional(), // Added owner field
12
- }),
13
- output: z.object({
14
- collections: z.array(
15
- z.object({
16
- id: z.number(),
17
- title: z.string(),
18
- description: z.string(),
19
- owner: z.number(),
20
- ownerUsername: z.string(),
21
- ownerAvatarUrl: z.string(),
22
- beatmapCount: z.number(),
23
- starRatingDistribution: z.array(
24
- z.object({
25
- stars: z.number(),
26
- count: z.number(),
27
- })
28
- ),
29
- createdAt: z.string(),
30
- })
31
- ),
32
- totalPages: z.number(),
33
- error: z.string().optional(),
34
- }),
35
- };
36
-
37
- export async function POST(request: Request) {
38
- return protectedApi({
39
- request,
40
- schema: Schema,
41
- authorization: () => {},
42
- activity: handler,
43
- });
44
- }
45
-
46
- export async function handler(data: (typeof Schema)["input"]["_type"]) {
47
- const { data: collections, error } = await supabase
48
- .rpc("get_collections_v3", {
49
- page_number: data.page,
50
- items_per_page: data.itemsPerPage,
51
- owner_filter: data.owner || undefined, // Added owner_filter parameter
52
- })
53
- .returns<
54
- {
55
- id: number;
56
- title: string;
57
- description: string;
58
- created_at: string;
59
- owner: number;
60
- owner_username: string;
61
- owner_avatar_url: string;
62
- beatmap_count: number;
63
- star1: number;
64
- star2: number;
65
- star3: number;
66
- star4: number;
67
- star5: number;
68
- star6: number;
69
- star7: number;
70
- star8: number;
71
- star9: number;
72
- star10: number;
73
- star11: number;
74
- star12: number;
75
- star13: number;
76
- star14: number;
77
- star15: number;
78
- star16: number;
79
- star17: number;
80
- star18: number;
81
- total_pages: number;
82
- }[]
83
- >();
84
-
85
- if (error) {
86
- return NextResponse.json({ error });
87
- }
88
-
89
- // Get the total pages from the first row (all rows will have the same value)
90
- const totalPages = collections?.[0]?.total_pages ?? 1;
91
-
92
- const formattedCollections =
93
- collections?.map((collection) => ({
94
- id: collection.id,
95
- title: collection.title,
96
- description: collection.description,
97
- owner: collection.owner,
98
- ownerUsername: collection.owner_username,
99
- ownerAvatarUrl: collection.owner_avatar_url,
100
- beatmapCount: collection.beatmap_count,
101
- starRatingDistribution: [
102
- { stars: 1, count: collection.star1 },
103
- { stars: 2, count: collection.star2 },
104
- { stars: 3, count: collection.star3 },
105
- { stars: 4, count: collection.star4 },
106
- { stars: 5, count: collection.star5 },
107
- { stars: 6, count: collection.star6 },
108
- { stars: 7, count: collection.star7 },
109
- { stars: 8, count: collection.star8 },
110
- { stars: 9, count: collection.star9 },
111
- { stars: 10, count: collection.star10 },
112
- { stars: 11, count: collection.star11 },
113
- { stars: 12, count: collection.star12 },
114
- { stars: 13, count: collection.star13 },
115
- { stars: 14, count: collection.star14 },
116
- { stars: 15, count: collection.star15 },
117
- { stars: 16, count: collection.star16 },
118
- { stars: 17, count: collection.star17 },
119
- { stars: 18, count: collection.star18 },
120
- ],
121
- createdAt: collection.created_at,
122
- })) || [];
123
-
124
- return NextResponse.json({
125
- collections: formattedCollections,
126
- totalPages,
127
- });
128
- }
1
+ import { NextResponse } from "next/server";
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
+ page: z.number().optional().default(1),
10
+ itemsPerPage: z.number().optional().default(10),
11
+ }),
12
+ output: z.object({
13
+ collections: z.array(
14
+ z.object({
15
+ id: z.number(),
16
+ title: z.string(),
17
+ description: z.string(),
18
+ owner: z.number(),
19
+ ownerUsername: z.string(),
20
+ ownerAvatarUrl: z.string(),
21
+ beatmapCount: z.number(),
22
+ starRatingDistribution: z.array(
23
+ z.object({
24
+ stars: z.number(),
25
+ count: z.number(),
26
+ })
27
+ ),
28
+ createdAt: z.string(),
29
+ })
30
+ ),
31
+ totalPages: z.number(),
32
+ error: z.string().optional(),
33
+ }),
34
+ };
35
+
36
+ export async function POST(request: Request) {
37
+ return protectedApi({
38
+ request,
39
+ schema: Schema,
40
+ authorization: () => {},
41
+ activity: handler,
42
+ });
43
+ }
44
+
45
+ export async function handler(data: (typeof Schema)["input"]["_type"]) {
46
+ const { data: collections, error } = await supabase
47
+ .rpc("get_collections_v2", {
48
+ page_number: data.page,
49
+ items_per_page: data.itemsPerPage,
50
+ })
51
+ .returns<
52
+ {
53
+ id: number;
54
+ title: string;
55
+ description: string;
56
+ created_at: string;
57
+ owner: number;
58
+ owner_username: string;
59
+ owner_avatar_url: string;
60
+ beatmap_count: number;
61
+ star1: number;
62
+ star2: number;
63
+ star3: number;
64
+ star4: number;
65
+ star5: number;
66
+ star6: number;
67
+ star7: number;
68
+ star8: number;
69
+ star9: number;
70
+ star10: number;
71
+ star11: number;
72
+ star12: number;
73
+ star13: number;
74
+ star14: number;
75
+ star15: number;
76
+ star16: number;
77
+ star17: number;
78
+ star18: number;
79
+ total_pages: number;
80
+ }[]
81
+ >();
82
+
83
+ if (error) {
84
+ return NextResponse.json({ error });
85
+ }
86
+
87
+ // Get the total pages from the first row (all rows will have the same value)
88
+ const totalPages = collections?.[0]?.total_pages ?? 1;
89
+
90
+ const formattedCollections =
91
+ collections?.map((collection) => ({
92
+ id: collection.id,
93
+ title: collection.title,
94
+ description: collection.description,
95
+ owner: collection.owner,
96
+ ownerUsername: collection.owner_username,
97
+ ownerAvatarUrl: collection.owner_avatar_url,
98
+ beatmapCount: collection.beatmap_count,
99
+ starRatingDistribution: [
100
+ { stars: 1, count: collection.star1 },
101
+ { stars: 2, count: collection.star2 },
102
+ { stars: 3, count: collection.star3 },
103
+ { stars: 4, count: collection.star4 },
104
+ { stars: 5, count: collection.star5 },
105
+ { stars: 6, count: collection.star6 },
106
+ { stars: 7, count: collection.star7 },
107
+ { stars: 8, count: collection.star8 },
108
+ { stars: 9, count: collection.star9 },
109
+ { stars: 10, count: collection.star10 },
110
+ { stars: 11, count: collection.star11 },
111
+ { stars: 12, count: collection.star12 },
112
+ { stars: 13, count: collection.star13 },
113
+ { stars: 14, count: collection.star14 },
114
+ { stars: 15, count: collection.star15 },
115
+ { stars: 16, count: collection.star16 },
116
+ { stars: 17, count: collection.star17 },
117
+ { stars: 18, count: collection.star18 },
118
+ ],
119
+ createdAt: collection.created_at,
120
+ })) || [];
121
+
122
+ return NextResponse.json({
123
+ collections: formattedCollections,
124
+ totalPages,
125
+ });
126
+ }