2025-07-22 18:49:04 +00:00
|
|
|
|
import { ErrorInfo } from "react";
|
|
|
|
|
import { Ticket, createTicket, getAuthToken, sendTicketMessage } from "..";
|
|
|
|
|
|
|
|
|
|
let errorsQueue: ComponentError[] = [];
|
|
|
|
|
let timeoutId: ReturnType<typeof setTimeout>;
|
|
|
|
|
|
|
|
|
|
interface ComponentError {
|
|
|
|
|
timestamp: number;
|
|
|
|
|
message: string;
|
|
|
|
|
callStack: string | undefined;
|
|
|
|
|
componentStack: string | null | undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isErrorReportingAllowed(error?: Error): boolean {
|
|
|
|
|
// Если ошибка помечена как debug-override — всегда отправлять
|
|
|
|
|
if (error && (error as any).__forceSend) return true;
|
|
|
|
|
// Проверяем домен
|
|
|
|
|
const currentDomain = window.location.hostname;
|
|
|
|
|
return currentDomain !== 'localhost';
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-22 18:49:14 +00:00
|
|
|
|
// Новый API: getTickets — callback, возвращающий актуальные тикеты
|
|
|
|
|
export function handleComponentError(error: Error, info: ErrorInfo, getTickets: () => Ticket[]) {
|
2025-07-22 18:49:04 +00:00
|
|
|
|
//репортим только о авторизонышах
|
|
|
|
|
if (!getAuthToken()) return;
|
|
|
|
|
// Проверяем разрешение на отправку ошибок (по домену)
|
|
|
|
|
if (!isErrorReportingAllowed(error)) {
|
|
|
|
|
console.log('❌ Отправка ошибки заблокирована:', error.message);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
console.log(`✅ Обработка ошибки: ${error.message}`);
|
|
|
|
|
// Копируем __forceSend если есть
|
|
|
|
|
const componentError: ComponentError & { __forceSend?: boolean } = {
|
|
|
|
|
timestamp: Math.floor(Date.now() / 1000),
|
|
|
|
|
message: error.message,
|
|
|
|
|
callStack: error.stack,
|
|
|
|
|
componentStack: info.componentStack,
|
|
|
|
|
...(error && (error as any).__forceSend ? { __forceSend: true } : {})
|
|
|
|
|
};
|
2025-07-22 18:49:14 +00:00
|
|
|
|
queueErrorRequest(componentError, getTickets);
|
2025-07-22 18:49:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
2025-07-22 18:49:14 +00:00
|
|
|
|
// Ставит ошибку в очередь для отправки, через 1 секунду вызывает sendErrorsToServer
|
|
|
|
|
export function queueErrorRequest(error: ComponentError, getTickets: () => Ticket[]) {
|
2025-07-22 18:49:04 +00:00
|
|
|
|
errorsQueue.push(error);
|
|
|
|
|
clearTimeout(timeoutId);
|
|
|
|
|
timeoutId = setTimeout(() => {
|
2025-07-22 18:49:14 +00:00
|
|
|
|
sendErrorsToServer(getTickets);
|
2025-07-22 18:49:04 +00:00
|
|
|
|
}, 1000);
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-22 18:49:14 +00:00
|
|
|
|
// Отправляет накопленные ошибки в тикеты, ищет существующий тикет с system: true или создает новый
|
|
|
|
|
export async function sendErrorsToServer(getTickets: () => Ticket[]) {
|
2025-07-22 18:49:04 +00:00
|
|
|
|
if (errorsQueue.length === 0) return;
|
|
|
|
|
// Проверяем разрешение на отправку ошибок (по домену и debug-override)
|
|
|
|
|
// Если хотя бы одна ошибка в очереди с __forceSend, отправляем всё
|
|
|
|
|
const forceSend = errorsQueue.some(e => (e as any).__forceSend);
|
|
|
|
|
if (!forceSend && !isErrorReportingAllowed()) {
|
|
|
|
|
console.log('❌ Отправка ошибок заблокирована, очищаем очередь');
|
|
|
|
|
errorsQueue = [];
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-07-22 18:49:14 +00:00
|
|
|
|
const tickets = getTickets();
|
2025-07-22 18:49:04 +00:00
|
|
|
|
try {
|
|
|
|
|
// Формируем сообщение об ошибке
|
|
|
|
|
const errorMessage = errorsQueue.map(error => {
|
|
|
|
|
return `[${new Date(error.timestamp * 1000).toISOString()}] ${error.message}\n\nCall Stack:\n${error.callStack || 'N/A'}\n\nComponent Stack:\n${error.componentStack || 'N/A'}`;
|
|
|
|
|
}).join('\n\n---\n\n');
|
|
|
|
|
// ВСЕГДА ищем тикет через API
|
|
|
|
|
const existingSystemTicket = await findSystemTicket(tickets);
|
|
|
|
|
if (existingSystemTicket) {
|
|
|
|
|
sendTicketMessage({
|
|
|
|
|
ticketId: existingSystemTicket,
|
|
|
|
|
message: errorMessage,
|
|
|
|
|
systemError: true,
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
// Создаем новый тикет для ошибки
|
|
|
|
|
createTicket({
|
|
|
|
|
message: errorMessage,
|
|
|
|
|
useToken: true,
|
|
|
|
|
systemError: true,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error in sendErrorsToServer:', error);
|
|
|
|
|
} finally {
|
|
|
|
|
// Очищаем очередь ошибок
|
|
|
|
|
errorsQueue = [];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ищет существующий тикет с system: true
|
2025-07-22 18:49:14 +00:00
|
|
|
|
export async function findSystemTicket(tickets: Ticket[]) {
|
2025-07-22 18:49:04 +00:00
|
|
|
|
for (const ticket of tickets) {
|
|
|
|
|
console.log("[findSystemTicket] Проверяем тикет:", ticket);
|
|
|
|
|
if (!('messages' in ticket)) {
|
|
|
|
|
if (ticket.top_message && ticket.top_message.system === true) {
|
|
|
|
|
console.log("[findSystemTicket] Найден тикет по top_message.system:true:", ticket.id);
|
|
|
|
|
return ticket.id;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-07-22 18:49:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function clearErrorHandlingConfig () {
|
|
|
|
|
clearTimeout(timeoutId);
|
|
|
|
|
errorsQueue = [];
|
|
|
|
|
}
|