chore: import gti-maquina-comercial history via subtree
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> git-subtree-dir: gti-maquina-comercial git-subtree-mainline:93ca82979egit-subtree-split:b77484946f
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { paths } from '../../env.mjs';
|
||||
import { countLeads } from '../../treatment/queries.mjs';
|
||||
|
||||
const TIPOS = new Set(['excel', 'zpro-oportunidades', 'zpro-bulk']);
|
||||
|
||||
function parseExportRow(row) {
|
||||
return {
|
||||
...row,
|
||||
filtro: JSON.parse(row.filtro_json || '{}'),
|
||||
params: JSON.parse(row.params_json || '{}'),
|
||||
arquivos: JSON.parse(row.arquivos_json || '[]'),
|
||||
filtro_json: undefined,
|
||||
params_json: undefined,
|
||||
arquivos_json: undefined
|
||||
};
|
||||
}
|
||||
|
||||
export function registerExportRoutes(router, { db, jobs }) {
|
||||
router.add('POST', '/api/exports', async (req, res, { json, body }) => {
|
||||
const tipo = String(body?.tipo || '');
|
||||
if (!TIPOS.has(tipo)) {
|
||||
json(400, { erro: `tipo invalido. Use: ${[...TIPOS].join(', ')}` });
|
||||
return;
|
||||
}
|
||||
|
||||
const filtro = body?.filtro && typeof body.filtro === 'object' ? body.filtro : {};
|
||||
const params = body?.params && typeof body.params === 'object' ? body.params : {};
|
||||
|
||||
const total = countLeads(db, filtro);
|
||||
if (!total) {
|
||||
json(400, { erro: 'nenhum lead corresponde ao filtro selecionado' });
|
||||
return;
|
||||
}
|
||||
|
||||
const inserted = db.prepare(
|
||||
"INSERT INTO exports (tipo, filtro_json, params_json, status) VALUES (?, ?, ?, 'pending')"
|
||||
).run(tipo, JSON.stringify(filtro), JSON.stringify(params));
|
||||
const exportId = Number(inserted.lastInsertRowid);
|
||||
|
||||
const jobId = jobs.startJob({
|
||||
type: 'exportacao',
|
||||
script: 'src/export/export-job.mjs',
|
||||
args: [`--export-id=${exportId}`],
|
||||
meta: { exportId, tipo, total }
|
||||
});
|
||||
db.prepare('UPDATE exports SET job_id = ? WHERE id = ?').run(jobId, exportId);
|
||||
|
||||
json(202, { exportId, jobId, totalEstimado: total });
|
||||
});
|
||||
|
||||
router.add('GET', '/api/exports', async (req, res, { json, query }) => {
|
||||
const limit = Math.min(100, Math.max(1, Number(query.get('limit')) || 20));
|
||||
const rows = db.prepare('SELECT * FROM exports ORDER BY id DESC LIMIT ?').all(limit);
|
||||
json(200, { exports: rows.map(parseExportRow) });
|
||||
});
|
||||
|
||||
router.add('GET', '/api/exports/:id', async (req, res, { json, params }) => {
|
||||
const row = db.prepare('SELECT * FROM exports WHERE id = ?').get(Number(params.id));
|
||||
if (!row) {
|
||||
json(404, { erro: 'exportacao nao encontrada' });
|
||||
return;
|
||||
}
|
||||
json(200, parseExportRow(row));
|
||||
});
|
||||
|
||||
router.add('GET', '/api/exports/:id/download', async (req, res, { json, params, query }) => {
|
||||
const row = db.prepare('SELECT * FROM exports WHERE id = ?').get(Number(params.id));
|
||||
if (!row) {
|
||||
json(404, { erro: 'exportacao nao encontrada' });
|
||||
return;
|
||||
}
|
||||
|
||||
const arquivos = JSON.parse(row.arquivos_json || '[]');
|
||||
const nome = String(query.get('arquivo') || arquivos[0] || '');
|
||||
if (!arquivos.includes(nome)) {
|
||||
json(400, { erro: 'arquivo nao pertence a esta exportacao' });
|
||||
return;
|
||||
}
|
||||
|
||||
const dir = path.resolve(paths.exports, `export-${row.id}`);
|
||||
const resolved = path.resolve(dir, nome);
|
||||
if (!resolved.startsWith(dir + path.sep) || !fs.existsSync(resolved)) {
|
||||
json(404, { erro: 'arquivo nao encontrado' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.writeHead(200, {
|
||||
'content-type': 'application/octet-stream',
|
||||
'content-disposition': `attachment; filename="${encodeURIComponent(nome)}"`,
|
||||
'content-length': fs.statSync(resolved).size
|
||||
});
|
||||
fs.createReadStream(resolved).pipe(res);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user