Merge branch 'dev' of gitea.pena:PenaSide/UIKit into dev
Some checks failed
CreateVersion / BumpVersion (push) Has been cancelled

This commit is contained in:
Nastya 2025-07-22 21:49:14 +03:00
commit 784bd57410
4 changed files with 34 additions and 75 deletions

@ -1,49 +1,27 @@
import axios, { AxiosResponse, Method, ResponseType } from "axios"; import axios, { AxiosResponse, Method, ResponseType } from "axios";
import { getAuthToken, setAuthToken } from "../stores/auth"; import { getAuthToken, setAuthToken } from "../stores/auth";
import { Ticket, clearErrorHandlingConfig } from "..";
export interface MakeRequestConfig {
getAuthToken: () => string | undefined;
setAuthToken: (token: string) => void;
refreshUrl: string;
logoutFn: () => void;
handleComponentError?: (error: Error, info?: any) => void;
clearAuthDataFn?: () => void;
clearErrorHandlingConfig?: () => void;
allowedDomains?: string[];
debugSecretKey?: string;
logErrorFn?: (message: string, error?: any) => void;
}
let makeRequestConfig: MakeRequestConfig | null = null; let makeRequestConfig: MakeRequestConfig | null = null;
export interface MakeRequestConfig {
logoutFn: () => void;
handleComponentError?: (error: Error, info: any, getTickets: () => Ticket[]) => void;
getTickets?: () => Ticket[];
}
export function createMakeRequestConfig( export function createMakeRequestConfig(
getAuthToken: () => string | undefined,
setAuthToken: (token: string) => void,
refreshUrl: string,
logoutFn?: () => void, logoutFn?: () => void,
handleComponentError?: (error: Error, info?: any) => void, handleComponentError?: (error: Error, info: any, getTickets: () => Ticket[]) => void,
clearAuthDataFn?: () => void, getTickets?: () => Ticket[]
clearErrorHandlingConfig?: () => void,
allowedDomains?: string[],
debugSecretKey?: string,
logErrorFn?: (message: string, error?: any) => void,
) { ) {
makeRequestConfig = { makeRequestConfig = {
getAuthToken,
setAuthToken,
refreshUrl,
logoutFn: () => { logoutFn: () => {
clearMakeRequestConfig(); clearErrorHandlingConfig();
if (typeof clearErrorHandlingConfig === 'function') clearErrorHandlingConfig();
if (logoutFn) logoutFn(); if (logoutFn) logoutFn();
}, },
handleComponentError, handleComponentError,
clearAuthDataFn, getTickets,
clearErrorHandlingConfig,
allowedDomains,
debugSecretKey,
logErrorFn,
}; };
} }
@ -51,10 +29,6 @@ export function getMakeRequestConfig(): MakeRequestConfig | null {
return makeRequestConfig; return makeRequestConfig;
} }
export function clearMakeRequestConfig() {
makeRequestConfig = null;
}
export async function makeRequest<TRequest = unknown, TResponse = unknown>({ export async function makeRequest<TRequest = unknown, TResponse = unknown>({
method = "post", method = "post",
url, url,
@ -129,15 +103,16 @@ export async function makeRequest<TRequest = unknown, TResponse = unknown>({
const errorMessage = `HTTP ${error.response.status}: ${error.response?.data?.message || error.message}`; const errorMessage = `HTTP ${error.response.status}: ${error.response?.data?.message || error.message}`;
const httpError = new Error(errorMessage); const httpError = new Error(errorMessage);
httpError.stack = error.stack; httpError.stack = error.stack;
config.handleComponentError(httpError, { componentStack: null }); // Передаем getTickets как callback
config.handleComponentError(httpError, { componentStack: null }, config.getTickets || (() => []));
} }
// refreshToken is empty // refreshToken is empty
if ( if (
error.response?.status === 400 && error.response?.status === 400 &&
error.response?.data?.message === "refreshToken is empty" && error.response?.data?.message === "refreshToken is empty" &&
config?.clearAuthDataFn config?.logoutFn
) { ) {
config.clearAuthDataFn(); config.logoutFn();
} }
throw error; throw error;
} }
@ -152,4 +127,4 @@ export async function refresh(token?: string): Promise<AxiosResponse<{ accessTok
}, },
method: "post" method: "post"
}); });
} }

