02d89e6385
Add knowledge document GET/PUT and admin edit UI; auto-create support tickets on client 400 validation errors across user/shop/partner apps; batch create tasks and publish from support tickets; restore mini-user store env single-column layout. Co-authored-by: Cursor <cursoragent@cursor.com>
98 lines
2.7 KiB
TypeScript
98 lines
2.7 KiB
TypeScript
import { postJson } from './http';
|
|
|
|
export type ClientErrorLevel = 'fatal' | 'error' | 'warn' | 'info';
|
|
export type ClientErrorCategory =
|
|
| 'js_error'
|
|
| 'unhandled_rejection'
|
|
| 'api_error'
|
|
| 'validation_error'
|
|
| 'network'
|
|
| 'render'
|
|
| 'bridge'
|
|
| 'other';
|
|
|
|
export type ClientErrorReporterOptions = {
|
|
apiBase: string;
|
|
clientApp: string;
|
|
getToken?: () => string | null;
|
|
};
|
|
|
|
export function installClientErrorReporting(opts: ClientErrorReporterOptions) {
|
|
if (typeof window === 'undefined') return;
|
|
|
|
const report = (payload: {
|
|
level: ClientErrorLevel;
|
|
category: ClientErrorCategory;
|
|
message: string;
|
|
stack?: string;
|
|
extra?: Record<string, unknown>;
|
|
}) => {
|
|
const token = opts.getToken?.() ?? localStorage.getItem('accessToken');
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json',
|
|
'X-Client-App': opts.clientApp,
|
|
};
|
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
|
|
postJson({
|
|
url: `${opts.apiBase}/common/client-errors`,
|
|
headers,
|
|
body: {
|
|
level: payload.level,
|
|
category: payload.category,
|
|
message: payload.message,
|
|
stack: payload.stack,
|
|
pagePath: window.location.pathname,
|
|
clientApp: opts.clientApp,
|
|
extra: payload.extra,
|
|
},
|
|
});
|
|
};
|
|
|
|
window.addEventListener('error', (ev) => {
|
|
report({
|
|
level: 'error',
|
|
category: 'js_error',
|
|
message: ev.message || 'Unknown error',
|
|
stack: ev.error instanceof Error ? ev.error.stack : undefined,
|
|
});
|
|
});
|
|
|
|
window.addEventListener('unhandledrejection', (ev) => {
|
|
const reason = ev.reason;
|
|
report({
|
|
level: 'error',
|
|
category: 'unhandled_rejection',
|
|
message: reason instanceof Error ? reason.message : String(reason),
|
|
stack: reason instanceof Error ? reason.stack : undefined,
|
|
});
|
|
});
|
|
}
|
|
|
|
export function reportApiError(
|
|
opts: ClientErrorReporterOptions,
|
|
input: { message: string; status?: number; url?: string; category?: ClientErrorCategory },
|
|
) {
|
|
if (typeof window === 'undefined') return;
|
|
if (input.url?.includes('/common/client-errors')) return;
|
|
const token = opts.getToken?.() ?? localStorage.getItem('accessToken');
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json',
|
|
'X-Client-App': opts.clientApp,
|
|
};
|
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
|
|
postJson({
|
|
url: `${opts.apiBase}/common/client-errors`,
|
|
headers,
|
|
body: {
|
|
level: 'warn',
|
|
category: input.category ?? (input.status === 400 ? 'validation_error' : 'api_error'),
|
|
message: input.message.slice(0, 1000),
|
|
pagePath: window.location.pathname,
|
|
clientApp: opts.clientApp,
|
|
extra: { status: input.status, url: input.url },
|
|
},
|
|
});
|
|
}
|