78 lines
3.3 KiB
JavaScript
78 lines
3.3 KiB
JavaScript
'use strict';
|
|
const express = require('express');
|
|
const { db } = require('../db');
|
|
const { log } = require('../lib/audit');
|
|
const { toDisplayDateTime } = require('../lib/dates');
|
|
const { requireAuth, requireAdmin } = require('../middleware/auth');
|
|
const { sendMail, adminEmails } = require('../lib/mailer');
|
|
|
|
const router = express.Router();
|
|
router.use(requireAuth, requireAdmin);
|
|
|
|
function query({ q, action, page, from, to }) {
|
|
const where = [];
|
|
const params = [];
|
|
if (q) { where.push('(actor LIKE ? OR detail LIKE ?)'); params.push(`%${q}%`, `%${q}%`); }
|
|
if (action) { where.push('action = ?'); params.push(action); }
|
|
if (page) { where.push('page = ?'); params.push(page); }
|
|
if (from) { where.push('ts >= ?'); params.push(from); }
|
|
if (to) { where.push('ts <= ?'); params.push(to); }
|
|
const sql =
|
|
'SELECT id, ts, actor, action, page, detail, ip FROM logs' +
|
|
(where.length ? ' WHERE ' + where.join(' AND ') : '') +
|
|
' ORDER BY ts DESC, id DESC';
|
|
return db.prepare(sql).all(...params);
|
|
}
|
|
|
|
router.get('/', (req, res) => {
|
|
const rows = query(req.query);
|
|
const actions = db.prepare('SELECT DISTINCT action FROM logs ORDER BY action').all().map((r) => r.action);
|
|
const pages = db.prepare('SELECT DISTINCT page FROM logs WHERE page IS NOT NULL ORDER BY page').all().map((r) => r.page);
|
|
res.json({ logs: rows.slice(0, 1000), total: rows.length, actions, pages });
|
|
});
|
|
|
|
function toCSV(rows) {
|
|
const head = ['id', 'timestamp', 'actor', 'action', 'page', 'detail', 'ip'];
|
|
const esc = (v) => `"${String(v == null ? '' : v).replace(/"/g, '""')}"`;
|
|
const lines = [head.join(',')];
|
|
for (const r of rows) {
|
|
lines.push([r.id, toDisplayDateTime(r.ts), r.actor, r.action, r.page, r.detail, r.ip].map(esc).join(','));
|
|
}
|
|
return lines.join('\r\n');
|
|
}
|
|
|
|
router.get('/export', (req, res) => {
|
|
const rows = query(req.query);
|
|
const csv = toCSV(rows);
|
|
log(req, 'LOGS_EXPORT', 'Logs', `Exported ${rows.length} log rows as CSV`);
|
|
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
res.setHeader('Content-Disposition', `attachment; filename="infosec-logs-${Date.now()}.csv"`);
|
|
res.send(csv);
|
|
});
|
|
|
|
router.post('/email', async (req, res) => {
|
|
const rows = query(req.body || {});
|
|
const csv = toCSV(rows);
|
|
const to = (req.body && req.body.to) || adminEmails().join(',');
|
|
if (!to) return res.status(400).json({ error: 'No recipient. Add an admin email or provide "to".' });
|
|
const html =
|
|
`<h2>Infosec — audit log export</h2>` +
|
|
`<p>${rows.length} log entries are attached as CSV.</p>` +
|
|
`<hr style="border:none;border-top:1px solid #d8d8d8">` +
|
|
`<p style="color:#8a8a8a;font-size:12px">© 2026 Martinhal IT - Joao Vaz - Version 2.3</p>`;
|
|
try {
|
|
const r = await sendMail({
|
|
to, subject: `Infosec audit logs (${rows.length} entries)`, html,
|
|
context: 'Audit log export', actor: req.session.user.username,
|
|
attachments: [{ filename: `infosec-logs-${Date.now()}.csv`, content: csv }],
|
|
});
|
|
log(req, 'LOGS_EMAIL', 'Logs', `Emailed ${rows.length} log rows to ${to} ${r.delivered ? '(delivered)' : '(queued, no SMTP)'}`);
|
|
res.json({ ok: true, delivered: r.delivered, count: rows.length });
|
|
} catch (e) {
|
|
log(req, 'MAIL_ERROR', 'Logs', `Log email failed: ${e.message}`);
|
|
res.status(500).json({ error: e.message });
|
|
}
|
|
});
|
|
|
|
module.exports = router;
|