import { Router } from 'express'; import multer from 'multer'; import { asyncHandler } from '../utils'; import { importStatement, isValidationError } from '../services/import'; import { convertPdfToStatementStreaming, 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') ); } function sseWrite(res: import('express').Response, data: Record) { res.write(`data: ${JSON.stringify(data)}\n\n`); } 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; } 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.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; } sseWrite(res, { stage: 'import', progress: 88, message: 'Импорт в базу данных...', }); 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; } 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;