rhythia-api 110.0.0 → 111.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.
@@ -2,6 +2,19 @@ import { NextResponse } from "next/server";
2
2
  import z from "zod";
3
3
  import { protectedApi, validUser } from "../utils/requestUtils";
4
4
  import { SSPMParser } from "../utils/star-calc/sspmParser";
5
+ import { supabase } from "../utils/supabase";
6
+ import { createHash } from "crypto";
7
+ import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
8
+ import { rateMap } from "../utils/star-calc";
9
+
10
+ const s3Client = new S3Client({
11
+ region: "auto",
12
+ endpoint: "https://s3.eu-central-003.backblazeb2.com",
13
+ credentials: {
14
+ secretAccessKey: "K0039mm4iKsteQOXpZSzf0+VDzuH89U",
15
+ accessKeyId: "003c245e893e8060000000001",
16
+ },
17
+ });
5
18
 
6
19
  export const Schema = {
7
20
  input: z.strictObject({
@@ -38,7 +51,33 @@ export async function handler({
38
51
  const parser = new SSPMParser(Buffer.from(bytes));
39
52
 
40
53
  const parsedData = parser.parse();
54
+ let sum = createHash("sha1");
55
+ sum.update(Buffer.from(bytes));
56
+ const digested = sum.digest("hex");
57
+ const imgkey = `beatmap-img-${Date.now()}-${digested}`;
58
+ const command = new PutObjectCommand({
59
+ Bucket: "rhthia-avatars",
60
+ Key: imgkey,
61
+ Body: parsedData.cover,
62
+ ContentType: "image/jpeg",
63
+ });
64
+
65
+ await s3Client.send(command);
66
+ parsedData.markers.sort((a, b) => a.position - b.position);
67
+ const upserted = await supabase.from("beatmaps").upsert({
68
+ beatmapHash: digested,
69
+ title: parsedData.strings.songName,
70
+ playcount: 0,
71
+ difficulty: parsedData.metadata.difficulty,
72
+ noteCount: parsedData.metadata.noteCount,
73
+ length: parsedData.pointers.audioLength,
74
+ beatmapFile: url,
75
+ image: `https://rhthia-avatars.s3.eu-central-003.backblazeb2.com/${imgkey}`,
76
+ starRating: rateMap(parsedData),
77
+ });
41
78
 
42
- console.log(parsedData);
43
- return NextResponse.json({});
79
+ if (upserted.error?.message.length) {
80
+ return NextResponse.json({ error: upserted.error.message });
81
+ }
82
+ return NextResponse.json({ hash: digested });
44
83
  }
@@ -0,0 +1,88 @@
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
+ id: z.number(),
10
+ }),
11
+ output: z.object({
12
+ error: z.string().optional(),
13
+ beatmap: z
14
+ .object({
15
+ playcount: z.number().nullable().optional(),
16
+ created_at: z.string().nullable().optional(),
17
+ difficulty: z.number().nullable().optional(),
18
+ noteCount: z.number().nullable().optional(),
19
+ length: z.number().nullable().optional(),
20
+ title: z.string().nullable().optional(),
21
+ ranked: z.boolean().nullable().optional(),
22
+ beatmapFile: z.string().nullable().optional(),
23
+ image: z.string().nullable().optional(),
24
+ starRating: z.number().nullable().optional(),
25
+ owner: z.number().nullable().optional(),
26
+ ownerUsername: z.string().nullable().optional(),
27
+ })
28
+ .optional(),
29
+ }),
30
+ };
31
+
32
+ export async function POST(request: Request): Promise<NextResponse> {
33
+ return protectedApi({
34
+ request,
35
+ schema: Schema,
36
+ authorization: () => {},
37
+ activity: handler,
38
+ });
39
+ }
40
+
41
+ export async function handler(
42
+ data: (typeof Schema)["input"]["_type"],
43
+ req: Request
44
+ ): Promise<NextResponse<(typeof Schema)["output"]["_type"]>> {
45
+ let { data: beatmapPage, error: errorlast } = await supabase
46
+ .from("beatmapPages")
47
+ .select(
48
+ `
49
+ *,
50
+ beatmaps (
51
+ created_at,
52
+ playcount,
53
+ length,
54
+ ranked,
55
+ beatmapFile,
56
+ image,
57
+ starRating,
58
+ difficulty,
59
+ noteCount,
60
+ title
61
+ ),
62
+ profiles (
63
+ username
64
+ )
65
+ `
66
+ )
67
+ .eq("id", data.id)
68
+ .single();
69
+
70
+ if (!beatmapPage) return NextResponse.json({});
71
+
72
+ return NextResponse.json({
73
+ beatmap: {
74
+ playcount: beatmapPage.beatmaps?.playcount,
75
+ created_at: beatmapPage.created_at,
76
+ difficulty: beatmapPage.beatmaps?.difficulty,
77
+ noteCount: beatmapPage.beatmaps?.noteCount,
78
+ length: beatmapPage.beatmaps?.length,
79
+ title: beatmapPage.beatmaps?.title,
80
+ ranked: beatmapPage.beatmaps?.ranked,
81
+ beatmapFile: beatmapPage.beatmaps?.beatmapFile,
82
+ image: beatmapPage.beatmaps?.image,
83
+ starRating: beatmapPage.beatmaps?.starRating,
84
+ owner: beatmapPage.owner,
85
+ ownerUsername: beatmapPage.profiles?.username,
86
+ },
87
+ });
88
+ }
@@ -56,7 +56,7 @@ export async function handler({
56
56
  });
57
57
  }
