Revert SSE streaming for PDF import, use synchronous flow

SSE streaming added unnecessary complexity and latency due to
buffering issues across Node.js event loop, Nginx proxy, and
Docker layers. Reverted to a simple synchronous request/response
for PDF conversion. Kept extractLlmErrorMessage for user-friendly
LLM errors, lazy-loaded pdf-parse, and extended Nginx timeout.
This commit is contained in:
vakabunga
2026-03-14 20:12:27 +03:00
parent ea234ea007
commit 8b57dd987e
10 changed files with 73 additions and 695 deletions

View File

@@ -3,7 +3,7 @@ import multer from 'multer';
import { asyncHandler } from '../utils';
import { importStatement, isValidationError } from '../services/import';
import {
convertPdfToStatementStreaming,
convertPdfToStatement,
isPdfConversionError,
} from '../services/pdfToStatement';
@@ -28,10 +28,6 @@ function isJsonFile(file: { mimetype: string; originalname: string }): boolean {
);
}
function sseWrite(res: import('express').Response, data: Record<string, unknown>) {
res.write(`data: ${JSON.stringify(data)}\n\n`);
}
const router = Router();
router.post(
@@ -55,68 +51,28 @@ router.post(
return;
}
if (isPdfFile(file)) {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.setHeader('X-Accel-Buffering', 'no');
res.socket?.setNoDelay(true);
res.flushHeaders();
try {
const converted = await convertPdfToStatementStreaming(
file.buffer,
(stage, progress, message) => {
sseWrite(res, { stage, progress, message });
},
);
if (isPdfConversionError(converted)) {
sseWrite(res, {
stage: 'error',
message: converted.message,
});
res.end();
return;
}
const result = await importStatement(converted);
if (isValidationError(result)) {
sseWrite(res, {
stage: 'error',
message: (result as { message: string }).message,
});
res.end();
return;
}
sseWrite(res, {
stage: 'done',
progress: 100,
result,
});
} catch (err) {
console.error('SSE import error:', err);
sseWrite(res, {
stage: 'error',
message: 'Внутренняя ошибка сервера',
});
}
res.end();
return;
}
// JSON files — synchronous response as before
let body: unknown;
try {
body = JSON.parse(file.buffer.toString('utf-8'));
} catch {
res.status(400).json({
error: 'BAD_REQUEST',
message: 'Некорректный JSON-файл',
});
return;
if (isPdfFile(file)) {
const converted = await convertPdfToStatement(file.buffer);
if (isPdfConversionError(converted)) {
res.status(converted.status).json({
error: converted.error,
message: converted.message,
});
return;
}
body = converted;
} else {
try {
body = JSON.parse(file.buffer.toString('utf-8'));
} catch {
res.status(400).json({
error: 'BAD_REQUEST',
message: 'Некорректный JSON-файл',
});
return;
}
}
const result = await importStatement(body);