rhythia-api 233.0.0 → 235.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/.codex +0 -0
- package/.env +1 -12
- package/README.md +4 -4
- package/api/acceptInvite.ts +1 -1
- package/api/addCollectionMap.ts +1 -1
- package/api/chartPublicStats.ts +1 -1
- package/api/checkQualified.ts +93 -93
- package/api/createBeatmap.ts +53 -62
- package/api/createBeatmapPage.ts +1 -1
- package/api/createClan.ts +1 -1
- package/api/createCollection.ts +1 -1
- package/api/createInvite.ts +1 -1
- package/api/createSupporter.ts +1 -1
- package/api/deleteBeatmapPage.ts +2 -5
- package/api/deleteCollection.ts +1 -1
- package/api/deleteCollectionMap.ts +1 -1
- package/api/editAboutMe.ts +1 -1
- package/api/editClan.ts +1 -1
- package/api/editCollection.ts +1 -2
- package/api/editProfile.ts +1 -1
- package/api/enhancedSearch.ts +113 -113
- package/api/executeAdminOperation.ts +1 -22
- package/api/getAvatarUploadUrl.ts +1 -1
- package/api/getBadgeLeaders.ts +1 -1
- package/api/getBadgedUsers.ts +1 -1
- package/api/getBeatmapComments.ts +1 -1
- package/api/getBeatmapPage.ts +74 -106
- package/api/getBeatmapPageById.ts +70 -109
- package/api/getBeatmapStarRating.ts +1 -1
- package/api/getBeatmaps.ts +123 -93
- package/api/getClan.ts +1 -1
- package/api/getClans.ts +1 -1
- package/api/getCollection.ts +1 -1
- package/api/getCollections.ts +1 -1
- package/api/getInventory.ts +1 -1
- package/api/getLeaderboard.ts +1 -1
- package/api/getMapUploadUrl.ts +2 -2
- package/api/getOnlinePlayers.ts +1 -1
- package/api/getPassToken.ts +1 -1
- package/api/getProfile.ts +51 -31
- package/api/getPublicStats.ts +5 -5
- package/api/getRawStarRating.ts +1 -1
- package/api/getScore.ts +1 -1
- package/api/getStoryBeatmaps.ts +1 -1
- package/api/getTimestamp.ts +1 -1
- package/api/getUserScores.ts +19 -19
- package/api/getVerified.ts +1 -1
- package/api/getVideoUploadUrl.ts +1 -1
- package/api/postBeatmapComment.ts +1 -1
- package/api/qualifyMap.ts +97 -92
- package/api/rankMapsArchive.ts +20 -20
- package/api/searchUsers.ts +1 -1
- package/api/setPasskey.ts +1 -1
- package/api/submitScore.ts +1 -6
- package/api/submitScoreInternal.ts +461 -449
- package/api/updateBeatmapPage.ts +1 -1
- package/api/vetoMap.ts +101 -101
- package/index.ts +180 -167
- package/package.json +7 -12
- package/queries/admin_delete_user.sql +39 -39
- package/queries/admin_exclude_user.sql +21 -21
- package/queries/admin_invalidate_ranked_scores.sql +18 -18
- package/queries/admin_log_action.sql +10 -10
- package/queries/admin_profanity_clear.sql +29 -29
- package/queries/admin_remove_all_scores.sql +29 -29
- package/queries/admin_restrict_user.sql +21 -21
- package/queries/admin_search_users.sql +24 -24
- package/queries/admin_silence_user.sql +21 -21
- package/queries/admin_unban_user.sql +21 -21
- package/queries/enhanced_search.sql +217 -217
- package/queries/get_badge_leaderboard.sql +50 -50
- package/queries/get_clan_leaderboard.sql +68 -68
- package/queries/get_collections_v4.sql +109 -109
- package/queries/get_top_scores_for_beatmap.sql +44 -44
- package/queries/get_top_scores_for_beatmap3.sql +38 -0
- package/queries/get_user_by_email.sql +32 -32
- package/queries/get_user_scores_lastday.sql +47 -47
- package/queries/get_user_scores_reign.sql +31 -31
- package/queries/get_user_scores_top_and_stats.sql +84 -84
- package/queries/grant_special_badges.sql +69 -69
- package/types/database.ts +1288 -1248
- package/utils/beatmapTopScores.ts +84 -0
- package/utils/mapLifecycleWebhook.ts +287 -277
- package/utils/requestGeo.ts +13 -0
- package/utils/requestUtils.ts +127 -127
- package/utils/response.ts +11 -0
- package/worker.ts +189 -0
- package/wrangler.jsonc +10 -0
- package/index.html +0 -3
- package/vercel.json +0 -13
package/.codex
ADDED
|
File without changes
|
package/.env
CHANGED
|
@@ -1,12 +1 @@
|
|
|
1
|
-
|
|
2
|
-
REDIS_USERNAME=default
|
|
3
|
-
REDIS_PASSWORD=yg7hYF9LPhGJRmORVIjMB8FWP2axPGto
|
|
4
|
-
REDIS_HOST=redis-18304.c55.eu-central-1-1.ec2.cloud.redislabs.com
|
|
5
|
-
CF_NAMESPACE_ID=571cf88650514eeba649517a75ede74a
|
|
6
|
-
CF_API_TOKEN=Ldsh-UKJZw0feHmZOvhSCxaurwJBVW12LgnV_v38
|
|
7
|
-
CF_ACCOUNT_ID=571cf88650514eeba649517a75ede74a
|
|
8
|
-
TOKEN_SECRET=fa686bfdffd3758f6377abbc23bf3d9bdc1a0dda4a6e7f8dbdd579fa1ff6d7e2
|
|
9
|
-
ACCESS_BUCKET=003c245e893e8060000000003
|
|
10
|
-
SECRET_BUCKET=K003LpAu8X+2lJ09EB8NtPL/OZXV8ts
|
|
11
|
-
ADMIN_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBma2FqbmdibGxjYmR6b3lscnZwIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcyOTAxNTMwNSwiZXhwIjoyMDQ0NTkxMzA1fQ.dKg8Wq3zZEBAH63V7q1D8a8N-d6qkB5Inm524Vmso3k
|
|
12
|
-
PROD_DO_NOT_WRITE_PG=postgresql://postgres:huaUpTz3d3p7w6VU@db.pfkajngbllcbdzoylrvp.supabase.co:5432/postgres
|
|
1
|
+
PROD_DO_NOT_WRITE_PG=postgresql://postgres:pMCnLpLl6UmdcStP@db.pfkajngbllcbdzoylrvp.supabase.co:5432/postgres
|
package/README.md
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
-
|
|
5
|
+
The deploy target is Cloudflare Workers. `worker.ts` dispatches the existing `api/*.ts` handlers, keeps the old `/api/*` paths, and applies the same CORS policy the old deployment used.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Local development uses Wrangler and reads secrets from the existing `.env` file. Install dependencies, then run `yarn dev`. Deploy with `yarn deploy`. For production, mirror the same keys in the Cloudflare dashboard or with Wrangler secrets.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
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`. Score ingestion also still expects the Redis credentials already used by `submitScoreInternal`.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
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.
|
package/api/acceptInvite.ts
CHANGED
package/api/addCollectionMap.ts
CHANGED
package/api/chartPublicStats.ts
CHANGED
package/api/checkQualified.ts
CHANGED
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
import { NextResponse } from "
|
|
2
|
-
import z from "zod";
|
|
3
|
-
import { protectedApi } from "../utils/requestUtils";
|
|
4
|
-
import { supabase } from "../utils/supabase";
|
|
5
|
-
import { postMapLifecycleWebhook } from "../utils/mapLifecycleWebhook";
|
|
6
|
-
|
|
7
|
-
const INTERNAL_SECRET = "testing-1";
|
|
8
|
-
const THREE_DAYS_MS = 3 * 24 * 60 * 60 * 1000;
|
|
9
|
-
|
|
10
|
-
export const Schema = {
|
|
11
|
-
input: z.strictObject({
|
|
12
|
-
secret: z.string(),
|
|
13
|
-
}),
|
|
14
|
-
output: z.object({
|
|
15
|
-
error: z.string().optional(),
|
|
16
|
-
updated: z.number(),
|
|
17
|
-
}),
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export async function POST(request: Request): Promise<NextResponse> {
|
|
21
|
-
return protectedApi({
|
|
22
|
-
request,
|
|
23
|
-
schema: Schema,
|
|
24
|
-
authorization: () => {},
|
|
25
|
-
activity: handler,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export async function handler({
|
|
30
|
-
secret,
|
|
31
|
-
}: (typeof Schema)["input"]["_type"]): Promise<
|
|
32
|
-
NextResponse<(typeof Schema)["output"]["_type"]>
|
|
33
|
-
> {
|
|
34
|
-
if (secret !== INTERNAL_SECRET) {
|
|
35
|
-
return NextResponse.json({ error: "Internal usage only", updated: 0 });
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const { data: qualifiedMaps, error: qualifiedMapsError } = await supabase
|
|
39
|
-
.from("beatmapPages")
|
|
40
|
-
.select("id,status,qualifiedAt")
|
|
41
|
-
.eq("qualified", true)
|
|
42
|
-
.not("qualifiedAt", "is", null);
|
|
43
|
-
|
|
44
|
-
if (qualifiedMapsError) {
|
|
45
|
-
return NextResponse.json({ error: qualifiedMapsError.message, updated: 0 });
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const cutoffTimestamp = Date.now() - THREE_DAYS_MS;
|
|
49
|
-
const mapsToRank = (qualifiedMaps || []).filter((mapData) => {
|
|
50
|
-
if (mapData.status === "RANKED") {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const qualifiedAt = Date.parse(mapData.qualifiedAt || "");
|
|
55
|
-
if (!Number.isFinite(qualifiedAt)) {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return qualifiedAt <= cutoffTimestamp;
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
if (mapsToRank.length === 0) {
|
|
63
|
-
return NextResponse.json({ updated: 0 });
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const { error: updateError } = await supabase
|
|
67
|
-
.from("beatmapPages")
|
|
68
|
-
.update({
|
|
69
|
-
status: "RANKED",
|
|
70
|
-
ranked_at: Date.now(),
|
|
71
|
-
qualified: false,
|
|
72
|
-
qualifiedAt: null,
|
|
73
|
-
})
|
|
74
|
-
.in(
|
|
75
|
-
"id",
|
|
76
|
-
mapsToRank.map((mapData) => mapData.id)
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
if (updateError) {
|
|
80
|
-
return NextResponse.json({ error: updateError.message, updated: 0 });
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
await Promise.allSettled(
|
|
84
|
-
mapsToRank.map((mapData) =>
|
|
85
|
-
postMapLifecycleWebhook({
|
|
86
|
-
mapId: mapData.id,
|
|
87
|
-
event: "ranked",
|
|
88
|
-
})
|
|
89
|
-
)
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
return NextResponse.json({ updated: mapsToRank.length });
|
|
93
|
-
}
|
|
1
|
+
import { NextResponse } from "../utils/response";
|
|
2
|
+
import z from "zod";
|
|
3
|
+
import { protectedApi } from "../utils/requestUtils";
|
|
4
|
+
import { supabase } from "../utils/supabase";
|
|
5
|
+
import { postMapLifecycleWebhook } from "../utils/mapLifecycleWebhook";
|
|
6
|
+
|
|
7
|
+
const INTERNAL_SECRET = "testing-1";
|
|
8
|
+
const THREE_DAYS_MS = 3 * 24 * 60 * 60 * 1000;
|
|
9
|
+
|
|
10
|
+
export const Schema = {
|
|
11
|
+
input: z.strictObject({
|
|
12
|
+
secret: z.string(),
|
|
13
|
+
}),
|
|
14
|
+
output: z.object({
|
|
15
|
+
error: z.string().optional(),
|
|
16
|
+
updated: z.number(),
|
|
17
|
+
}),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export async function POST(request: Request): Promise<NextResponse> {
|
|
21
|
+
return protectedApi({
|
|
22
|
+
request,
|
|
23
|
+
schema: Schema,
|
|
24
|
+
authorization: () => {},
|
|
25
|
+
activity: handler,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function handler({
|
|
30
|
+
secret,
|
|
31
|
+
}: (typeof Schema)["input"]["_type"]): Promise<
|
|
32
|
+
NextResponse<(typeof Schema)["output"]["_type"]>
|
|
33
|
+
> {
|
|
34
|
+
if (secret !== INTERNAL_SECRET) {
|
|
35
|
+
return NextResponse.json({ error: "Internal usage only", updated: 0 });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { data: qualifiedMaps, error: qualifiedMapsError } = await supabase
|
|
39
|
+
.from("beatmapPages")
|
|
40
|
+
.select("id,status,qualifiedAt")
|
|
41
|
+
.eq("qualified", true)
|
|
42
|
+
.not("qualifiedAt", "is", null);
|
|
43
|
+
|
|
44
|
+
if (qualifiedMapsError) {
|
|
45
|
+
return NextResponse.json({ error: qualifiedMapsError.message, updated: 0 });
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const cutoffTimestamp = Date.now() - THREE_DAYS_MS;
|
|
49
|
+
const mapsToRank = (qualifiedMaps || []).filter((mapData) => {
|
|
50
|
+
if (mapData.status === "RANKED") {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const qualifiedAt = Date.parse(mapData.qualifiedAt || "");
|
|
55
|
+
if (!Number.isFinite(qualifiedAt)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return qualifiedAt <= cutoffTimestamp;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (mapsToRank.length === 0) {
|
|
63
|
+
return NextResponse.json({ updated: 0 });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const { error: updateError } = await supabase
|
|
67
|
+
.from("beatmapPages")
|
|
68
|
+
.update({
|
|
69
|
+
status: "RANKED",
|
|
70
|
+
ranked_at: Date.now(),
|
|
71
|
+
qualified: false,
|
|
72
|
+
qualifiedAt: null,
|
|
73
|
+
})
|
|
74
|
+
.in(
|
|
75
|
+
"id",
|
|
76
|
+
mapsToRank.map((mapData) => mapData.id)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (updateError) {
|
|
80
|
+
return NextResponse.json({ error: updateError.message, updated: 0 });
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await Promise.allSettled(
|
|
84
|
+
mapsToRank.map((mapData) =>
|
|
85
|
+
postMapLifecycleWebhook({
|
|
86
|
+
mapId: mapData.id,
|
|
87
|
+
event: "ranked",
|
|
88
|
+
})
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return NextResponse.json({ updated: mapsToRank.length });
|
|
93
|
+
}
|
package/api/createBeatmap.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { NextResponse } from "
|
|
1
|
+
import { NextResponse } from "../utils/response";
|
|
2
2
|
import z from "zod";
|
|
3
3
|
import { protectedApi, validUser } from "../utils/requestUtils";
|
|
4
4
|
import { SSPMParser } from "../utils/star-calc/sspmParser";
|
|
@@ -7,7 +7,6 @@ import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
|
|
7
7
|
import { rateMap } from "../utils/star-calc";
|
|
8
8
|
import { getUserBySession } from "../utils/getUserBySession";
|
|
9
9
|
import { User } from "@supabase/supabase-js";
|
|
10
|
-
import { NodeHttpHandler } from "@aws-sdk/node-http-handler";
|
|
11
10
|
|
|
12
11
|
const s3Client = new S3Client({
|
|
13
12
|
region: "auto",
|
|
@@ -19,21 +18,10 @@ const s3Client = new S3Client({
|
|
|
19
18
|
requestChecksumCalculation: "WHEN_REQUIRED",
|
|
20
19
|
});
|
|
21
20
|
|
|
22
|
-
// Remove ALL validation and checksum middleware
|
|
23
|
-
const middlewareToRemove = [
|
|
24
|
-
"build:checksum",
|
|
25
|
-
"build:content-checksum",
|
|
26
|
-
"build:content-md5",
|
|
27
|
-
"validate",
|
|
28
|
-
"validateChecksum",
|
|
29
|
-
];
|
|
30
|
-
|
|
31
21
|
s3Client.middlewareStack.add(
|
|
32
22
|
(next) =>
|
|
33
23
|
async (args): Promise<any> => {
|
|
34
24
|
const request = args.request as RequestInit;
|
|
35
|
-
|
|
36
|
-
// Remove checksum headers
|
|
37
25
|
const headers = request.headers as Record<string, string>;
|
|
38
26
|
delete headers["x-amz-checksum-crc32"];
|
|
39
27
|
delete headers["x-amz-checksum-crc32c"];
|
|
@@ -41,21 +29,51 @@ s3Client.middlewareStack.add(
|
|
|
41
29
|
delete headers["x-amz-checksum-sha256"];
|
|
42
30
|
request.headers = headers;
|
|
43
31
|
|
|
44
|
-
Object.entries(request.headers).forEach(
|
|
45
|
-
// @ts-ignore
|
|
46
|
-
([key, value]: [string, string]): void => {
|
|
47
|
-
if (!request.headers) {
|
|
48
|
-
request.headers = {};
|
|
49
|
-
}
|
|
50
|
-
(request.headers as Record<string, string>)[key] = value;
|
|
51
|
-
}
|
|
52
|
-
);
|
|
53
|
-
|
|
54
32
|
return next(args);
|
|
55
33
|
},
|
|
56
34
|
{ step: "build", name: "customHeaders" }
|
|
57
35
|
);
|
|
58
36
|
|
|
37
|
+
function getCoverContentType(cover: Uint8Array) {
|
|
38
|
+
if (
|
|
39
|
+
cover.length >= 8 &&
|
|
40
|
+
cover[0] === 0x89 &&
|
|
41
|
+
cover[1] === 0x50 &&
|
|
42
|
+
cover[2] === 0x4e &&
|
|
43
|
+
cover[3] === 0x47
|
|
44
|
+
) {
|
|
45
|
+
return "image/png";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (cover.length >= 3 && cover[0] === 0xff && cover[1] === 0xd8) {
|
|
49
|
+
return "image/jpeg";
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (
|
|
53
|
+
cover.length >= 12 &&
|
|
54
|
+
cover[0] === 0x52 &&
|
|
55
|
+
cover[1] === 0x49 &&
|
|
56
|
+
cover[2] === 0x46 &&
|
|
57
|
+
cover[3] === 0x46 &&
|
|
58
|
+
cover[8] === 0x57 &&
|
|
59
|
+
cover[9] === 0x45 &&
|
|
60
|
+
cover[10] === 0x42 &&
|
|
61
|
+
cover[11] === 0x50
|
|
62
|
+
) {
|
|
63
|
+
return "image/webp";
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (
|
|
67
|
+
cover.length >= 6 &&
|
|
68
|
+
cover[0] === 0x47 &&
|
|
69
|
+
cover[1] === 0x49 &&
|
|
70
|
+
cover[2] === 0x46
|
|
71
|
+
) {
|
|
72
|
+
return "image/gif";
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return "image/jpeg";
|
|
76
|
+
}
|
|
59
77
|
export const Schema = {
|
|
60
78
|
input: z.strictObject({
|
|
61
79
|
url: z.string(),
|
|
@@ -147,44 +165,18 @@ export async function handler({
|
|
|
147
165
|
return NextResponse.json({ error: "Already Exists" });
|
|
148
166
|
}
|
|
149
167
|
}
|
|
150
|
-
|
|
151
168
|
const imgkey = `beatmap-img-${Date.now()}-${digested}`;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
largeBuffer = await require("sharp")(parsedData.cover)
|
|
164
|
-
.resize(850)
|
|
165
|
-
.jpeg({ mozjpeg: true })
|
|
166
|
-
.toBuffer();
|
|
167
|
-
} catch (error) {}
|
|
168
|
-
|
|
169
|
-
// Images
|
|
170
|
-
const command = new PutObjectCommand({
|
|
171
|
-
Bucket: "rhthia-avatars",
|
|
172
|
-
Key: imgkey,
|
|
173
|
-
Body: buffer,
|
|
174
|
-
ContentType: "image/jpeg",
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
await s3Client.send(command);
|
|
178
|
-
|
|
179
|
-
const command2 = new PutObjectCommand({
|
|
180
|
-
Bucket: "rhthia-avatars",
|
|
181
|
-
Key: imgkey + "large",
|
|
182
|
-
Body: largeBuffer,
|
|
183
|
-
ContentType: "image/jpeg",
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
await s3Client.send(command2);
|
|
187
|
-
// Images End
|
|
169
|
+
const cover = parsedData.cover || Buffer.from([]);
|
|
170
|
+
const coverContentType = getCoverContentType(cover);
|
|
171
|
+
|
|
172
|
+
await s3Client.send(
|
|
173
|
+
new PutObjectCommand({
|
|
174
|
+
Bucket: "rhthia-avatars",
|
|
175
|
+
Key: imgkey,
|
|
176
|
+
Body: cover,
|
|
177
|
+
ContentType: coverContentType,
|
|
178
|
+
})
|
|
179
|
+
);
|
|
188
180
|
|
|
189
181
|
const markers = parsedData.markers.sort((a, b) => a.position - b.position);
|
|
190
182
|
|
|
@@ -197,10 +189,9 @@ export async function handler({
|
|
|
197
189
|
length: markers[markers.length - 1].position,
|
|
198
190
|
beatmapFile: url,
|
|
199
191
|
image: `https://static.rhythia.com/${imgkey}`,
|
|
200
|
-
imageLarge: `https://static.rhythia.com/${imgkey}
|
|
192
|
+
imageLarge: `https://static.rhythia.com/${imgkey}`,
|
|
201
193
|
starRating: rateMap(parsedData),
|
|
202
194
|
});
|
|
203
|
-
|
|
204
195
|
if (upserted.error?.message.length) {
|
|
205
196
|
return NextResponse.json({ error: upserted.error.message });
|
|
206
197
|
}
|
package/api/createBeatmapPage.ts
CHANGED
package/api/createClan.ts
CHANGED
package/api/createCollection.ts
CHANGED
package/api/createInvite.ts
CHANGED
package/api/createSupporter.ts
CHANGED
package/api/deleteBeatmapPage.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { NextResponse } from "
|
|
1
|
+
import { NextResponse } from "../utils/response";
|
|
2
2
|
import z from "zod";
|
|
3
3
|
import { protectedApi, validUser } from "../utils/requestUtils";
|
|
4
4
|
import { supabase } from "../utils/supabase";
|
|
5
5
|
import { getUserBySession } from "../utils/getUserBySession";
|
|
6
6
|
import { User } from "@supabase/supabase-js";
|
|
7
|
-
import { invalidateCache
|
|
7
|
+
import { invalidateCache } from "../utils/cache";
|
|
8
8
|
|
|
9
9
|
export const Schema = {
|
|
10
10
|
input: z.strictObject({
|
|
@@ -76,9 +76,6 @@ export async function handler({
|
|
|
76
76
|
.eq("beatmapHash", beatmapData.beatmapHash);
|
|
77
77
|
|
|
78
78
|
await invalidateCache(`beatmap-comments:${id}`);
|
|
79
|
-
if (pageData.latestBeatmapHash) {
|
|
80
|
-
await invalidateCachePrefix(`beatmap-scores:${pageData.latestBeatmapHash}`);
|
|
81
|
-
}
|
|
82
79
|
|
|
83
80
|
return NextResponse.json({});
|
|
84
81
|
}
|
package/api/deleteCollection.ts
CHANGED
package/api/editAboutMe.ts
CHANGED
package/api/editClan.ts
CHANGED
package/api/editCollection.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { NextResponse } from "
|
|
1
|
+
import { NextResponse } from "../utils/response";
|
|
2
2
|
import z from "zod";
|
|
3
3
|
import { protectedApi, validUser } from "../utils/requestUtils";
|
|
4
4
|
import { supabase } from "../utils/supabase";
|
|
5
5
|
import { getUserBySession } from "../utils/getUserBySession";
|
|
6
6
|
import { User } from "@supabase/supabase-js";
|
|
7
|
-
import { describe } from "node:test";
|
|
8
7
|
|
|
9
8
|
export const Schema = {
|
|
10
9
|
input: z.strictObject({
|
package/api/editProfile.ts
CHANGED