2026-06-02 12:38:01 +08:00
|
|
|
const ERROR_REPORT_ENDPOINT = "/api/client-errors";
|
2026-06-03 20:19:07 +08:00
|
|
|
const CLIENT_ERROR_REPORTING_ENABLED = import.meta.env.VITE_ENABLE_CLIENT_ERROR_REPORTING === "1";
|
2026-06-02 12:38:01 +08:00
|
|
|
|
|
|
|
|
interface ErrorReport {
|
|
|
|
|
message: string;
|
|
|
|
|
stack?: string;
|
|
|
|
|
source: "boundary" | "unhandled" | "rejection" | "manual";
|
|
|
|
|
url: string;
|
|
|
|
|
timestamp: number;
|
|
|
|
|
userAgent: string;
|
|
|
|
|
sessionId?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let reportQueue: ErrorReport[] = [];
|
|
|
|
|
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 baseUrl = import.meta.env.VITE_API_BASE_URL || "";
|
|
|
|
|
const url = `${baseUrl}${ERROR_REPORT_ENDPOINT}`;
|
|
|
|
|
const token = localStorage.getItem("omniai:token") || sessionStorage.getItem("omniai:token") || "";
|
|
|
|
|
const headers: Record<string, string> = { "Content-Type": "application/json" };
|
|
|
|
|
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
|
|
|
navigator.sendBeacon?.(url, new Blob([JSON.stringify({ errors: batch })], { type: "application/json" }));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function scheduleFlush() {
|
|
|
|
|
if (flushTimer) return;
|
|
|
|
|
flushTimer = setTimeout(() => {
|
|
|
|
|
flushTimer = null;
|
|
|
|
|
flush();
|
|
|
|
|
}, 2000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function reportError(error: unknown, source: ErrorReport["source"] = "manual") {
|
2026-06-03 20:19:07 +08:00
|
|
|
if (!CLIENT_ERROR_REPORTING_ENABLED) return;
|
|
|
|
|
|
2026-06-02 12:38:01 +08:00
|
|
|
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();
|
|
|
|
|
}
|