86 lines
3.3 KiB
JavaScript
86 lines
3.3 KiB
JavaScript
'use strict';
|
|||
|
|
const { db } = require('../db');
|
||
|
|
|
||
|
|
const DEFAULTS = {
|
||
|
|
request_to_admin: {
|
||
|
|
subject: 'Action needed: access request from {{username}} awaiting approval',
|
||
|
|
body_html:
|
||
|
|
'<h2>Access request pending approval</h2>' +
|
||
|
|
'<p><strong>{{username}}</strong> ({{email}}) has requested access to ' +
|
||
|
|
'the {{target_type}} <strong>{{target_name}}</strong>.</p>' +
|
||
|
|
'<p>Requested at {{created_at}}. Request reference #{{request_id}}.</p>' +
|
||
|
|
'<p><strong>{{pending_count}}</strong> request(s) are currently waiting for a decision.</p>' +
|
||
|
|
'<p><a href="{{approvals_url}}" style="display:inline-block;background:#1f7a70;color:#fff;' +
|
||
|
|
'padding:10px 18px;border-radius:8px;text-decoration:none;font-weight:600">' +
|
||
|
|
'Review pending requests</a></p>' +
|
||
|
|
'<p style="font-size:12px;color:#6b7280">If the button does not work, open: {{approvals_url}}</p>',
|
||
|
|
},
|
||
|
|
approval_to_user: {
|
||
|
|
subject: 'Your access request was approved',
|
||
|
|
body_html:
|
||
|
|
'<h2>Request approved</h2>' +
|
||
|
|
'<p>Hello {{username}},</p>' +
|
||
|
|
'<p>Your request to access the {{target_type}} <strong>{{target_name}}</strong> ' +
|
||
|
|
'has been <strong>approved</strong>. You may now open it from the View Data page.</p>' +
|
||
|
|
'<p><strong>{{validity}}</strong> — access expires on {{expires_at}}.</p>' +
|
||
|
|
'<p>A live countdown is shown next to the folder on the View Data page.</p>',
|
||
|
|
},
|
||
|
|
denial_to_user: {
|
||
|
|
subject: 'Your access request was declined',
|
||
|
|
body_html:
|
||
|
|
'<h2>Request declined</h2>' +
|
||
|
|
'<p>Hello {{username}},</p>' +
|
||
|
|
'<p>Unfortunately your request to access the {{target_type}} ' +
|
||
|
|
'<strong>{{target_name}}</strong> was not approved at this time.</p>',
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
function seedDefaults() {
|
||
|
|
const exists = db.prepare('SELECT key FROM email_templates WHERE key = ?');
|
||
|
|
const insert = db.prepare(
|
||
|
|
'INSERT INTO email_templates (key, subject, body_html) VALUES (?, ?, ?)'
|
||
|
|
);
|
||
|
|
for (const [key, t] of Object.entries(DEFAULTS)) {
|
||
|
|
if (!exists.get(key)) insert.run(key, t.subject, t.body_html);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function getTemplate(key) {
|
||
|
|
return (
|
||
|
|
db.prepare('SELECT * FROM email_templates WHERE key = ?').get(key) ||
|
||
|
|
DEFAULTS[key]
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
function render(str, vars) {
|
||
|
|
return String(str).replace(/\{\{\s*([\w.]+)\s*\}\}/g, (_, k) =>
|
||
|
|
vars[k] !== undefined && vars[k] !== null ? String(vars[k]) : ''
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Returns { subject, html } with placeholders filled and footer appended. */
|
||
|
|
function buildEmail(key, vars) {
|
||
|
|
const t = getTemplate(key);
|
||
|
|
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 2.3</p>';
|
||
|
|
return {
|
||
|
|
subject: render(t.subject, vars),
|
||
|
|
html: render(t.body_html, vars) + footer,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
const PLACEHOLDERS = {
|
||
|
|
request_to_admin: ['username', 'email', 'target_type', 'target_name', 'created_at',
|
||
|
|
'request_id', 'pending_count', 'approvals_url', 'portal_url'],
|
||
|
|
approval_to_user: ['username', 'email', 'target_type', 'target_name', 'portal_url',
|
||
|
|
'validity', 'expires_at'],
|
||
|
|
denial_to_user: ['username', 'email', 'target_type', 'target_name', 'portal_url'],
|
||
|
|
};
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
seedDefaults, getTemplate, render, buildEmail,
|
||
|
|
DEFAULTS, PLACEHOLDERS, KEYS: Object.keys(DEFAULTS),
|
||
|
|
};
|