rhythia-api 216.0.0 → 225.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/.env +12 -0
- package/.yarnrc +1 -0
- package/README.md +2 -0
- package/api/createBeatmap.ts +1 -1
- package/api/deleteBeatmapPage.ts +12 -5
- package/api/executeAdminOperation.ts +39 -3
- package/api/getBadgeLeaders.ts +25 -3
- package/api/getBadgedUsers.ts +2 -1
- package/api/getBeatmapComments.ts +14 -0
- package/api/getBeatmapPage.ts +32 -9
- package/api/getBeatmapPageById.ts +39 -7
- package/api/getLeaderboard.ts +173 -44
- package/api/getOnlinePlayers.ts +78 -0
- package/api/getProfile.ts +21 -7
- package/api/getPublicStats.ts +5 -2
- package/api/getUserScores.ts +46 -17
- package/api/postBeatmapComment.ts +4 -0
- package/api/submitScore.ts +30 -0
- package/api/submitScoreInternal.ts +426 -0
- package/handleApi.ts +7 -3
- package/index.ts +54 -0
- package/package.json +7 -4
- package/types/database.ts +1179 -1084
- package/utils/activityStatus.ts +36 -0
- package/utils/cache.ts +60 -0
- package/utils/leaderboardCache.ts +8 -0
- package/utils/requestUtils.ts +2 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { supabase } from "./supabase";
|
|
2
|
+
|
|
3
|
+
export const INACTIVITY_WINDOW_MS = 30 * 24 * 60 * 60 * 1000;
|
|
4
|
+
|
|
5
|
+
export type ActivityStatus = "active" | "inactive";
|
|
6
|
+
|
|
7
|
+
export function getScoreActivityCutoffIso(now = Date.now()) {
|
|
8
|
+
return new Date(now - INACTIVITY_WINDOW_MS).toISOString();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function getActivityStatusForUserId(userId: number) {
|
|
12
|
+
const cutoffIso = getScoreActivityCutoffIso();
|
|
13
|
+
const { data } = await supabase
|
|
14
|
+
.from("scores")
|
|
15
|
+
.select("id")
|
|
16
|
+
.eq("userId", userId)
|
|
17
|
+
.gte("created_at", cutoffIso)
|
|
18
|
+
.limit(1);
|
|
19
|
+
|
|
20
|
+
return data?.length ? ("active" as const) : ("inactive" as const);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function getActiveProfileIdSet(profileIds: number[]) {
|
|
24
|
+
if (!profileIds.length) return new Set<number>();
|
|
25
|
+
|
|
26
|
+
const cutoffIso = getScoreActivityCutoffIso();
|
|
27
|
+
const { data } = await supabase
|
|
28
|
+
.from("profiles")
|
|
29
|
+
.select("id,scores!inner(id)")
|
|
30
|
+
.in("id", profileIds)
|
|
31
|
+
.gte("scores.created_at", cutoffIso)
|
|
32
|
+
.limit(1, { foreignTable: "scores" });
|
|
33
|
+
|
|
34
|
+
return new Set((data || []).map((row) => row.id));
|
|
35
|
+
}
|
|
36
|
+
|
package/utils/cache.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { supabase } from "./supabase";
|
|
2
|
+
|
|
3
|
+
const CACHE_TABLE = "cache";
|
|
4
|
+
|
|
5
|
+
type CacheKey = string | number;
|
|
6
|
+
|
|
7
|
+
const normalizeKey = (key: CacheKey) => String(key);
|
|
8
|
+
|
|
9
|
+
export async function getCacheValue<T = unknown>(
|
|
10
|
+
key: CacheKey
|
|
11
|
+
): Promise<T | null> {
|
|
12
|
+
const { data, error } = await supabase
|
|
13
|
+
.from(CACHE_TABLE)
|
|
14
|
+
.select("value")
|
|
15
|
+
.eq("key", normalizeKey(key))
|
|
16
|
+
.maybeSingle();
|
|
17
|
+
|
|
18
|
+
if (error) {
|
|
19
|
+
console.error("getCacheValue error", error);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (data?.value as T) ?? null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function setCacheValue<T = unknown>(
|
|
27
|
+
key: CacheKey,
|
|
28
|
+
value: T
|
|
29
|
+
): Promise<void> {
|
|
30
|
+
const { error } = await supabase
|
|
31
|
+
.from(CACHE_TABLE)
|
|
32
|
+
.upsert({ key: normalizeKey(key), value } as any);
|
|
33
|
+
|
|
34
|
+
if (error) {
|
|
35
|
+
console.error("setCacheValue error", error);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function invalidateCache(key: CacheKey): Promise<void> {
|
|
40
|
+
const { error } = await supabase
|
|
41
|
+
.from(CACHE_TABLE)
|
|
42
|
+
.delete()
|
|
43
|
+
.eq("key", normalizeKey(key));
|
|
44
|
+
|
|
45
|
+
if (error) {
|
|
46
|
+
console.error("invalidateCache error", error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function invalidateCachePrefix(prefix: CacheKey): Promise<void> {
|
|
51
|
+
const normalizedPrefix = normalizeKey(prefix);
|
|
52
|
+
const { error } = await supabase
|
|
53
|
+
.from(CACHE_TABLE)
|
|
54
|
+
.delete()
|
|
55
|
+
.like("key", `${normalizedPrefix}%`);
|
|
56
|
+
|
|
57
|
+
if (error) {
|
|
58
|
+
console.error("invalidateCachePrefix error", error);
|
|
59
|
+
}
|
|
60
|
+
}
|
package/utils/requestUtils.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { supabase } from "./supabase";
|
|
|
5
5
|
|
|
6
6
|
interface Props<
|
|
7
7
|
K = (...args: any[]) => Promise<NextResponse<any>>,
|
|
8
|
-
T = ZodObject<any
|
|
8
|
+
T = ZodObject<any>,
|
|
9
9
|
> {
|
|
10
10
|
request: Request;
|
|
11
11
|
schema: { input: T; output: T };
|
|
@@ -49,6 +49,7 @@ export async function protectedApi({
|
|
|
49
49
|
}
|
|
50
50
|
return await activity(data, request);
|
|
51
51
|
} catch (error) {
|
|
52
|
+
console.log(`Couldn't parse`, error.toString());
|
|
52
53
|
return NextResponse.json({ error: error.toString() }, { status: 400 });
|
|
53
54
|
}
|
|
54
55
|
}
|