desloppify: code health overhaul plus abuse hardening (140 green, strict 87.2)
Honest baseline after removing 45 gaming suppressions. Structural: lib/game plus root hooks extraction, CORS origin echo, DEV_ORIGINS layering. Fixes: error-cause binding, discord 5xx split, null-cache guard, co-located tests, rejecting-fetch coverage, naming and typing cleanup, socket controller, cache bound, cascade helpers, latch resets. Hardening: per-IP rate limit on API routes, deterministic-miss UX, retry cooldown plus cap. Suite 140 green, build plus lint clean.
This commit is contained in:
78 files changed
+4908
-1430
No files matched your search
@@ -0,0 +1,95 @@
|
||||
import { warnDev } from "@/lib/log";
|
||||
|
||||
export function discordAppIconUrl(
|
||||
applicationId: string,
|
||||
iconHash: string | null,
|
||||
size = 128,
|
||||
): string | null {
|
||||
if (!iconHash) {
|
||||
return null;
|
||||
}
|
||||
const extension = iconHash.startsWith("a_") ? "gif" : "png";
|
||||
return `https://cdn.discordapp.com/app-icons/${applicationId}/${iconHash}.${extension}?size=${size}`;
|
||||
}
|
||||
|
||||
export function discordGameUrl(gameId: string): string {
|
||||
return `https://discord.com/games/${gameId}`;
|
||||
}
|
||||
|
||||
export function discordAppCoverUrl(
|
||||
applicationId: string,
|
||||
coverHash: string | null,
|
||||
size = 512,
|
||||
): string | null {
|
||||
if (!coverHash) {
|
||||
return null;
|
||||
}
|
||||
return `https://cdn.discordapp.com/app-icons/${applicationId}/${coverHash}.png?size=${size}`;
|
||||
}
|
||||
|
||||
export function steamdbAppUrl(steamId: string): string {
|
||||
return `https://steamdb.info/app/${steamId}/`;
|
||||
}
|
||||
|
||||
export interface DiscordAppInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
icon: string | null;
|
||||
iconUrl: string | null;
|
||||
coverImage: string | null;
|
||||
coverUrl: string | null;
|
||||
gameId: string;
|
||||
gameUrl: string;
|
||||
}
|
||||
|
||||
interface DiscordRpcResponse {
|
||||
id?: string;
|
||||
name?: string;
|
||||
icon?: string | null;
|
||||
cover_image?: string | null;
|
||||
linked_games?: Array<{ id: string }> | null;
|
||||
}
|
||||
|
||||
export async function getDiscordAppInfo(applicationId: string): Promise<DiscordAppInfo | null> {
|
||||
if (!/^\d+$/.test(applicationId)) {
|
||||
return null;
|
||||
}
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch(`https://discord.com/api/v10/applications/${applicationId}/rpc`, {
|
||||
headers: { "User-Agent": "fristy.net (personal site)" },
|
||||
next: { revalidate: 86_400 },
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error(`discord rpc unreachable: ${applicationId}`, { cause: error });
|
||||
}
|
||||
if (!response.ok) {
|
||||
if (response.status === 404) {
|
||||
return null;
|
||||
}
|
||||
if (response.status >= 500 || response.status === 429) {
|
||||
throw new Error(`discord rpc upstream failed: ${applicationId} -> ${response.status}`, {
|
||||
cause: new Error(`HTTP ${response.status}`),
|
||||
});
|
||||
}
|
||||
warnDev(`discord app failed: ${response.status}`);
|
||||
return null;
|
||||
}
|
||||
let json: DiscordRpcResponse;
|
||||
try {
|
||||
json = (await response.json()) as DiscordRpcResponse;
|
||||
} catch (error) {
|
||||
throw new Error(`discord rpc malformed response: ${applicationId}`, { cause: error });
|
||||
}
|
||||
const gameId = json.linked_games?.[0]?.id ?? json.id ?? applicationId;
|
||||
return {
|
||||
id: json.id ?? applicationId,
|
||||
name: json.name ?? "",
|
||||
icon: json.icon ?? null,
|
||||
iconUrl: discordAppIconUrl(applicationId, json.icon ?? null),
|
||||
coverImage: json.cover_image ?? null,
|
||||
coverUrl: discordAppCoverUrl(applicationId, json.cover_image ?? null),
|
||||
gameId,
|
||||
gameUrl: discordGameUrl(gameId),
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user