Files
omniai-web/src/utils/errorReporting.ts
T

64 lines
1.7 KiB
TypeScript
Raw Normal View History

2026-06-02 12:38:01 +08:00
const ERROR_REPORT_ENDPOINT = "/api/client-errors";
interface ErrorReport {
message: string;
stack?: string;
source: "boundary" | "unhandled" | "rejection" | "manual";
url: string;
timestamp: number;
userAgent: string;
sessionId?: string;
}
2026-06-09 12:02:30 +08:00
const reportQueue: ErrorReport[] = [];
2026-06-02 12:38:01 +08:00
let flushTimer: ReturnType<typeof setTimeout> | null = null;
function getSessionId(): string | undefined {
try {
const raw = localStorage.getItem("omniai:session") || sessionStorage.getItem("omniai:session");
if (!raw) return undefined;
const parsed = JSON.parse(raw);
return parsed?.user?.sessionId ?? undefined;
} catch {
return undefined;
}
}
function flush() {
if (reportQueue.length === 0) return;
const batch = reportQueue.splice(0, 10);
const payload = new Blob([JSON.stringify({ errors: batch })], { type: "application/json" });
if (navigator.sendBeacon?.(ERROR_REPORT_ENDPOINT, payload)) return;
void fetch(ERROR_REPORT_ENDPOINT, {
method: "POST",
body: JSON.stringify({ errors: batch }),
headers: { "Content-Type": "application/json" },
credentials: "include",
keepalive: true,
}).catch(() => {});
2026-06-02 12:38:01 +08:00
}
function scheduleFlush() {
if (flushTimer) return;
flushTimer = setTimeout(() => {
flushTimer = null;
flush();
}, 2000);
}
export function reportError(error: unknown, source: ErrorReport["source"] = "manual") {
const err = error instanceof Error ? error : new Error(String(error));
const report: ErrorReport = {
message: err.message,
stack: err.stack?.slice(0, 2000),
source,
url: window.location.href,
timestamp: Date.now(),
userAgent: navigator.userAgent,
sessionId: getSessionId(),
};
reportQueue.push(report);
scheduleFlush();
}