@ -1,11 +1,9 @@
import { ErrorInfo } from "react"; import { ErrorInfo } from "react";
import { Ticket, createTicket, getAuthToken, sendTicketMessage } from ".."; import { Ticket, createTicket, getAuthToken, sendTicketMessage } from "..";
let errorsQueue: ComponentError[] = []; let errorsQueue: ComponentError[] = [];
let timeoutId: ReturnType<typeof setTimeout>; let timeoutId: ReturnType<typeof setTimeout>;
interface ComponentError { interface ComponentError {
timestamp: number; timestamp: number;
message: string; message: string;
@ -13,29 +11,24 @@ interface ComponentError {
componentStack: string | null | undefined; componentStack: string | null | undefined;
} }
function isErrorReportingAllowed(error?: Error): boolean { function isErrorReportingAllowed(error?: Error): boolean {
// Если ошибка помечена как debug-override — всегда отправлять // Если ошибка помечена как debug-override — всегда отправлять
if (error && (error as any).__forceSend) return true; if (error && (error as any).__forceSend) return true;
// Проверяем домен // Проверяем домен
const currentDomain = window.location.hostname; const currentDomain = window.location.hostname;
return currentDomain !== 'localhost'; return currentDomain !== 'localhost';
} }
export function handleComponentError(error: Error, info: ErrorInfo, tickets: Ticket[]) { // Новый API: getTickets — callback, возвращающий актуальные тикеты
export function handleComponentError(error: Error, info: ErrorInfo, getTickets: () => Ticket[]) {
//репортим только о авторизонышах //репортим только о авторизонышах
if (!getAuthToken()) return; if (!getAuthToken()) return;
// Проверяем разрешение на отправку ошибок (по домену) // Проверяем разрешение на отправку ошибок (по домену)
if (!isErrorReportingAllowed(error)) { if (!isErrorReportingAllowed(error)) {
console.log('❌ Отправка ошибки заблокирована:', error.message); console.log('❌ Отправка ошибки заблокирована:', error.message);
return; return;
} }
console.log(`✅ Обработка ошибки: ${error.message}`); console.log(`✅ Обработка ошибки: ${error.message}`);
// Копируем __forceSend если есть // Копируем __forceSend если есть
const componentError: ComponentError & { __forceSend?: boolean } = { const componentError: ComponentError & { __forceSend?: boolean } = {
timestamp: Math.floor(Date.now() / 1000), timestamp: Math.floor(Date.now() / 1000),
@ -44,30 +37,21 @@ export function handleComponentError(error: Error, info: ErrorInfo, tickets: Tic
componentStack: info.componentStack, componentStack: info.componentStack,
...(error && (error as any).__forceSend ? { __forceSend: true } : {}) ...(error && (error as any).__forceSend ? { __forceSend: true } : {})
}; };
queueErrorRequest(componentError, getTickets);
queueErrorRequest(componentError, tickets);
} }
//Ставит ошибку в очередь для отправки // Ставит ошибку в очередь для отправки, через 1 секунду вызывает sendErrorsToServer
//Через 1 секунду вызывает sendErrorsToServer export function queueErrorRequest(error: ComponentError, getTickets: () => Ticket[]) {
export function queueErrorRequest(error: ComponentError, tickets: Ticket[]) {
errorsQueue.push(error); errorsQueue.push(error);
clearTimeout(timeoutId); clearTimeout(timeoutId);
timeoutId = setTimeout(() => { timeoutId = setTimeout(() => {
sendErrorsToServer(tickets); sendErrorsToServer(getTickets);
}, 1000); }, 1000);
} }
// Отправляет накопленные ошибки в тикеты, ищет существующий тикет с system: true или создает новый
//Отправляет накопленные ошибки в тикеты export async function sendErrorsToServer(getTickets: () => Ticket[]) {
//Ищет существующий тикет с system: true или создает новый
export async function sendErrorsToServer(
tickets: Ticket[]
) {
if (errorsQueue.length === 0) return; if (errorsQueue.length === 0) return;
// Проверяем разрешение на отправку ошибок (по домену и debug-override) // Проверяем разрешение на отправку ошибок (по домену и debug-override)
// Если хотя бы одна ошибка в очереди с __forceSend, отправляем всё // Если хотя бы одна ошибка в очереди с __forceSend, отправляем всё
const forceSend = errorsQueue.some(e => (e as any).__forceSend); const forceSend = errorsQueue.some(e => (e as any).__forceSend);
@ -76,16 +60,14 @@ export async function sendErrorsToServer(
errorsQueue = []; errorsQueue = [];
return; return;
} }
const tickets = getTickets();
try { try {
// Формируем сообщение об ошибке // Формируем сообщение об ошибке
const errorMessage = errorsQueue.map(error => { 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'}`; 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'); }).join('\n\n---\n\n');
// ВСЕГДА ищем тикет через API // ВСЕГДА ищем тикет через API
const existingSystemTicket = await findSystemTicket(tickets); const existingSystemTicket = await findSystemTicket(tickets);
if (existingSystemTicket) { if (existingSystemTicket) {
sendTicketMessage({ sendTicketMessage({
ticketId: existingSystemTicket, ticketId: existingSystemTicket,
@ -109,9 +91,7 @@ export async function sendErrorsToServer(
} }
// Ищет существующий тикет с system: true // Ищет существующий тикет с system: true
export async function findSystemTicket( export async function findSystemTicket(tickets: Ticket[]) {
tickets: Ticket[]
) {
for (const ticket of tickets) { for (const ticket of tickets) {
console.log("[findSystemTicket] Проверяем тикет:", ticket); console.log("[findSystemTicket] Проверяем тикет:", ticket);
if (!('messages' in ticket)) { if (!('messages' in ticket)) {
@ -119,7 +99,11 @@ export async function findSystemTicket(
console.log("[findSystemTicket] Найден тикет по top_message.system:true:", ticket.id); console.log("[findSystemTicket] Найден тикет по top_message.system:true:", ticket.id);
return ticket.id; return ticket.id;
} }
} }
} }
} }
export function clearErrorHandlingConfig () {
clearTimeout(timeoutId);
errorsQueue = [];
}

4
package-lock.json generated

@ -1,12 +1,12 @@
{ {
"name": "@frontend/kitui", "name": "@frontend/kitui",
"version": "1.0.108", "version": "1.0.109",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@frontend/kitui", "name": "@frontend/kitui",
"version": "1.0.108", "version": "1.0.109",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"immer": "^10.0.2", "immer": "^10.0.2",

@ -1,6 +1,6 @@
{ {
"name": "@frontend/kitui", "name": "@frontend/kitui",
"version": "1.0.109", "version": "1.0.110",
"description": "test", "description": "test",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",