'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 = '
' + '

© 2026 Martinhal IT - Joao Vaz - Version 2.3

'; 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;