rhythia-api 150.0.0 → 152.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/approveMap.ts CHANGED
@@ -2,6 +2,8 @@ import { NextResponse } from "next/server";
2
2
  import z from "zod";
3
3
  import { protectedApi, validUser } from "../utils/requestUtils";
4
4
  import { supabase } from "../utils/supabase";
5
+ import { getUserBySession } from "../utils/getUserBySession";
6
+ import { User } from "@supabase/supabase-js";
5
7
 
6
8
  export const Schema = {
7
9
  input: z.strictObject({
@@ -23,7 +25,7 @@ export async function POST(request: Request) {
23
25
  }
24
26
 
25
27
  export async function handler(data: (typeof Schema)["input"]["_type"]) {
26
- const user = (await supabase.auth.getUser(data.session)).data.user!;
28
+ const user = (await getUserBySession(data.session)) as User;
27
29
  let { data: queryUserData, error: userError } = await supabase
28
30
  .from("profiles")
29
31
  .select("*")
@@ -5,6 +5,8 @@ import { SSPMParser } from "../utils/star-calc/sspmParser";
5
5
  import { supabase } from "../utils/supabase";
6
6
  import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
7
7
  import { rateMap } from "../utils/star-calc";
8
+ import { getUserBySession } from "../utils/getUserBySession";
9
+ import { User } from "@supabase/supabase-js";
8
10
  const s3Client = new S3Client({
9
11
  region: "auto",
10
12
  endpoint: "https://s3.eu-central-003.backblazeb2.com",
@@ -52,7 +54,7 @@ export async function handler({
52
54
  const parsedData = parser.parse();
53
55
  const digested = parsedData.strings.mapID;
54
56
 
55
- const user = (await supabase.auth.getUser(session)).data.user!;
57
+ const user = (await getUserBySession(session)) as User;
56
58
  let { data: userData, error: userError } = await supabase
57
59
  .from("profiles")
58
60
  .select("*")
@@ -101,6 +103,15 @@ export async function handler({
101
103
  .toBuffer();
102
104
  } catch (error) {}
103
105
 
106
+ let largeBuffer = Buffer.from([]);
107
+ try {
108
+ largeBuffer = await require("sharp")(parsedData.cover)
109
+ .resize(850)
110
+ .jpeg({ mozjpeg: true })
111
+ .toBuffer();
112
+ } catch (error) {}
113
+
114
+ // Images
104
115
  const command = new PutObjectCommand({
105
116
  Bucket: "rhthia-avatars",
106
117
  Key: imgkey,
@@ -109,6 +120,17 @@ export async function handler({
109
120
  });
110
121
 
111
122
  await s3Client.send(command);
123
+
124
+ const command2 = new PutObjectCommand({
125
+ Bucket: "rhthia-avatars",
126
+ Key: imgkey + "large",
127
+ Body: largeBuffer,
128
+ ContentType: "image/jpeg",
129
+ });
130
+
131
+ await s3Client.send(command2);
132
+ // Images End
133
+
112
134
  const markers = parsedData.markers.sort((a, b) => a.position - b.position);
113
135
 
114
136
  const upserted = await supabase.from("beatmaps").upsert({
@@ -120,6 +142,7 @@ export async function handler({
120
142
  length: markers[markers.length - 1].position,
121
143
  beatmapFile: url,
122
144
  image: `https://static.rhythia.com/${imgkey}`,
145
+ imageLarge: `https://static.rhythia.com/${imgkey}large`,
123
146
  starRating: rateMap(parsedData),
124
147
  });
125
148
 
@@ -2,6 +2,8 @@ import { NextResponse } from "next/server";
2
2
  import z from "zod";
3
3
  import { protectedApi, validUser } from "../utils/requestUtils";
4
4
  import { supabase } from "../utils/supabase";
5
+ import { getUserBySession } from "../utils/getUserBySession";
6
+ import { User } from "@supabase/supabase-js";
5
7
 
6
8
  export const Schema = {
7
9
  input: z.strictObject({
@@ -27,7 +29,7 @@ export async function handler({
27
29
  }: (typeof Schema)["input"]["_type"]): Promise<
28
30
  NextResponse<(typeof Schema)["output"]["_type"]>
29
31
  > {
30
- const user = (await supabase.auth.getUser(session)).data.user!;
32
+ const user = (await getUserBySession(session)) as User;
31
33
  let { data: userData, error: userError } = await supabase
32
34
  .from("profiles")
33
35
  .select("*")
@@ -49,7 +51,7 @@ export async function handler({
49
51
  { status: 404 }
50
52
  );
51
53
  }
52
-
54
+
53
55
  const upserted = await supabase
54
56
  .from("beatmapPages")
55
57
  .upsert({
@@ -2,6 +2,8 @@ import { NextResponse } from "next/server";
2
2
  import z from "zod";
3
3
  import { protectedApi, validUser } from "../utils/requestUtils";
4
4
  import { supabase } from "../utils/supabase";
5
+ import { getUserBySession } from "../utils/getUserBySession";
6
+ import { User } from "@supabase/supabase-js";
5
7
 
6
8
  export const Schema = {
7
9
  input: z.strictObject({
@@ -28,7 +30,7 @@ export async function handler({
28
30
  }: (typeof Schema)["input"]["_type"]): Promise<
29
31
  NextResponse<(typeof Schema)["output"]["_type"]>
30
32
  > {
31
- const user = (await supabase.auth.getUser(session)).data.user!;
33
+ const user = (await getUserBySession(session)) as User;
32
34
  let { data: userData, error: userError } = await supabase
33
35
  .from("profiles")
34
36
  .select("*")
@@ -3,6 +3,8 @@ import z from "zod";
3
3
  import { Database } from "../types/database";
4
4
  import { protectedApi, validUser } from "../utils/requestUtils";
5
5
  import { supabase } from "../utils/supabase";
6
+ import { getUserBySession } from "../utils/getUserBySession";
7
+ import { User } from "@supabase/supabase-js";
6
8
  export const Schema = {
7
9
  input: z.strictObject({
8
10
  session: z.string(),
@@ -45,7 +47,7 @@ export async function handler(
45
47
  );
46
48
  }
47
49
 
48
- const user = (await supabase.auth.getUser(data.session)).data.user!;
50
+ const user = (await getUserBySession(data.session)) as User;
49
51
  let userData: Database["public"]["Tables"]["profiles"]["Update"];
50
52
 
51
53
  // Find user's entry
@@ -3,6 +3,8 @@ import z from "zod";
3
3
  import { Database } from "../types/database";
4
4
  import { protectedApi, validUser } from "../utils/requestUtils";
5
5
  import { supabase } from "../utils/supabase";
6
+ import { getUserBySession } from "../utils/getUserBySession";
7
+ import { User } from "@supabase/supabase-js";
6
8
  export const Schema = {
7
9
  input: z.strictObject({
8
10
  session: z.string(),
@@ -46,7 +48,7 @@ export async function handler(
46
48
  );
47
49
  }
48
50
 
49
- const user = (await supabase.auth.getUser(data.session)).data.user!;
51
+ const user = (await getUserBySession(data.session)) as User;
50
52
 
51
53
  let userData: Database["public"]["Tables"]["profiles"]["Update"];
52
54
 
@@ -10,6 +10,8 @@ import {
10
10
  } from "@aws-sdk/client-s3";
11
11
  import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
12
12
  import { validateIntrinsicToken } from "../utils/validateToken";
13
+ import { getUserBySession } from "../utils/getUserBySession";
14
+ import { User } from "@supabase/supabase-js";
13
15
 
14
16
  const s3Client = new S3Client({
15
17
  region: "auto",
@@ -51,7 +53,7 @@ export async function handler({
51
53
  }: (typeof Schema)["input"]["_type"]): Promise<
52
54
  NextResponse<(typeof Schema)["output"]["_type"]>
53
55
  > {
54
- const user = (await supabase.auth.getUser(session)).data.user!;
56
+ const user = (await getUserBySession(session)) as User;
55
57
 
56
58
  if (!validateIntrinsicToken(intrinsicToken)) {
57
59
  return NextResponse.json({
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import z from "zod";
3
- import { getUser, protectedApi } from "../utils/requestUtils";
3
+ import { protectedApi } from "../utils/requestUtils";
4
4
  import { supabase } from "../utils/supabase";
5
5
 
6
6
  export const Schema = {
@@ -23,6 +23,7 @@ export const Schema = {
23
23
  ranked: z.boolean().nullable().optional(),
24
24
  beatmapFile: z.string().nullable().optional(),
25
25
  image: z.string().nullable().optional(),
26
+ imageLarge: z.string().nullable().optional(),
26
27
  starRating: z.number().nullable().optional(),
27
28
  owner: z.number().nullable().optional(),
28
29
  ownerUsername: z.string().nullable().optional(),
@@ -60,6 +61,7 @@ export async function handler(
60
61
  ranked,
61
62
  beatmapFile,
62
63
  image,
64
+ imageLarge,
63
65
  starRating,
64
66
  difficulty,
65
67
  noteCount,
@@ -87,6 +89,7 @@ export async function handler(
87
89
  ranked: beatmapPage.beatmaps?.ranked,
88
90
  beatmapFile: beatmapPage.beatmaps?.beatmapFile,
89
91
  image: beatmapPage.beatmaps?.image,
92
+ imageLarge: beatmapPage.beatmaps?.imageLarge,
90
93
  starRating: beatmapPage.beatmaps?.starRating,
91
94
  owner: beatmapPage.owner,
92
95
  ownerUsername: beatmapPage.profiles?.username,
@@ -1,7 +1,9 @@
1
1
  import { NextResponse } from "next/server";
2
2
  import z from "zod";
3
- import { getUser, protectedApi } from "../utils/requestUtils";
3
+ import { protectedApi } from "../utils/requestUtils";
4
4
  import { supabase } from "../utils/supabase";
5
+ import { getUserBySession } from "../utils/getUserBySession";
6
+ import { User } from "@supabase/supabase-js";
5
7
 
6
8
  export const Schema = {
7
9
  input: z.strictObject({
@@ -48,7 +50,7 @@ export async function handler(
48
50
  const VIEW_PER_PAGE = 50;
49
51
 
50
52
  export async function getLeaderboard(page = 1, session: string) {
51
- const getUserData = await getUser({ session });
53
+ const getUserData = (await getUserBySession(session)) as User;
52
54
 
53
55
  let leaderPosition = 0;
54
56
 
@@ -56,7 +58,7 @@ export async function getLeaderboard(page = 1, session: string) {
56
58
  let { data: queryData, error } = await supabase
57
59
  .from("profiles")
58
60
  .select("*")
59
- .eq("uid", getUserData.data.user.id)
61
+ .eq("uid", getUserData.id)
60
62
  .single();
61
63
 
62
64
  if (queryData) {
@@ -10,6 +10,8 @@ import {
10
10
  } from "@aws-sdk/client-s3";
11
11
  import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
12
12
  import { validateIntrinsicToken } from "../utils/validateToken";
13
+ import { getUserBySession } from "../utils/getUserBySession";
14
+ import { User } from "@supabase/supabase-js";
13
15
 
14
16
  const s3Client = new S3Client({
15
17
  region: "auto",
@@ -53,7 +55,7 @@ export async function handler({
53
55
  }: (typeof Schema)["input"]["_type"]): Promise<
54
56
  NextResponse<(typeof Schema)["output"]["_type"]>
55
57
  > {
56
- const user = (await supabase.auth.getUser(session)).data.user!;
58
+ const user = (await getUserBySession(session)) as User;
57
59
 
58
60
  if (!validateIntrinsicToken(intrinsicToken)) {
59
61
  return NextResponse.json({
@@ -0,0 +1,56 @@
1
+ import { NextResponse } from "next/server";
2
+ import z from "zod";
3
+ import { Database } from "../types/database";
4
+ import { protectedApi, validUser } from "../utils/requestUtils";
5
+ import { supabase } from "../utils/supabase";
6
+ import md5 from "md5";
7
+ import { encryptString } from "../utils/security";
8
+ export const Schema = {
9
+ input: z.strictObject({
10
+ data: z.object({
11
+ email: z.string(),
12
+ passkey: z.string(),
13
+ computerName: z.string(),
14
+ }),
15
+ }),
16
+ output: z.object({
17
+ token: z.string().optional(),
18
+ error: z.string().optional(),
19
+ }),
20
+ };
21
+
22
+ export async function POST(request: Request): Promise<NextResponse> {
23
+ return protectedApi({
24
+ request,
25
+ schema: Schema,
26
+ authorization: () => {},
27
+ activity: handler,
28
+ });
29
+ }
30
+
31
+ export async function handler(
32
+ data: (typeof Schema)["input"]["_type"]
33
+ ): Promise<NextResponse<(typeof Schema)["output"]["_type"]>> {
34
+ let { data: queryPasskey, error } = await supabase
35
+ .from("passkeys")
36
+ .select("*")
37
+ .eq("email", data.data.email)
38
+ .eq("passkey", data.data.passkey)
39
+ .single();
40
+
41
+ if (!queryPasskey) {
42
+ return NextResponse.json({
43
+ error: "Wrong combination",
44
+ });
45
+ }
46
+ return NextResponse.json({
47
+ token: encryptString(
48
+ JSON.stringify({
49
+ userId: queryPasskey.id,
50
+ email: queryPasskey.email,
51
+ passKey: queryPasskey.passkey,
52
+ computerName: data.data.computerName,
53
+ })
54
+ ),
55
+ });
56
+ }
@@ -2,6 +2,8 @@ import { NextResponse } from "next/server";
2
2
  import z from "zod";
3
3
  import { protectedApi, validUser } from "../utils/requestUtils";
4
4
  import { supabase } from "../utils/supabase";
5
+ import { getUserBySession } from "../utils/getUserBySession";
6
+ import { User } from "@supabase/supabase-js";
5
7
 
6
8
  export const Schema = {
7
9
  input: z.strictObject({
@@ -23,7 +25,7 @@ export async function POST(request: Request) {
23
25
  }
24
26
 
25
27
  export async function handler(data: (typeof Schema)["input"]["_type"]) {
26
- const user = (await supabase.auth.getUser(data.session)).data.user!;
28
+ const user = (await getUserBySession(data.session)) as User;
27
29
  let { data: queryUserData, error: userError } = await supabase
28
30
  .from("profiles")
29
31
  .select("*")
@@ -2,6 +2,8 @@ import { NextResponse } from "next/server";
2
2
  import z from "zod";
3
3
  import { protectedApi, validUser } from "../utils/requestUtils";
4
4
  import { supabase } from "../utils/supabase";
5
+ import { User } from "@supabase/supabase-js";
6
+ import { getUserBySession } from "../utils/getUserBySession";
5
7
 
6
8
  export const Schema = {
7
9
  input: z.strictObject({
@@ -30,7 +32,7 @@ export async function handler({
30
32
  }: (typeof Schema)["input"]["_type"]): Promise<
31
33
  NextResponse<(typeof Schema)["output"]["_type"]>
32
34
  > {
33
- const user = (await supabase.auth.getUser(session)).data.user!;
35
+ const user = (await getUserBySession(session)) as User;
34
36
  let { data: userData, error: userError } = await supabase
35
37
  .from("profiles")
36
38
  .select("*")
@@ -2,6 +2,8 @@ import { NextResponse } from "next/server";
2
2
  import z from "zod";
3
3
  import { protectedApi, validUser } from "../utils/requestUtils";
4
4
  import { supabase } from "../utils/supabase";
5
+ import { getUserBySession } from "../utils/getUserBySession";
6
+ import { User } from "@supabase/supabase-js";
5
7
 
6
8
  export const Schema = {
7
9
  input: z.strictObject({
@@ -23,7 +25,7 @@ export async function POST(request: Request) {
23
25
  }
24
26
 
25
27
  export async function handler(data: (typeof Schema)["input"]["_type"]) {
26
- const user = (await supabase.auth.getUser(data.session)).data.user!;
28
+ const user = (await getUserBySession(data.session)) as User;
27
29
  let { data: queryUserData, error: userError } = await supabase
28
30
  .from("profiles")
29
31
  .select("*")
@@ -0,0 +1,60 @@
1
+ import { NextResponse } from "next/server";
2
+ import z from "zod";
3
+ import { Database } from "../types/database";
4
+ import { protectedApi, validUser } from "../utils/requestUtils";
5
+ import { supabase } from "../utils/supabase";
6
+ import md5 from "md5";
7
+ import { getUserBySession } from "../utils/getUserBySession";
8
+ import { User } from "@supabase/supabase-js";
9
+ export const Schema = {
10
+ input: z.strictObject({
11
+ session: z.string(),
12
+ data: z.object({
13
+ passkey: z.string(),
14
+ }),
15
+ }),
16
+ output: z.object({
17
+ error: z.string().optional(),
18
+ }),
19
+ };
20
+
21
+ export async function POST(request: Request): Promise<NextResponse> {
22
+ return protectedApi({
23
+ request,
24
+ schema: Schema,
25
+ authorization: () => {},
26
+ activity: handler,
27
+ });
28
+ }
29
+
30
+ export async function handler(
31
+ data: (typeof Schema)["input"]["_type"]
32
+ ): Promise<NextResponse<(typeof Schema)["output"]["_type"]>> {
33
+ const user = (await getUserBySession(data.session)) as User;
34
+ let userData: Database["public"]["Tables"]["profiles"]["Update"];
35
+ // Find user's entry
36
+ {
37
+ let { data: queryUserData, error } = await supabase
38
+ .from("profiles")
39
+ .select("*")
40
+ .eq("uid", user.id);
41
+
42
+ if (!queryUserData?.length) {
43
+ return NextResponse.json(
44
+ {
45
+ error: "User cannot be retrieved from session",
46
+ },
47
+ { status: 404 }
48
+ );
49
+ }
50
+ userData = queryUserData[0];
51
+ }
52
+
53
+ await supabase.from("passkeys").upsert({
54
+ id: userData.id!,
55
+ email: user.email!,
56
+ passkey: md5(data.data.passkey),
57
+ });
58
+
59
+ return NextResponse.json({});
60
+ }
@@ -2,6 +2,10 @@ import { NextResponse } from "next/server";
2
2
  import z from "zod";
3
3
  import { protectedApi, validUser } from "../utils/requestUtils";
4
4
  import { supabase } from "../utils/supabase";
5
+ import { decryptString } from "../utils/security";
6
+ import { isEqual } from "lodash";
7
+ import { getUserBySession } from "../utils/getUserBySession";
8
+ import { User } from "@supabase/supabase-js";
5
9
 
6
10
  export const Schema = {
7
11
  input: z.strictObject({
@@ -50,13 +54,27 @@ export async function handler({
50
54
  }: (typeof Schema)["input"]["_type"]): Promise<
51
55
  NextResponse<(typeof Schema)["output"]["_type"]>
52
56
  > {
53
- return NextResponse.json(
54
- {
55
- error: "Disabled",
56
- },
57
- { status: 500 }
58
- );
59
- const user = (await supabase.auth.getUser(session)).data.user!;
57
+ const tokenContents = JSON.parse(decryptString(data.token));
58
+ if (
59
+ !isEqual(tokenContents, {
60
+ relayHwid: data.relayHwid,
61
+ songId: data.songId,
62
+ misses: data.misses,
63
+ hits: data.hits,
64
+ mapHash: data.mapHash,
65
+ mapNoteCount: data.mapNoteCount,
66
+ speed: data.speed,
67
+ })
68
+ ) {
69
+ return NextResponse.json(
70
+ {
71
+ error: "Token miscalculation",
72
+ },
73
+ { status: 500 }
74
+ );
75
+ }
76
+
77
+ const user = (await getUserBySession(session)) as User;
60
78
 
61
79
  let { data: userData, error: userError } = await supabase
62
80
  .from("profiles")
@@ -2,6 +2,8 @@ import { NextResponse } from "next/server";
2
2
  import z from "zod";
3
3
  import { protectedApi, validUser } from "../utils/requestUtils";
4
4
  import { supabase } from "../utils/supabase";
5
+ import { getUserBySession } from "../utils/getUserBySession";
6
+ import { User } from "@supabase/supabase-js";
5
7
 
6
8
  export const Schema = {
7
9
  input: z.strictObject({
@@ -34,7 +36,7 @@ export async function handler({
34
36
  }: (typeof Schema)["input"]["_type"]): Promise<
35
37
  NextResponse<(typeof Schema)["output"]["_type"]>
36
38
  > {
37
- const user = (await supabase.auth.getUser(session)).data.user!;
39
+ const user = (await getUserBySession(session)) as User;
38
40
  let { data: userData, error: userError } = await supabase
39
41
  .from("profiles")
40
42
  .select("*")
@@ -67,17 +69,17 @@ export async function handler({
67
69
 
68
70
  const upsertPayload = {
69
71
  id,
70
- latestBeatmapHash: beatmapHash ? beatmapHash : pageData.latestBeatmapHash,
71
72
  genre: "",
72
73
  status: "UNRANKED",
73
74
  owner: userData.id,
74
75
  description: description ? description : pageData.description,
75
76
  tags: tags ? tags : pageData.tags,
76
- nominations: [],
77
77
  };
78
78
 
79
79
  if (beatmapHash && beatmapData) {
80
80
  upsertPayload["title"] = beatmapData.title;
81
+ upsertPayload["latestBeatmapHash"] = beatmapHash;
82
+ upsertPayload["nominations"] = [];
81
83
  }
82
84
 
83
85
  const upserted = await supabase
package/index.ts CHANGED
@@ -198,6 +198,7 @@ export const Schema = {
198
198
  ranked: z.boolean().nullable().optional(),
199
199
  beatmapFile: z.string().nullable().optional(),
200
200
  image: z.string().nullable().optional(),
201
+ imageLarge: z.string().nullable().optional(),
201
202
  starRating: z.number().nullable().optional(),
202
203
  owner: z.number().nullable().optional(),
203
204
  ownerUsername: z.string().nullable().optional(),
@@ -352,6 +353,26 @@ import { Schema as GetMapUploadUrl } from "./api/getMapUploadUrl"
352
353
  export { Schema as SchemaGetMapUploadUrl } from "./api/getMapUploadUrl"
353
354
  export const getMapUploadUrl = handleApi({url:"/api/getMapUploadUrl",...GetMapUploadUrl})
354
355
 
356
+ // ./api/getPassToken.ts API
357
+
358
+ /*
359
+ export const Schema = {
360
+ input: z.strictObject({
361
+ data: z.object({
362
+ email: z.string(),
363
+ passkey: z.string(),
364
+ computerName: z.string(),
365
+ }),
366
+ }),
367
+ output: z.object({
368
+ token: z.string().optional(),
369
+ error: z.string().optional(),
370
+ }),
371
+ };*/
372
+ import { Schema as GetPassToken } from "./api/getPassToken"
373
+ export { Schema as SchemaGetPassToken } from "./api/getPassToken"
374
+ export const getPassToken = handleApi({url:"/api/getPassToken",...GetPassToken})
375
+
355
376
  // ./api/getProfile.ts API
356
377
 
357
378
  /*
@@ -561,6 +582,24 @@ import { Schema as SearchUsers } from "./api/searchUsers"
561
582
  export { Schema as SchemaSearchUsers } from "./api/searchUsers"
562
583
  export const searchUsers = handleApi({url:"/api/searchUsers",...SearchUsers})
563
584
 
585
+ // ./api/setPasskey.ts API
586
+
587
+ /*
588
+ export const Schema = {
589
+ input: z.strictObject({
590
+ session: z.string(),
591
+ data: z.object({
592
+ passkey: z.string(),
593
+ }),
594
+ }),
595
+ output: z.object({
596
+ error: z.string().optional(),
597
+ }),
598
+ };*/
599
+ import { Schema as SetPasskey } from "./api/setPasskey"
600
+ export { Schema as SchemaSetPasskey } from "./api/setPasskey"
601
+ export const setPasskey = handleApi({url:"/api/setPasskey",...SetPasskey})
602
+
564
603
  // ./api/submitScore.ts API
565
604
 
566
605
  /*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhythia-api",
3
- "version": "150.0.0",
3
+ "version": "152.0.0",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "update": "bun ./scripts/update.ts",
@@ -16,6 +16,7 @@
16
16
  "@aws-sdk/client-s3": "^3.637.0",
17
17
  "@aws-sdk/s3-request-presigner": "^3.637.0",
18
18
  "@netlify/zip-it-and-ship-it": "^9.37.9",
19
+ "@noble/ciphers": "^1.0.0",
19
20
  "@supabase/supabase-js": "^2.45.1",
20
21
  "@types/aws-lambda": "^8.10.143",
21
22
  "@types/bun": "^1.1.6",
@@ -38,7 +39,7 @@
38
39
  "osu-standard-stable": "^5.0.0",
39
40
  "sharp": "^0.33.5",
40
41
  "simple-git": "^3.25.0",
41
- "supabase": "^1.204.3",
42
+ "supabase": "^1.207.9",
42
43
  "tsx": "^4.17.0",
43
44
  "utf-8-validate": "^6.0.4",
44
45
  "zod": "^3.23.8"
package/types/database.ts CHANGED
@@ -109,6 +109,7 @@ export type Database = {
109
109
  created_at: string
110
110
  difficulty: number | null
111
111
  image: string | null
112
+ imageLarge: string | null
112
113
  length: number | null
113
114
  noteCount: number | null
114
115
  playcount: number | null
@@ -122,6 +123,7 @@ export type Database = {
122
123
  created_at?: string
123
124
  difficulty?: number | null
124
125
  image?: string | null
126
+ imageLarge?: string | null
125
127
  length?: number | null
126
128
  noteCount?: number | null
127
129
  playcount?: number | null
@@ -135,6 +137,7 @@ export type Database = {
135
137
  created_at?: string
136
138
  difficulty?: number | null
137
139
  image?: string | null
140
+ imageLarge?: string | null
138
141
  length?: number | null
139
142
  noteCount?: number | null
140
143
  playcount?: number | null
@@ -144,6 +147,32 @@ export type Database = {
144
147
  }
145
148
  Relationships: []
146
149
  }
150
+ passkeys: {
151
+ Row: {
152
+ email: string
153
+ id: number
154
+ passkey: string
155
+ }
156
+ Insert: {
157
+ email: string
158
+ id: number
159
+ passkey: string
160
+ }
161
+ Update: {
162
+ email?: string
163
+ id?: number
164
+ passkey?: string
165
+ }
166
+ Relationships: [
167
+ {
168
+ foreignKeyName: "passkeys_id_fkey"
169
+ columns: ["id"]
170
+ isOneToOne: true
171
+ referencedRelation: "profiles"
172
+ referencedColumns: ["id"]
173
+ },
174
+ ]
175
+ }
147
176
  profiles: {
148
177
  Row: {
149
178
  about_me: string | null
@@ -202,15 +231,7 @@ export type Database = {
202
231
  username?: string | null
203
232
  verified?: boolean | null
204
233
  }
205
- Relationships: [
206
- {
207
- foreignKeyName: "profiles_uid_fkey"
208
- columns: ["uid"]
209
- isOneToOne: true
210
- referencedRelation: "users"
211
- referencedColumns: ["id"]
212
- },
213
- ]
234
+ Relationships: []
214
235
  }
215
236
  scores: {
216
237
  Row: {
@@ -363,3 +384,18 @@ export type Enums<
363
384
  : PublicEnumNameOrOptions extends keyof PublicSchema["Enums"]
364
385
  ? PublicSchema["Enums"][PublicEnumNameOrOptions]
365
386
  : never
387
+
388
+ export type CompositeTypes<
389
+ PublicCompositeTypeNameOrOptions extends
390
+ | keyof PublicSchema["CompositeTypes"]
391
+ | { schema: keyof Database },
392
+ CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
393
+ schema: keyof Database
394
+ }
395
+ ? keyof Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
396
+ : never = never,
397
+ > = PublicCompositeTypeNameOrOptions extends { schema: keyof Database }
398
+ ? Database[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
399
+ : PublicCompositeTypeNameOrOptions extends keyof PublicSchema["CompositeTypes"]
400
+ ? PublicSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
401
+ : never
@@ -0,0 +1,37 @@
1
+ import { User } from "@supabase/supabase-js";
2
+ import { decryptString } from "./security";
3
+ import { supabase } from "./supabase";
4
+
5
+ export async function getUserBySession(session: string): Promise<User | null> {
6
+ const user = (await supabase.auth.getUser(session)).data.user;
7
+
8
+ if (user) {
9
+ return user;
10
+ }
11
+
12
+ try {
13
+ const decryptedToken = JSON.parse(decryptString(session)) as {
14
+ userId: number;
15
+ email: string;
16
+ passKey: string;
17
+ computerName: string;
18
+ };
19
+
20
+ let { data: queryPasskey, error } = await supabase
21
+ .from("passkeys")
22
+ .select("*,profiles(uid)")
23
+ .eq("id", decryptedToken.userId || "nil")
24
+ .eq("email", decryptedToken.email || "nil")
25
+ .eq("passkey", decryptedToken.passKey || "nil")
26
+ .single();
27
+
28
+ if (!queryPasskey) {
29
+ return null;
30
+ }
31
+
32
+ return (await supabase.auth.admin.getUserById(queryPasskey.profiles?.uid!))
33
+ .data.user;
34
+ } catch (error) {}
35
+
36
+ return null;
37
+ }
@@ -1,6 +1,6 @@
1
1
  import { NextResponse } from "next/server";
2
- import { supabase } from "./supabase";
3
2
  import { ZodObject } from "zod";
3
+ import { getUserBySession } from "./getUserBySession";
4
4
 
5
5
  interface Props<
6
6
  K = (...args: any[]) => Promise<NextResponse<any>>,
@@ -43,8 +43,8 @@ export async function validUser(data) {
43
43
  );
44
44
  }
45
45
 
46
- const user = await supabase.auth.getUser(data.session);
47
- if (user.error || !user.data.user) {
46
+ const user = await getUserBySession(data.session);
47
+ if (!user) {
48
48
  return NextResponse.json(
49
49
  {
50
50
  error: "Invalid user session",
@@ -53,15 +53,3 @@ export async function validUser(data) {
53
53
  );
54
54
  }
55
55
  }
56
-
57
- export async function getUser(data) {
58
- if (!data.session) {
59
- return;
60
- }
61
-
62
- const user = await supabase.auth.getUser(data.session);
63
- if (user.error || !user.data.user) {
64
- return;
65
- }
66
- return user;
67
- }
@@ -0,0 +1,20 @@
1
+ import { xchacha20poly1305 } from "@noble/ciphers/chacha";
2
+ import { managedNonce } from "@noble/ciphers/webcrypto";
3
+ import {
4
+ bytesToHex,
5
+ bytesToUtf8,
6
+ hexToBytes,
7
+ utf8ToBytes,
8
+ } from "@noble/ciphers/utils";
9
+ const key = hexToBytes(process.env.TOKEN_SECRET || "");
10
+ const chacha = managedNonce(xchacha20poly1305)(key); // manages nonces for you
11
+
12
+ export function encryptString(str: string) {
13
+ const data = utf8ToBytes(str);
14
+ return bytesToHex(chacha.encrypt(data));
15
+ }
16
+
17
+ export function decryptString(str: string) {
18
+ const data = hexToBytes(str);
19
+ return bytesToUtf8(chacha.decrypt(data));
20
+ }
@@ -1,4 +1,4 @@
1
- import { BeatmapDecoder } from "osu-parsers";
1
+ import { BeatmapDecoder, BeatmapEncoder } from "osu-parsers";
2
2
  import { StandardRuleset } from "osu-standard-stable";
3
3
  import { sampleMap } from "./osuUtils";
4
4
  import { SSPMParsedMap } from "./sspmParser";
@@ -18,9 +18,6 @@ export function calculatePerformancePoints(
18
18
  }
19
19
 
20
20
  export function rateMap(map: SSPMParsedMap) {
21
- const decoder = new BeatmapDecoder();
22
- const beatmap1 = decoder.decodeFromString(sampleMap);
23
-
24
21
  let notes = map.markers
25
22
  .filter((marker) => marker.type === 0)
26
23
  .map((marker) => ({
@@ -29,40 +26,44 @@ export function rateMap(map: SSPMParsedMap) {
29
26
  y: marker.data["field0"].y,
30
27
  }));
31
28
 
32
- notes = notes.sort((a, b) => a.time - b.time);
33
-
34
- for (const note of notes) {
35
- const hittable = beatmap1.hitObjects[0].clone();
36
- hittable.startX = Math.round((note.x / 2) * 100);
37
- hittable.startY = Math.round((note.y / 2) * 100);
38
- hittable.startTime = note.time;
39
- beatmap1.hitObjects.push(hittable);
40
- }
41
- const ruleset = new StandardRuleset();
42
- const mods = ruleset.createModCombination("RX");
43
- const difficultyCalculator = ruleset.createDifficultyCalculator(beatmap1);
44
- const difficultyAttributes = difficultyCalculator.calculateWithMods(mods);
45
- return difficultyAttributes.starRating;
29
+ return rate(notes);
46
30
  }
47
31
 
48
32
  export function rateMapOld(map: SSPMMap) {
49
- const decoder = new BeatmapDecoder();
50
- const beatmap1 = decoder.decodeFromString(sampleMap);
51
-
52
33
  let notes = map.notes.map((marker) => ({
53
34
  time: marker.position,
54
35
  x: marker.x,
55
36
  y: marker.y,
56
37
  }));
57
38
 
39
+ return rate(notes);
40
+ }
41
+
42
+ export function rate(
43
+ notes: {
44
+ time: number;
45
+ x: number;
46
+ y: number;
47
+ }[]
48
+ ) {
58
49
  notes = notes.sort((a, b) => a.time - b.time);
50
+ const decoder = new BeatmapDecoder();
51
+ const beatmap1 = decoder.decodeFromString(sampleMap);
52
+ let i = 0;
53
+ while (i < notes.length - 1) {
54
+ const note = notes[i];
55
+ const nextNote = notes[i + 1];
56
+ if (distance(note.x, note.y, nextNote.x, nextNote.y) < 1.25) {
57
+ notes.splice(i + 1, 1);
58
+ continue;
59
+ }
59
60
 
60
- for (const note of notes) {
61
61
  const hittable = beatmap1.hitObjects[0].clone();
62
62
  hittable.startX = Math.round((note.x / 2) * 100);
63
63
  hittable.startY = Math.round((note.y / 2) * 100);
64
64
  hittable.startTime = note.time;
65
65
  beatmap1.hitObjects.push(hittable);
66
+ i++;
66
67
  }
67
68
  const ruleset = new StandardRuleset();
68
69
  const mods = ruleset.createModCombination("RX");
@@ -70,3 +71,5 @@ export function rateMapOld(map: SSPMMap) {
70
71
  const difficultyAttributes = difficultyCalculator.calculateWithMods(mods);
71
72
  return difficultyAttributes.starRating;
72
73
  }
74
+
75
+ const distance = (x1, y1, x2, y2) => Math.hypot(x2 - x1, y2 - y1);