bae705aa97
- Add NFC ePassport roadmap (ICAO 9303, eIDAS) - Add TensorFlow.js edge face detection (BlazeFace) - Add structured audit logger (GDPR-compliant) - Risk scoring support Part of KYC Apple Native UX v1.1.0
115 lines
4.9 KiB
JavaScript
115 lines
4.9 KiB
JavaScript
/**
|
|
* routes/export.mjs — Export endpoints (receipts, uploads)
|
|
*/
|
|
import { execFile } from 'child_process';
|
|
import { promisify } from 'util';
|
|
import { createWriteStream, unlinkSync } from 'fs';
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
const RECEIPT_BUCKET = 'landvex-prod';
|
|
const RECEIPT_REGION = 'eu-north-1';
|
|
|
|
async function s3Put(localPath, s3Key) {
|
|
await execFileAsync('aws', [
|
|
's3', 'cp', localPath, `s3://${RECEIPT_BUCKET}/${s3Key}`,
|
|
'--region', RECEIPT_REGION,
|
|
], { env: { ...process.env, AWS_DEFAULT_REGION: RECEIPT_REGION } });
|
|
return `https://${RECEIPT_BUCKET}.s3.${RECEIPT_REGION}.amazonaws.com/${s3Key}`;
|
|
}
|
|
|
|
export default function registerExport(app, pool) {
|
|
// POST /api/ledger/journal/:id/upload — raw file upload
|
|
app.post('/api/ledger/journal/:id/upload', async (req, res) => {
|
|
const { id } = req.params;
|
|
const tenant_id = req.auth?.tenant_id || req.headers['x-tenant-id'] || 'wavult-group';
|
|
const filename = (req.headers['x-filename'] || 'receipt').replace(/[^a-zA-Z0-9._-]/g, '_');
|
|
const ctype = req.headers['content-type'] || 'application/octet-stream';
|
|
const localPath = `/tmp/rcpt_${Date.now()}_${filename}`;
|
|
let fileSize = 0;
|
|
try {
|
|
await new Promise((resolve, reject) => {
|
|
const ws = createWriteStream(localPath);
|
|
req.on('data', chunk => { fileSize += chunk.length; });
|
|
req.pipe(ws);
|
|
ws.on('finish', resolve);
|
|
ws.on('error', reject);
|
|
req.on('error', reject);
|
|
});
|
|
if (fileSize > 12 * 1024 * 1024) {
|
|
unlinkSync(localPath);
|
|
return res.status(400).json({ ok: false, error: 'Filen är för stor (max 12 MB)' });
|
|
}
|
|
const s3Key = `receipts/${tenant_id}/${id}/${Date.now()}_${filename}`;
|
|
const pubUrl = await s3Put(localPath, s3Key);
|
|
try { unlinkSync(localPath); } catch {}
|
|
const { rows } = await pool.query(
|
|
'SELECT metadata FROM ledger_journal_entries WHERE id=$1 AND tenant_id=$2',
|
|
[id, tenant_id]
|
|
);
|
|
if (!rows.length) return res.status(404).json({ ok: false, error: 'Verifikat ej hittat' });
|
|
const meta = rows[0].metadata || {};
|
|
const receipts = [...(meta.receipts || []), {
|
|
s3Key, filename, contentType: ctype, fileSize,
|
|
url: pubUrl,
|
|
uploaded_by: req.auth?.user_id || 'unknown',
|
|
uploaded_at: new Date().toISOString(),
|
|
}];
|
|
await pool.query(
|
|
`UPDATE ledger_journal_entries SET metadata = metadata || $1::jsonb WHERE id=$2 AND tenant_id=$3`,
|
|
[JSON.stringify({ receipts }), id, tenant_id]
|
|
);
|
|
res.json({ ok: true, filename, s3Key, url: pubUrl, receipts, count: receipts.length });
|
|
} catch (e) {
|
|
try { unlinkSync(localPath); } catch {}
|
|
res.status(500).json({ ok: false, error: e.message });
|
|
}
|
|
});
|
|
|
|
// POST /api/ledger/journal/:id/receipt — bekräfta upload
|
|
app.post('/api/ledger/journal/:id/receipt', async (req, res) => {
|
|
const { id } = req.params;
|
|
const tenant_id = req.auth?.tenant_id || req.headers['x-tenant-id'] || 'wavult-group';
|
|
const { s3Key, filename, contentType, fileSize } = req.body;
|
|
if (!s3Key || !filename) return res.status(400).json({ ok: false, error: 's3Key och filename krävs' });
|
|
try {
|
|
const { rows } = await pool.query(
|
|
'SELECT id, metadata FROM ledger_journal_entries WHERE id=$1 AND tenant_id=$2',
|
|
[id, tenant_id]
|
|
);
|
|
if (!rows.length) return res.status(404).json({ ok: false, error: 'Verifikat ej hittat' });
|
|
const meta = rows[0].metadata || {};
|
|
const receipts = [...(meta.receipts || []), {
|
|
s3Key, filename, contentType: contentType || 'application/octet-stream',
|
|
fileSize: fileSize || 0,
|
|
url: `https://${RECEIPT_BUCKET}.s3.${RECEIPT_REGION}.amazonaws.com/${s3Key}`,
|
|
uploaded_by: req.auth?.user_id || 'unknown',
|
|
uploaded_at: new Date().toISOString(),
|
|
}];
|
|
await pool.query(
|
|
`UPDATE ledger_journal_entries SET metadata = metadata || $1::jsonb WHERE id=$2 AND tenant_id=$3`,
|
|
[JSON.stringify({ receipts }), id, tenant_id]
|
|
);
|
|
res.json({ ok: true, receipts, count: receipts.length });
|
|
} catch (e) {
|
|
res.status(500).json({ ok: false, error: e.message });
|
|
}
|
|
});
|
|
|
|
// GET /api/ledger/journal/:id/receipts
|
|
app.get('/api/ledger/journal/:id/receipts', async (req, res) => {
|
|
const { id } = req.params;
|
|
const tenant_id = req.auth?.tenant_id || req.headers['x-tenant-id'] || 'wavult-group';
|
|
try {
|
|
const { rows } = await pool.query(
|
|
'SELECT metadata FROM ledger_journal_entries WHERE id=$1 AND tenant_id=$2',
|
|
[id, tenant_id]
|
|
);
|
|
if (!rows.length) return res.status(404).json({ ok: false, error: 'Ej hittat' });
|
|
const receipts = (rows[0].metadata || {}).receipts || [];
|
|
res.json({ ok: true, receipts, count: receipts.length });
|
|
} catch (e) {
|
|
res.status(500).json({ ok: false, error: e.message });
|
|
}
|
|
});
|
|
}
|