import fs from 'node:fs'; import { paths } from '../../env.mjs'; import { UF_NAMES } from '../../sources/common.mjs'; import { resolveZipReader } from '../../extract/rfb-extractor.mjs'; export function registerExtractionRoutes(router, { db, jobs }) { router.add('POST', '/api/extractions', async (req, res, { json, body }) => { const uf = String(body?.uf || '').toUpperCase(); if (!UF_NAMES[uf]) { json(400, { erro: 'informe uma UF valida' }); return; } if (!fs.existsSync(paths.rfbRaw)) { json(409, { erro: `zips da Receita Federal nao encontrados em ${paths.rfbRaw}. Rode scripts/baixar-base-rfb.sh primeiro.` }); return; } try { resolveZipReader(); } catch (error) { json(409, { erro: error.message }); return; } const running = db.prepare( "SELECT id, uf FROM extractions WHERE status IN ('pending','running')" ).get(); if (running) { json(409, { erro: `ja existe uma extracao em andamento (${running.uf}). Aguarde terminar.` }); return; } const inserted = db.prepare("INSERT INTO extractions (uf, status) VALUES (?, 'pending')").run(uf); const extractionId = Number(inserted.lastInsertRowid); const jobId = jobs.startJob({ type: 'extracao', script: 'src/extract/extract-job.mjs', args: [`--extraction-id=${extractionId}`], meta: { extractionId, uf } }); db.prepare('UPDATE extractions SET job_id = ? WHERE id = ?').run(jobId, extractionId); json(202, { extractionId, jobId }); }); router.add('GET', '/api/extractions', async (req, res, { json }) => { const rows = db.prepare( `SELECT id, job_id, uf, status, csv_path, total_rows, with_phone, municipios_count, progress_json, error, started_at, finished_at, created_at FROM extractions ORDER BY id DESC LIMIT 20` ).all(); json(200, { extractions: rows.map((row) => ({ ...row, progress: JSON.parse(row.progress_json || 'null'), progress_json: undefined })) }); }); router.add('POST', '/api/extractions/:id/cancel', async (req, res, { json, params }) => { const row = db.prepare('SELECT job_id, status FROM extractions WHERE id = ?').get(Number(params.id)); if (!row) { json(404, { erro: 'extracao nao encontrada' }); return; } const ok = row.job_id ? jobs.cancelJob(row.job_id) : false; json(ok ? 202 : 409, ok ? { cancelando: true } : { erro: `nao ha job ativo para cancelar (status: ${row.status})` }); }); }