Files
family_budget/backend/src/routes/import.ts
vakabunga 8b57dd987e 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.
2026-03-14 20:12:27 +03:00

91 lines
2.2 KiB
TypeScript

import { Router } from 'express';
import multer from 'multer';
import { asyncHandler } from '../utils';
import { importStatement, isValidationError } from '../services/import';
import {
convertPdfToStatement,
isPdfConversionError,
} from '../services/pdfToStatement';
const upload = multer({
storage: multer.memoryStorage(),
limits: { fileSize: 15 * 1024 * 1024 },
});
function isPdfFile(file: { mimetype: string; originalname: string }): boolean {
const name = file.originalname.toLowerCase();
return (
file.mimetype === 'application/pdf' ||
name.endsWith('.pdf')
);
}
function isJsonFile(file: { mimetype: string; originalname: string }): boolean {
const name = file.originalname.toLowerCase();
return (
file.mimetype === 'application/json' ||
name.endsWith('.json')
);
}
const router = Router();
router.post(
'/statement',
upload.single('file'),
asyncHandler(async (req, res) => {
const file = req.file;
if (!file) {
res.status(400).json({
error: 'BAD_REQUEST',
message: 'Файл не загружен',
});
return;
}
if (!isPdfFile(file) && !isJsonFile(file)) {
res.status(400).json({
error: 'BAD_REQUEST',
message: 'Допустимы только файлы PDF или JSON',
});
return;
}
let body: unknown;
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);
if (isValidationError(result)) {
res.status((result as { status: number }).status).json({
error: (result as { error: string }).error,
message: (result as { message: string }).message,
});
return;
}
res.json(result);
}),
);
export default router;