rhythia-api 211.0.0 → 214.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/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # Rhythia Online API
2
+
3
+ This repo is a typed wrapper around Rhythia's backend endpoints. The helpers under `api/` are wired through `handleApi.ts` so consumers can call functions like `acceptInvite` or `createCollection` and let the shared client post to the hosted service while censoring stray profanity in responses.
4
+
5
+ Install dependencies with Bun, then reach for the scripts in `package.json`; `bun run dev` starts the local handler, `bun run test` exercises the contract tests, and `bun run pipeline:build-api` prepares the bundle that powers deployments. The production build is pushed to Vercel from the `main` branch, so anything merged there lands at the hosted Rhythia Online API a few moments later.
6
+
7
+ Runtime secrets live in environment variables. Upload endpoints expect `ACCESS_BUCKET` and `SECRET_BUCKET` for S3, the purchase flow checks `BUY_SECRET`, auth helpers derive tokens from `TOKEN_SECRET`, and the Supabase admin calls need `ADMIN_KEY`. The deploy script also looks for `GIT_USER`, `GIT_KEY`, `SOURCE_BRANCH`, and `TARGET_BRANCH` when the CI job mirrors changes upstream.
8
+
9
+ If you need to point at a different stack, call `setEnvironment` with `development`, `testing`, or `production` before making requests so the helper talks to the right Rhythia host.
@@ -13,8 +13,6 @@ export const Schema = {
13
13
  display_name: z.string(),
14
14
  avatar_url: z.string().nullable(),
15
15
  special_badge_count: z.number(),
16
- earned_badges: z.array(z.string()),
17
- all_badges: z.any(), // JSON type
18
16
  })
19
17
  ),
20
18
  total_count: z.number(),
@@ -0,0 +1,85 @@
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
+ import {
7
+ PutBucketCorsCommand,
8
+ PutObjectCommand,
9
+ S3Client,
10
+ } from "@aws-sdk/client-s3";
11
+ import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
12
+ import { validateIntrinsicToken } from "../utils/validateToken";
13
+ import { getUserBySession } from "../utils/getUserBySession";
14
+ import { User } from "@supabase/supabase-js";
15
+
16
+ const s3Client = new S3Client({
17
+ region: "auto",
18
+ endpoint: "https://s3.eu-central-003.backblazeb2.com",
19
+ credentials: {
20
+ secretAccessKey: process.env.SECRET_BUCKET || "",
21
+ accessKeyId: process.env.ACCESS_BUCKET || "",
22
+ },
23
+ });
24
+
25
+ export const Schema = {
26
+ input: z.strictObject({
27
+ session: z.string(),
28
+ contentLength: z.number(),
29
+ contentType: z.string(),
30
+ intrinsicToken: z.string(),
31
+ }),
32
+ output: z.strictObject({
33
+ error: z.string().optional(),
34
+ url: z.string().optional(),
35
+ objectKey: z.string().optional(),
36
+ }),
37
+ };
38
+
39
+ export async function POST(request: Request): Promise<NextResponse> {
40
+ return protectedApi({
41
+ request,
42
+ schema: Schema,
43
+ authorization: validUser,
44
+ activity: handler,
45
+ });
46
+ }
47
+
48
+ export async function handler({
49
+ session,
50
+ contentLength,
51
+ contentType,
52
+ intrinsicToken,
53
+ }: (typeof Schema)["input"]["_type"]): Promise<
54
+ NextResponse<(typeof Schema)["output"]["_type"]>
55
+ > {
56
+ const user = (await getUserBySession(session)) as User;
57
+
58
+ if (!validateIntrinsicToken(intrinsicToken)) {
59
+ return NextResponse.json({
60
+ error: "Invalid intrinsic token",
61
+ });
62
+ }
63
+
64
+ if (contentLength > 25000000) {
65
+ return NextResponse.json({
66
+ error: "Max content length exceeded.",
67
+ });
68
+ }
69
+
70
+ const key = `beatmap-video-${Date.now()}-${user.id}`;
71
+ const command = new PutObjectCommand({
72
+ Bucket: "rhthia-avatars",
73
+ Key: key,
74
+ ContentLength: contentLength,
75
+ ContentType: contentType,
76
+ });
77
+
78
+ const presigned = await getSignedUrl(s3Client, command, {
79
+ expiresIn: 3600,
80
+ });
81
+ return NextResponse.json({
82
+ url: presigned,
83
+ objectKey: key,
84
+ });
85
+ }
@@ -13,6 +13,7 @@ export const Schema = {
13
13
  beatmapHash: z.string().optional(),
14
14
  tags: z.string().optional(),
15
15
  description: z.string().optional(),
16
+ videoUrl: z.string().optional(),
16
17
  }),