58
58
 
59
- const key = `beatmap-${Date.now()}-${user.id}`;
59
+ const key = `beatmap-${Date.now()}-${user.id}.sspm`;
60
60
  const command = new PutObjectCommand({
61
61
  Bucket: "rhthia-avatars",
62
62
  Key: key,
@@ -57,7 +57,7 @@ export async function handler({
57
57
  .select("*")
58
58
  .single();
59
59
 
60
- if (upserted.error) {
60
+ if (upserted.error?.message.length) {
61
61
  return NextResponse.json({ error: upserted.error.message });
62
62
  }
63
63
  return NextResponse.json({});
package/index.ts CHANGED
@@ -25,6 +25,11 @@ import { Schema as GetAvatarUploadUrl } from "./api/getAvatarUploadUrl"
25
25
  export { Schema as SchemaGetAvatarUploadUrl } from "./api/getAvatarUploadUrl"
26
26
  export const getAvatarUploadUrl = handleApi({url:"/api/getAvatarUploadUrl",...GetAvatarUploadUrl})
27
27
 
28
+ // ./api/getBeatmapPage.ts API
29
+ import { Schema as GetBeatmapPage } from "./api/getBeatmapPage"
30
+ export { Schema as SchemaGetBeatmapPage } from "./api/getBeatmapPage"
31
+ export const getBeatmapPage = handleApi({url:"/api/getBeatmapPage",...GetBeatmapPage})
32
+
28
33
  // ./api/getLeaderboard.ts API
29
34
  import { Schema as GetLeaderboard } from "./api/getLeaderboard"
30
35
  export { Schema as SchemaGetLeaderboard } from "./api/getLeaderboard"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhythia-api",
3
- "version": "110.0.0",
3
+ "version": "111.0.0",
4
4
  "main": "index.ts",
5
5
  "scripts": {
6
6
  "update": "bun ./scripts/update.ts",
package/types/database.ts CHANGED
@@ -54,8 +54,10 @@ export type Database = {
54
54
  image: string | null
55
55
  length: number | null
56
56
  noteCount: number | null
57
+ pageId: number | null
57
58
  playcount: number | null
58
59
  ranked: boolean | null
60
+ starRating: number | null
59
61
  title: string | null
60
62
  }
61
63
  Insert: {
@@ -66,8 +68,10 @@ export type Database = {
66
68
  image?: string | null
67
69
  length?: number | null
68
70
  noteCount?: number | null
71
+ pageId?: number | null
69
72
  playcount?: number | null
70
73
  ranked?: boolean | null
74
+ starRating?: number | null
71
75
  title?: string | null
72
76
  }
73
77
  Update: {
@@ -78,11 +82,21 @@ export type Database = {
78
82
  image?: string | null
79
83
  length?: number | null
80
84
  noteCount?: number | null
85
+ pageId?: number | null
81
86
  playcount?: number | null
82
87
  ranked?: boolean | null
88
+ starRating?: number | null
83
89
  title?: string | null
84
90
  }
85
- Relationships: []
91
+ Relationships: [
92
+ {
93
+ foreignKeyName: "beatmaps_pageId_fkey"
94
+ columns: ["pageId"]
95
+ isOneToOne: false
96
+ referencedRelation: "beatmapPages"
97
+ referencedColumns: ["id"]
98
+ },
99
+ ]
86
100
  }
87
101
  profiles: {
88
102
  Row: {