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,207 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import type { LanyardData } from "@/lib/game/lanyard";
|
||||
import { DISCORD_ID, LANYARD_API, LANYARD_WS } from "@/lib/game/lanyard";
|
||||
import { fetchJson } from "@/lib/client-fetch";
|
||||
|
||||
const WS_OPEN_TIMEOUT_MS = 10_000;
|
||||
const RECONNECT_BASE_MS = 2_000;
|
||||
const RECONNECT_MAX_MS = 30_000;
|
||||
const SAFETY_POLL_MS = 60_000;
|
||||
const HEARTBEAT_FALLBACK_MS = 30_000;
|
||||
|
||||
interface LanyardMessage {
|
||||
op: number;
|
||||
t?: string;
|
||||
d?: unknown;
|
||||
}
|
||||
|
||||
function parseLanyardMessage(data: unknown): LanyardMessage | null {
|
||||
if (typeof data !== "string") {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(data) as LanyardMessage;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function isLanyardData(value: unknown): value is LanyardData {
|
||||
if (typeof value !== "object" || value === null) {
|
||||
return false;
|
||||
}
|
||||
const candidate = value as { activities?: unknown; discord_status?: unknown };
|
||||
if (!Array.isArray(candidate.activities)) {
|
||||
return false;
|
||||
}
|
||||
const status = candidate.discord_status;
|
||||
return status === "online" || status === "idle" || status === "dnd" || status === "offline";
|
||||
}
|
||||
function nextBackoffDelay(current: number): number {
|
||||
return Math.min(current * 2, RECONNECT_MAX_MS);
|
||||
}
|
||||
|
||||
async function fetchLanyardSnapshot(): Promise<LanyardData | null> {
|
||||
const json = await fetchJson<{ success: boolean; data: LanyardData }>(LANYARD_API, {
|
||||
cache: "no-store",
|
||||
});
|
||||
return json && json.success ? json.data : null;
|
||||
}
|
||||
|
||||
interface LanyardSocketEvents {
|
||||
onData: (data: LanyardData) => void;
|
||||
}
|
||||
|
||||
interface LanyardSocket {
|
||||
start: () => void;
|
||||
stop: () => void;
|
||||
}
|
||||
|
||||
function createLanyardSocket(events: LanyardSocketEvents): LanyardSocket {
|
||||
let disposed = false;
|
||||
let socket: WebSocket | null = null;
|
||||
let heartbeatTimer: number | undefined;
|
||||
let reconnectTimer: number | undefined;
|
||||
let safetyTimer: number | undefined;
|
||||
let reconnectDelay = RECONNECT_BASE_MS;
|
||||
|
||||
async function pollOnce(): Promise<void> {
|
||||
const snapshot = await fetchLanyardSnapshot();
|
||||
if (snapshot && !disposed) {
|
||||
events.onData(snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleReconnect(): void {
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
window.clearTimeout(reconnectTimer);
|
||||
reconnectTimer = window.setTimeout(() => {
|
||||
reconnectDelay = nextBackoffDelay(reconnectDelay);
|
||||
connect();
|
||||
}, reconnectDelay);
|
||||
}
|
||||
|
||||
function dropSocket(current: WebSocket): void {
|
||||
current.onclose = null;
|
||||
current.onerror = null;
|
||||
current.onmessage = null;
|
||||
if (socket === current) {
|
||||
socket = null;
|
||||
}
|
||||
try {
|
||||
current.close();
|
||||
} catch {
|
||||
if (socket === current) {
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function connect(): void {
|
||||
if (disposed) {
|
||||
return;
|
||||
}
|
||||
let current: WebSocket;
|
||||
try {
|
||||
current = new WebSocket(LANYARD_WS);
|
||||
} catch {
|
||||
scheduleReconnect();
|
||||
return;
|
||||
}
|
||||
socket = current;
|
||||
let settled = false;
|
||||
|
||||
const openTimeout = window.setTimeout(() => {
|
||||
if (!settled) {
|
||||
settled = true;
|
||||
dropSocket(current);
|
||||
if (!disposed) {
|
||||
void pollOnce();
|
||||
scheduleReconnect();
|
||||
}
|
||||
}
|
||||
}, WS_OPEN_TIMEOUT_MS);
|
||||
|
||||
const handleDrop = (): void => {
|
||||
if (settled && socket !== current) {
|
||||
return;
|
||||
}
|
||||
settled = true;
|
||||
window.clearTimeout(openTimeout);
|
||||
window.clearInterval(heartbeatTimer);
|
||||
dropSocket(current);
|
||||
if (!disposed) {
|
||||
void pollOnce();
|
||||
scheduleReconnect();
|
||||
}
|
||||
};
|
||||
|
||||
current.onmessage = (event: MessageEvent) => {
|
||||
const message = parseLanyardMessage(event.data);
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
if (message.op === 1) {
|
||||
settled = true;
|
||||
window.clearTimeout(openTimeout);
|
||||
reconnectDelay = RECONNECT_BASE_MS;
|
||||
const interval =
|
||||
(message.d as { heartbeat_interval?: number } | undefined)
|
||||
?.heartbeat_interval ?? HEARTBEAT_FALLBACK_MS;
|
||||
window.clearInterval(heartbeatTimer);
|
||||
heartbeatTimer = window.setInterval(() => {
|
||||
try {
|
||||
current.send(JSON.stringify({ op: 3 }));
|
||||
} catch {
|
||||
handleDrop();
|
||||
}
|
||||
}, interval);
|
||||
current.send(JSON.stringify({ op: 2, d: { subscribe_to_id: DISCORD_ID } }));
|
||||
} else if (
|
||||
message.op === 0 &&
|
||||
(message.t === "INIT_STATE" || message.t === "PRESENCE_UPDATE")
|
||||
) {
|
||||
if (!disposed && isLanyardData(message.d)) {
|
||||
events.onData(message.d);
|
||||
}
|
||||
}
|
||||
};
|
||||
current.onerror = handleDrop;
|
||||
current.onclose = handleDrop;
|
||||
}
|
||||
|
||||
function start(): void {
|
||||
connect();
|
||||
safetyTimer = window.setInterval(() => {
|
||||
if (!socket || socket.readyState !== WebSocket.OPEN) {
|
||||
void pollOnce();
|
||||
}
|
||||
}, SAFETY_POLL_MS);
|
||||
}
|
||||
|
||||
function stop(): void {
|
||||
disposed = true;
|
||||
window.clearTimeout(reconnectTimer);
|
||||
window.clearInterval(safetyTimer);
|
||||
window.clearInterval(heartbeatTimer);
|
||||
if (socket) {
|
||||
dropSocket(socket);
|
||||
}
|
||||
}
|
||||
|
||||
return { start, stop };
|
||||
}
|
||||
|
||||
export function useLanyard(initial: LanyardData | null): LanyardData | null {
|
||||
const [data, setData] = useState<LanyardData | null>(initial);
|
||||
|
||||
useEffect(() => {
|
||||
const controller = createLanyardSocket({ onData: (snapshot) => setData(snapshot) });
|
||||
controller.start();
|
||||
return () => controller.stop();
|
||||
}, []);
|
||||
|
||||
return data;
|
||||
}
|
||||
Reference in new issue
Block a user