fix-spa-review-findings (ornith-35): fetch timeout+res.ok, AuthSection loop fix, me() timeout, enable2fa finally, TOTP mask toggle, snapshot backoff, dup key removed
Cross-review by qwopus35b pending. SPA lint 2/2 green.
This commit is contained in:
+37
-7
@@ -45,22 +45,52 @@ export interface AuthUser {
|
||||
onboarded?: boolean;
|
||||
}
|
||||
|
||||
// 8-second timeout for all tRPC calls to prevent hung requests.
|
||||
const TRPC_TIMEOUT_MS = 8_000;
|
||||
|
||||
async function parse<T>(res: Response): Promise<T> {
|
||||
if (!res.ok) {
|
||||
const body = await res.text();
|
||||
throw new Error(`tRPC ${res.status}: ${body}`);
|
||||
}
|
||||
const json = await res.json();
|
||||
if (json?.error) {
|
||||
const msg = json.error.message ?? `tRPC error ${json.error.data?.code ?? ""}`;
|
||||
throw new Error(typeof msg === "string" ? msg : "tRPC error");
|
||||
throw new Error(`tRPC error: ${json.error.message ?? json.error.data?.code ?? "unknown"}`);
|
||||
}
|
||||
return json.result.data as T;
|
||||
}
|
||||
|
||||
async function trpcQuery<T>(procedure: string, input?: unknown): Promise<T> {
|
||||
const search = input ? `?input=${encodeURIComponent(JSON.stringify(input))}` : "";
|
||||
const res = await fetch(`/api/trpc/${procedure}${search}`, { headers: { "content-type": "application/json" }, credentials: "include" });
|
||||
return parse<T>(res);
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), TRPC_TIMEOUT_MS);
|
||||
try {
|
||||
const search = input ? `?input=${encodeURIComponent(JSON.stringify(input))}` : "";
|
||||
const res = await fetch(`/api/trpc/${procedure}${search}`, {
|
||||
headers: { "content-type": "application/json" },
|
||||
credentials: "include",
|
||||
signal: controller.signal,
|
||||
});
|
||||
return parse<T>(res);
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
async function trpcMutate<T>(procedure: string, input: unknown): Promise<T> {
|
||||
const res = await fetch(`/api/trpc/${procedure}`, { method: "POST", headers: { "content-type": "application/json" }, credentials: "include", body: JSON.stringify(input) });
|
||||
return parse<T>(res);
|
||||
const controller = new AbortController();
|
||||
const timer = setTimeout(() => controller.abort(), TRPC_TIMEOUT_MS);
|
||||
try {
|
||||
const res = await fetch(`/api/trpc/${procedure}`, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
credentials: "include",
|
||||
body: JSON.stringify(input),
|
||||
signal: controller.signal,
|
||||
});
|
||||
return parse<T>(res);
|
||||
} finally {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
}
|
||||
|
||||
export type Complexity = "beginner" | "intermediate" | "advanced";
|
||||
|
||||
Reference in New Issue
Block a user