Files
ISDSS---Internal-Survailanc…/routes/settings.js
T

126 lines
5.8 KiB
JavaScript
Raw Normal View History

2026-09-13 20:12:28 +01:00
'use strict';
const express = require('express');
const path = require('path');
const fs = require('fs');
const crypto = require('crypto');
const multer = require('multer');
const { db } = require('../db');
const { log } = require('../lib/audit');
const { requireAuth, requireAdmin } = require('../middleware/auth');
const { getTemplate, buildEmail, KEYS, DEFAULTS, PLACEHOLDERS } = require('../lib/templates');
const router = express.Router();
const BRAND_DIR = process.env.BRAND_DIR || path.join(__dirname, '..', 'brand');
fs.mkdirSync(BRAND_DIR, { recursive: true });
const storage = multer.diskStorage({
destination: (req, file, cb) => cb(null, BRAND_DIR),
filename: (req, file, cb) =>
cb(null, `${Date.now()}-${crypto.randomBytes(5).toString('hex')}${path.extname(file.originalname)}`),
});
const okImage = (req, file, cb) =>
cb(null, /^image\//.test(file.mimetype));
const upload = multer({ storage, fileFilter: okImage, limits: { fileSize: 1024 * 1024 * 10 } });
// ---- Email templates --------------------------------------------------
router.get('/templates', requireAuth, requireAdmin, (req, res) => {
const list = KEYS.map((key) => {
const t = getTemplate(key);
return {
key,
...t,
// So the UI can offer "Restore default" when a template has drifted.
is_default: t.subject === DEFAULTS[key].subject && t.body_html === DEFAULTS[key].body_html,
placeholders: PLACEHOLDERS[key] || [],
};
});
res.json({ templates: list, keys: KEYS });
});
// Put a template back to the version that ships with this release. Useful
// after an upgrade, since existing customised templates are never overwritten.
router.post('/templates/:key/restore', requireAuth, requireAdmin, (req, res) => {
const key = req.params.key;
if (!KEYS.includes(key)) return res.status(400).json({ error: 'Unknown template.' });
const d = DEFAULTS[key];
const exists = db.prepare('SELECT key FROM email_templates WHERE key = ?').get(key);
if (exists) {
db.prepare('UPDATE email_templates SET subject = ?, body_html = ?, updated_at = CURRENT_TIMESTAMP WHERE key = ?')
.run(d.subject, d.body_html, key);
} else {
db.prepare('INSERT INTO email_templates (key, subject, body_html) VALUES (?, ?, ?)')
.run(key, d.subject, d.body_html);
}
log(req, 'TEMPLATE_RESTORED', 'Data Management', `Restored default email template "${key}"`);
res.json({ ok: true, template: { key, ...d } });
});
router.put('/templates/:key', requireAuth, requireAdmin, (req, res) => {
const key = req.params.key;
if (!KEYS.includes(key)) return res.status(400).json({ error: 'Unknown template.' });
const { subject, body_html } = req.body || {};
if (!subject || !body_html) return res.status(400).json({ error: 'Subject and body are required.' });
const exists = db.prepare('SELECT key FROM email_templates WHERE key = ?').get(key);
if (exists) {
db.prepare('UPDATE email_templates SET subject = ?, body_html = ?, updated_at = CURRENT_TIMESTAMP WHERE key = ?')
.run(subject, body_html, key);
} else {
db.prepare('INSERT INTO email_templates (key, subject, body_html) VALUES (?, ?, ?)')
.run(key, subject, body_html);
}
log(req, 'TEMPLATE_UPDATE', 'Data Management', `Updated email template "${key}"`);
res.json({ ok: true });
});
// Live preview with sample data
router.post('/templates/:key/preview', requireAuth, requireAdmin, (req, res) => {
const { subject, body_html } = req.body || {};
// temporarily render provided draft with sample vars, without saving
const vars = {
username: 'jsmith', email: 'jsmith@example.com',
target_type: 'folder', target_name: 'Q4 Financials',
created_at: '2026-07-22 10:30:00',
};
const render = require('../lib/templates').render;
const footer =
'<hr style="margin-top:32px;border:none;border-top:1px solid #d8d8d8">' +
'<p style="color:#8a8a8a;font-size:12px;margin-top:12px">© 2026 Martinhal IT - Joao Vaz - Version 1.5 Patch 0.2</p>';
res.json({
subject: render(subject || '', vars),
html: render(body_html || '', vars) + footer,
});
});
// ---- Brand images -----------------------------------------------------
router.get('/images', requireAuth, requireAdmin, (req, res) => {
const images = db.prepare('SELECT id, label, stored_name, mime, created_at FROM brand_images ORDER BY created_at DESC').all();
res.json({ images: images.map((i) => ({ ...i, url: `/settings/images/${i.stored_name}` })) });
});
router.post('/images', requireAuth, requireAdmin, upload.single('image'), (req, res) => {
if (!req.file) return res.status(400).json({ error: 'Please choose an image file.' });
const label = (req.body.label || req.file.originalname).trim();
const info = db.prepare('INSERT INTO brand_images (label, stored_name, mime) VALUES (?, ?, ?)')
.run(label, req.file.filename, req.file.mimetype);
log(req, 'IMAGE_UPLOAD', 'Data Management', `Uploaded brand image "${label}"`);
res.json({ ok: true, id: info.lastInsertRowid, url: `/settings/images/${req.file.filename}` });
});
router.delete('/images/:id', requireAuth, requireAdmin, (req, res) => {
const img = db.prepare('SELECT * FROM brand_images WHERE id = ?').get(Number(req.params.id));
if (!img) return res.status(404).json({ error: 'Image not found.' });
db.prepare('DELETE FROM brand_images WHERE id = ?').run(img.id);
try { fs.unlinkSync(path.join(BRAND_DIR, img.stored_name)); } catch (_) {}
log(req, 'IMAGE_DELETE', 'Data Management', `Deleted brand image "${img.label}"`);
res.json({ ok: true });
});
// Public-ish serve so email clients / preview can render them (auth still required to browse app)
router.get('/images/:stored', (req, res) => {
const img = db.prepare('SELECT * FROM brand_images WHERE stored_name = ?').get(req.params.stored);
if (!img) return res.status(404).end();
res.sendFile(path.join(BRAND_DIR, img.stored_name));
});
module.exports = router;