rhythia-api 185.0.0 → 187.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 (57) 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 +132 -130
  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 +21 -20
  43. package/index.html +2 -2
  44. package/index.ts +867 -866
  45. package/package.json +1 -1
  46. package/types/database.ts +800 -798
  47. package/utils/getUserBySession.ts +48 -48
  48. package/utils/requestUtils.ts +87 -87
  49. package/utils/security.ts +20 -20
  50. package/utils/star-calc/index.ts +72 -72
  51. package/utils/star-calc/osuUtils.ts +53 -53
  52. package/utils/star-calc/sspmParser.ts +398 -398
  53. package/utils/star-calc/sspmv1Parser.ts +165 -165
  54. package/utils/supabase.ts +13 -13
  55. package/utils/test +4 -4
  56. package/utils/validateToken.ts +7 -7
  57. package/vercel.json +12 -12
@@ -1,159 +1,159 @@
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
- textFilter: z.string().optional(),
10
- authorFilter: z.string().optional(),
11
- tagsFilter: z.string().optional(),
12
- page: z.number().default(1),
13
- maxStars: z.number().optional(),
14
- minLength: z.number().optional(),
15
- maxLength: z.number().optional(),
16
- minStars: z.number().optional(),
17
- creator: z.number().optional(),
18
- status: z.string().optional(),
19
- }),
20
- output: z.object({
21
- error: z.string().optional(),
22
- total: z.number(),
23
- viewPerPage: z.number(),
24
- currentPage: z.number(),
25
- beatmaps: z
26
- .array(
27
- z.object({
28
- id: z.number(),
29
- playcount: z.number().nullable().optional(),
30
- created_at: z.string().nullable().optional(),
31
- difficulty: z.number().nullable().optional(),
32
- noteCount: z.number().nullable().optional(),
33
- length: z.number().nullable().optional(),
34
- title: z.string().nullable().optional(),
35
- ranked: z.boolean().nullable().optional(),
36
- beatmapFile: z.string().nullable().optional(),
37
- image: z.string().nullable().optional(),
38
- starRating: z.number().nullable().optional(),
39
- owner: z.number().nullable().optional(),
40
- ownerUsername: z.string().nullable().optional(),
41
- ownerAvatar: z.string().nullable().optional(),
42
- status: z.string().nullable().optional(),
43
- tags: z.string().nullable().optional(),
44
- })
45
- )
46
- .optional(),
47
- }),
48
- };
49
-
50
- export async function POST(request: Request): Promise<NextResponse> {
51
- return protectedApi({
52
- request,
53
- schema: Schema,
54
- authorization: () => {},
55
- activity: handler,
56
- });
57
- }
58
-
59
- export async function handler(
60
- data: (typeof Schema)["input"]["_type"]
61
- ): Promise<NextResponse<(typeof Schema)["output"]["_type"]>> {
62
- const result = await getBeatmaps(data);
63
- return NextResponse.json(result);
64
- }
65
-
66
- const VIEW_PER_PAGE = 50;
67
-
68
- export async function getBeatmaps(data: (typeof Schema)["input"]["_type"]) {
69
- const startPage = (data.page - 1) * VIEW_PER_PAGE;
70
- const endPage = startPage + VIEW_PER_PAGE - 1;
71
- const countQuery = await supabase
72
- .from("beatmapPages")
73
- .select("id", { count: "exact", head: true });
74
-
75
- let qry = supabase
76
- .from("beatmapPages")
77
- .select(
78
- `
79
- owner,
80
- created_at,
81
- id,
82
- status,
83
- tags,
84
- beatmaps!inner(
85
- playcount,
86
- ranked,
87
- beatmapFile,
88
- image,
89
- starRating,
90
- difficulty,
91
- length,
92
- title
93
- ),
94
- profiles!inner(
95
- username
96
- )`
97
- )
98
- .order("created_at", { ascending: false });
99
-
100
- if (data.textFilter) {
101
- qry = qry.ilike("beatmaps.title", `%${data.textFilter}%`);
102
- }
103
-
104
- if (data.authorFilter) {
105
- qry = qry.ilike("profiles.username", `%${data.authorFilter}%`);
106
- }
107
-
108
- if (data.tagsFilter) {
109
- qry = qry.ilike("tags", `%${data.tagsFilter}%`);
110
- }
111
-
112
- if (data.minStars) {
113
- qry = qry.gt("beatmaps.starRating", data.minStars);
114
- }
115
-
116
- if (data.maxStars) {
117
- qry = qry.lt("beatmaps.starRating", data.maxStars);
118
- }
119
-
120
- if (data.minLength) {
121
- qry = qry.gt("beatmaps.length", data.minLength);
122
- }
123
-
124
- if (data.maxLength) {
125
- qry = qry.lt("beatmaps.length", data.maxLength);
126
- }
127
-
128
- if (data.status) {
129
- qry = qry.eq("status", data.status);
130
- }
131
-
132
- if (data.creator !== undefined) {
133
- qry = qry.eq("owner", data.creator);
134
- }
135
-
136
- let queryData = await qry.range(startPage, endPage);
137
-
138
- return {
139
- total: countQuery.count || 0,
140
- viewPerPage: VIEW_PER_PAGE,
141
- currentPage: data.page,
142
- beatmaps: queryData.data?.map((beatmapPage) => ({
143
- id: beatmapPage.id,
144
- tags: beatmapPage.tags,
145
- playcount: beatmapPage.beatmaps?.playcount,
146
- created_at: beatmapPage.created_at,
147
- difficulty: beatmapPage.beatmaps?.difficulty,
148
- title: beatmapPage.beatmaps?.title,
149
- ranked: beatmapPage.beatmaps?.ranked,
150
- length: beatmapPage.beatmaps?.length,
151
- beatmapFile: beatmapPage.beatmaps?.beatmapFile,
152
- image: beatmapPage.beatmaps?.image,
153
- starRating: beatmapPage.beatmaps?.starRating,
154
- owner: beatmapPage.owner,
155
- status: beatmapPage.status,
156
- ownerUsername: beatmapPage.profiles?.username,
157
- })),
158
- };
159
- }
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
+ textFilter: z.string().optional(),
10
+ authorFilter: z.string().optional(),
11
+ tagsFilter: z.string().optional(),
12
+ page: z.number().default(1),
13
+ maxStars: z.number().optional(),
14
+ minLength: z.number().optional(),
15
+ maxLength: z.number().optional(),
16
+ minStars: z.number().optional(),
17
+ creator: z.number().optional(),
18
+ status: z.string().optional(),
19
+ }),
20
+ output: z.object({
21
+ error: z.string().optional(),
22
+ total: z.number(),
23
+ viewPerPage: z.number(),
24
+ currentPage: z.number(),
25
+ beatmaps: z
26
+ .array(
27
+ z.object({
28
+ id: z.number(),
29
+ playcount: z.number().nullable().optional(),
30
+ created_at: z.string().nullable().optional(),
31
+ difficulty: z.number().nullable().optional(),
32
+ noteCount: z.number().nullable().optional(),
33
+ length: z.number().nullable().optional(),
34
+ title: z.string().nullable().optional(),
35
+ ranked: z.boolean().nullable().optional(),
36
+ beatmapFile: z.string().nullable().optional(),
37
+ image: z.string().nullable().optional(),
38
+ starRating: z.number().nullable().optional(),
39
+ owner: z.number().nullable().optional(),
40
+ ownerUsername: z.string().nullable().optional(),
41
+ ownerAvatar: z.string().nullable().optional(),
42
+ status: z.string().nullable().optional(),
43
+ tags: z.string().nullable().optional(),
44
+ })
45
+ )
46
+ .optional(),
47
+ }),
48
+ };
49
+
50
+ export async function POST(request: Request): Promise<NextResponse> {
51
+ return protectedApi({
52
+ request,
53
+ schema: Schema,
54
+ authorization: () => {},
55
+ activity: handler,
56
+ });
57
+ }
58
+
59
+ export async function handler(
60
+ data: (typeof Schema)["input"]["_type"]
61
+ ): Promise<NextResponse<(typeof Schema)["output"]["_type"]>> {
62
+ const result = await getBeatmaps(data);
63
+ return NextResponse.json(result);
64
+ }
65
+
66
+ const VIEW_PER_PAGE = 50;
67
+
68
+ export async function getBeatmaps(data: (typeof Schema)["input"]["_type"]) {
69
+ const startPage = (data.page - 1) * VIEW_PER_PAGE;
70
+ const endPage = startPage + VIEW_PER_PAGE - 1;
71
+ const countQuery = await supabase
72
+ .from("beatmapPages")
73
+ .select("id", { count: "exact", head: true });
74
+
75
+ let qry = supabase
76
+ .from("beatmapPages")
77
+ .select(
78
+ `
79
+ owner,
80
+ created_at,
81
+ id,
82
+ status,
83
+ tags,
84
+ beatmaps!inner(
85
+ playcount,
86
+ ranked,
87
+ beatmapFile,
88
+ image,
89
+ starRating,
90
+ difficulty,
91
+ length,
92
+ title
93
+ ),
94
+ profiles!inner(
95
+ username
96
+ )`
97
+ )
98
+ .order("created_at", { ascending: false });
99
+
100
+ if (data.textFilter) {
101
+ qry = qry.ilike("beatmaps.title", `%${data.textFilter}%`);
102
+ }
103
+
104
+ if (data.authorFilter) {
105
+ qry = qry.ilike("profiles.username", `%${data.authorFilter}%`);
106
+ }
107
+
108
+ if (data.tagsFilter) {
109
+ qry = qry.ilike("tags", `%${data.tagsFilter}%`);
110
+ }
111
+
112
+ if (data.minStars) {
113
+ qry = qry.gt("beatmaps.starRating", data.minStars);
114
+ }
115
+
116
+ if (data.maxStars) {
117
+ qry = qry.lt("beatmaps.starRating", data.maxStars);
118
+ }
119
+
120
+ if (data.minLength) {
121
+ qry = qry.gt("beatmaps.length", data.minLength);
122
+ }
123
+
124
+ if (data.maxLength) {
125
+ qry = qry.lt("beatmaps.length", data.maxLength);
126
+ }
127
+
128
+ if (data.status) {
129
+ qry = qry.eq("status", data.status);
130
+ }
131
+
132
+ if (data.creator !== undefined) {
133
+ qry = qry.eq("owner", data.creator);
134
+ }
135
+
136
+ let queryData = await qry.range(startPage, endPage);
137
+
138
+ return {
139
+ total: countQuery.count || 0,
140
+ viewPerPage: VIEW_PER_PAGE,
141
+ currentPage: data.page,
142
+ beatmaps: queryData.data?.map((beatmapPage) => ({
143
+ id: beatmapPage.id,
144
+ tags: beatmapPage.tags,
145
+ playcount: beatmapPage.beatmaps?.playcount,
146
+ created_at: beatmapPage.created_at,
147
+ difficulty: beatmapPage.beatmaps?.difficulty,
148
+ title: beatmapPage.beatmaps?.title,
149
+ ranked: beatmapPage.beatmaps?.ranked,
150
+ length: beatmapPage.beatmaps?.length,
151
+ beatmapFile: beatmapPage.beatmaps?.beatmapFile,
152
+ image: beatmapPage.beatmaps?.image,
153
+ starRating: beatmapPage.beatmaps?.starRating,
154
+ owner: beatmapPage.owner,
155
+ status: beatmapPage.status,
156
+ ownerUsername: beatmapPage.profiles?.username,
157
+ })),
158
+ };
159
+ }
package/api/getClan.ts CHANGED
@@ -1,77 +1,77 @@
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
- id: z.number(),
10
- }),
11
- output: z.object({
12
- error: z.string().optional(),
13
- acronym: z.string(),
14
- avatar_url: z.string(),
15
- created_at: z.number(),
16
- description: z.string(),
17
- id: z.number(),
18
- name: z.string(),
19
- owner: z.number(),
20
- users: z.array(
21
- z.object({
22
- about_me: z.string().nullable(),
23
- avatar_url: z.string().nullable(),
24
- profile_image: z.string().nullable(),
25
- badges: z.any().nullable(),
26
- created_at: z.number().nullable(),
27
- flag: z.string().nullable(),
28
- id: z.number(),
29
- uid: z.string().nullable(),
30
- ban: z.string().nullable(),
31
- username: z.string().nullable(),
32
- verified: z.boolean().nullable(),
33
- play_count: z.number().nullable(),
34
- skill_points: z.number().nullable(),
35
- squares_hit: z.number().nullable(),
36
- total_score: z.number().nullable(),
37
- })
38
- ),
39
- }),
40
- };
41
-
42
- export async function POST(request: Request) {
43
- return protectedApi({
44
- request,
45
- schema: Schema,
46
- authorization: () => {},
47
- activity: handler,
48
- });
49
- }
50
-
51
- export async function handler(data: (typeof Schema)["input"]["_type"]) {
52
- let { data: queryClanData, error: clanError } = await supabase
53
- .from("clans")
54
- .select("*")
55
- .eq("id", data.id)
56
- .single();
57
-
58
- if (!queryClanData) {
59
- return NextResponse.json({ error: "No such clan ID" });
60
- }
61
-
62
- let { data: queryUsers, error: usersError } = await supabase
63
- .from("profiles")
64
- .select("*")
65
- .eq("clan", queryClanData.id);
66
-
67
- return NextResponse.json({
68
- acronym: queryClanData.acronym,
69
- avatar_url: queryClanData.avatar_url,
70
- created_at: queryClanData.created_at,
71
- description: queryClanData.description,
72
- id: queryClanData.id,
73
- name: queryClanData.name,
74
- owner: queryClanData.owner,
75
- users: queryUsers,
76
- });
77
- }
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
+ id: z.number(),
10
+ }),
11
+ output: z.object({
12
+ error: z.string().optional(),
13
+ acronym: z.string(),
14
+ avatar_url: z.string(),
15
+ created_at: z.number(),
16
+ description: z.string(),
17
+ id: z.number(),
18
+ name: z.string(),
19
+ owner: z.number(),
20
+ users: z.array(
21
+ z.object({
22
+ about_me: z.string().nullable(),
23
+ avatar_url: z.string().nullable(),
24
+ profile_image: z.string().nullable(),
25
+ badges: z.any().nullable(),
26
+ created_at: z.number().nullable(),
27
+ flag: z.string().nullable(),
28
+ id: z.number(),
29
+ uid: z.string().nullable(),
30
+ ban: z.string().nullable(),
31
+ username: z.string().nullable(),
32
+ verified: z.boolean().nullable(),
33
+ play_count: z.number().nullable(),
34
+ skill_points: z.number().nullable(),
35
+ squares_hit: z.number().nullable(),
36
+ total_score: z.number().nullable(),
37
+ })
38
+ ),
39
+ }),
40
+ };
41
+
42
+ export async function POST(request: Request) {
43
+ return protectedApi({
44
+ request,
45
+ schema: Schema,
46
+ authorization: () => {},
47
+ activity: handler,
48
+ });
49
+ }
50
+
51
+ export async function handler(data: (typeof Schema)["input"]["_type"]) {
52
+ let { data: queryClanData, error: clanError } = await supabase
53
+ .from("clans")
54
+ .select("*")
55
+ .eq("id", data.id)
56
+ .single();
57
+
58
+ if (!queryClanData) {
59
+ return NextResponse.json({ error: "No such clan ID" });
60
+ }
61
+
62
+ let { data: queryUsers, error: usersError } = await supabase
63
+ .from("profiles")
64
+ .select("*")
65
+ .eq("clan", queryClanData.id);
66
+
67
+ return NextResponse.json({
68
+ acronym: queryClanData.acronym,
69
+ avatar_url: queryClanData.avatar_url,
70
+ created_at: queryClanData.created_at,
71
+ description: queryClanData.description,
72
+ id: queryClanData.id,
73
+ name: queryClanData.name,
74
+ owner: queryClanData.owner,
75
+ users: queryUsers,
76
+ });
77
+ }