126 lines
5.8 KiB
JavaScript
126 lines
5.8 KiB
JavaScript
'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;
|