fix: бесконечный ретрай upload 400, скобки в именах файлов, тост batch-create
- useOfflineSync: tryUploadFile/trySendAttachmentMessage бросали ошибку без кода статуса — постоянная 400 не распознавалась regex \b400\b и вложение ретраилось бесконечно (спам POST /api/upload каждые ~30 сек). - validateFilename: разрешены скобки () в именах (типично для фото с телефонов; сохраняемое имя всё равно генерируется), в текст 400 добавлено само имя файла для диагностики. - BatchCreateDialog: тост ошибки показывает текст сервера (из BadRequestError или JSON в message 5xx) вместо общего «Ошибка создания задач».
This commit is contained in:
@@ -67,6 +67,22 @@ function getRemainingLockMinutes(startedAt: number): number {
|
||||
return Math.ceil(remaining / 60_000);
|
||||
}
|
||||
|
||||
// Достаём читабельный текст ошибки: apiRequest бросает BadRequestError/ConflictError
|
||||
// с текстом сервера в message, а для 5xx message имеет вид "500: {json}".
|
||||
function extractErrorMessage(e: unknown, fallback: string): string {
|
||||
if (!(e instanceof Error) || e instanceof TypeError) return fallback;
|
||||
const m = e.message || '';
|
||||
const jsonStart = m.indexOf('{');
|
||||
if (jsonStart >= 0) {
|
||||
try {
|
||||
const parsed = JSON.parse(m.slice(jsonStart));
|
||||
if (typeof parsed.error === 'string' && parsed.error) return parsed.error;
|
||||
} catch { /* не JSON — показываем как есть */ }
|
||||
}
|
||||
if (m && m !== 'bad_request' && m !== 'conflict' && m !== 'billing_blocked') return m;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
export function BatchCreateDialog({ formId, open, onOpenChange, onSuccess }: BatchCreateDialogProps) {
|
||||
const { toast } = useToast();
|
||||
const [selectedType, setSelectedType] = useState('');
|
||||
@@ -264,7 +280,11 @@ export function BatchCreateDialog({ formId, open, onOpenChange, onSuccess }: Bat
|
||||
onSuccess?.();
|
||||
queryClient.invalidateQueries({ queryKey: ['/api/dashboard'] });
|
||||
} catch (e) {
|
||||
toast({ title: 'Ошибка создания задач', variant: 'destructive' });
|
||||
toast({
|
||||
title: 'Ошибка создания задач',
|
||||
description: extractErrorMessage(e, 'Проверьте соединение и попробуйте ещё раз'),
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setCreating(false);
|
||||
}
|
||||
|
||||
@@ -229,7 +229,9 @@ async function tryUploadFile(
|
||||
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({}));
|
||||
throw new Error(err.error || `Ошибка загрузки: ${file.name}`);
|
||||
// Код статуса в сообщении обязателен: вызывающий код отличает постоянные
|
||||
// ошибки (400/401/403 → не ретраить) от временных по паттерну `\b400\b` и т.п.
|
||||
throw new Error(`${res.status}: ${err.error || `Ошибка загрузки: ${file.name}`}`);
|
||||
}
|
||||
const data = await res.json();
|
||||
return {
|
||||
@@ -293,7 +295,7 @@ async function trySendAttachmentMessage(
|
||||
|
||||
if (!res.ok) {
|
||||
const err = await res.json().catch(() => ({}));
|
||||
throw new Error(err.error || 'Ошибка отправки сообщения');
|
||||
throw new Error(`${res.status}: ${err.error || 'Ошибка отправки сообщения'}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user