17
18
  output: z.strictObject({
18
19
  error: z.string().optional(),
@@ -41,6 +42,7 @@ export async function handler({
41
42
  id,
42
43
  description,
43
44
  tags,
45
+ videoUrl,
44
46
  }: (typeof Schema)["input"]["_type"]): Promise<
45
47
  NextResponse<(typeof Schema)["output"]["_type"]>
46
48
  > {
@@ -82,8 +84,9 @@ export async function handler({
82
84
  owner: userData.id,
83
85
  description: description ? description : pageData.description,
84
86
  tags: tags ? tags : pageData.tags,
87
+ video_url: videoUrl ? videoUrl : pageData.video_url,
85
88
  updated_at: Date.now(),
86
- };
89
+ } as any;
87
90
 
88
91
  if (beatmapHash && beatmapData) {
89
92
  upsertPayload["title"] = beatmapData.title;
package/index.ts CHANGED
@@ -384,8 +384,6 @@ export const Schema = {
384
384
  display_name: z.string(),
385
385
  avatar_url: z.string().nullable(),
386
386
  special_badge_count: z.number(),
387
- earned_badges: z.array(z.string()),
388
- all_badges: z.any(), // JSON type
389
387
  })
390
388
  ),
391
389
  total_count: z.number(),
@@ -1166,6 +1164,26 @@ import { Schema as GetVerified } from "./api/getVerified"
1166
1164
  export { Schema as SchemaGetVerified } from "./api/getVerified"
1167
1165
  export const getVerified = handleApi({url:"/api/getVerified",...GetVerified})
1168
1166
 
1167
+ // ./api/getVideoUploadUrl.ts API
1168
+
1169
+ /*
1170
+ export const Schema = {
1171
+ input: z.strictObject({
1172
+ session: z.string(),
1173
+ contentLength: z.number(),
1174
+ contentType: z.string(),
1175
+ intrinsicToken: z.string(),
1176
+ }),
1177
+ output: z.strictObject({
1178
+ error: z.string().optional(),
1179
+ url: z.string().optional(),
1180
+ objectKey: z.string().optional(),
1181
+ }),
1182
+ };*/
1183
+ import { Schema as GetVideoUploadUrl } from "./api/getVideoUploadUrl"
1184
+ export { Schema as SchemaGetVideoUploadUrl } from "./api/getVideoUploadUrl"
1185
+ export const getVideoUploadUrl = handleApi({url:"/api/getVideoUploadUrl",...GetVideoUploadUrl})
1186
+
1169
1187
  // ./api/nominateMap.ts API
1170
1188
 
1171
1189
  /*
@@ -1295,6 +1313,7 @@ export const Schema = {
1295
1313
  beatmapHash: z.string().optional(),
1296
1314
  tags: z.string().optional(),
1297
1315
  description: z.string().optional(),
1316
+ videoUrl: z.string().optional(),
1298
1317
  }),
1299
1318
  output: z.strictObject({
1300
1319
  error: z.string().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rhythia-api",
3
- "version": "211.0.0",
3
+ "version": "214.0.0",
4
4
  "main": "index.ts",
5
5
  "author": "online-contributors-cunev",
6
6
  "scripts": {
@@ -43,7 +43,7 @@
43
43
  "sharp": "^0.33.5",
44
44
  "short-uuid": "^5.2.0",
45
45
  "simple-git": "^3.25.0",
46
- "supabase": "^2.19.7",
46
+ "supabase": "^2.53.6",
47
47
  "tsx": "^4.17.0",
48
48
  "utf-8-validate": "^6.0.4",
49
49
  "uuid": "^11.1.0",
package/types/database.ts CHANGED
@@ -10,7 +10,7 @@ export type Database = {
10
10
  // Allows to automatically instantiate createClient with right options
11
11
  // instead of createClient<Database, { PostgrestVersion: 'XX' }>(URL, KEY)
12
12
  __InternalSupabase: {
13
- PostgrestVersion: "12.2.3 (519615d)"
13
+ PostgrestVersion: "13.0.4"
14
14
  }
15
15
  public: {
16
16
  Tables: {
@@ -158,6 +158,7 @@ export type Database = {
158
158
  tags: string
159
159
  title: string | null
160
160
  updated_at: number | null
161
+ video_url: string | null
161
162
  }
162
163
  Insert: {
163
164
  created_at?: string
@@ -172,6 +173,7 @@ export type Database = {
172
173
  tags?: string
173
174
  title?: string | null
174
175
  updated_at?: number | null
176
+ video_url?: string | null
175
177
  }
176
178
  Update: {
177
179
  created_at?: string
@@ -186,6 +188,7 @@ export type Database = {
186
188
  tags?: string
187
189
  title?: string | null
188
190
  updated_at?: number | null
191
+ video_url?: string | null
189
192
  }
190
193
  Relationships: [
191
194
  {
@@ -703,10 +706,8 @@ export type Database = {
703
706
  get_badge_leaderboard: {
704
707
  Args: { p_limit?: number }
705
708
  Returns: {
706
- all_badges: Json
707
709
  avatar_url: string
708
710
  display_name: string
709
- earned_badges: string[]
710
711
  id: number
711
712
  special_badge_count: number
712
713
  }[]