BillTracker/routes/notifications.js

157 lines
6.1 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const express = require('express');
const router = express.Router();
const { getDb, getSetting, setSetting } = require('../db/database');
const { requireAuth, requireUser, requireAdmin } = require('../middleware/requireAuth');
const { sendTestEmail } = require('../services/notificationService');
const { sendTestPush } = require('../services/notificationService')._push || {};
const { decryptSecret } = require('../services/encryptionService.cts');
const { encryptSecret } = require('../services/encryptionService.cts');
// ── Admin: SMTP configuration ─────────────────────────────────────────────────
// GET /api/notifications/admin — read all SMTP settings (password masked)
router.get('/admin', requireAuth, requireAdmin, (req, res) => {
const keys = [
'notify_smtp_enabled', 'notify_sender_name', 'notify_sender_address',
'notify_smtp_host', 'notify_smtp_port', 'notify_smtp_encryption',
'notify_smtp_self_signed', 'notify_smtp_username', 'notify_smtp_password',
'notify_allow_user_config', 'notify_global_recipient', 'reminder_hour',
];
const settings = {};
for (const k of keys) settings[k] = getSetting(k) || '';
if (!settings.reminder_hour) settings.reminder_hour = '6';
// Mask password in response
if (settings.notify_smtp_password) settings.notify_smtp_password = '••••••••';
res.json(settings);
});
// PUT /api/notifications/admin — save SMTP settings
router.put('/admin', requireAuth, requireAdmin, (req, res) => {
const allowed = [
'notify_smtp_enabled', 'notify_sender_name', 'notify_sender_address',
'notify_smtp_host', 'notify_smtp_port', 'notify_smtp_encryption',
'notify_smtp_self_signed', 'notify_smtp_username',
'notify_allow_user_config', 'notify_global_recipient',
];
for (const key of allowed) {
if (req.body[key] !== undefined) setSetting(key, req.body[key]);
}
// Only update password if a real value was sent (not the masked placeholder)
if (req.body.notify_smtp_password && !req.body.notify_smtp_password.startsWith('•')) {
setSetting('notify_smtp_password', encryptSecret(req.body.notify_smtp_password));
}
// Reminder send-hour (023, global): persist clamped + live-reschedule the cron.
if (req.body.reminder_hour !== undefined) {
const h = parseInt(req.body.reminder_hour, 10);
const hour = Number.isInteger(h) && h >= 0 && h <= 23 ? h : 6;
setSetting('reminder_hour', String(hour));
try {
require('../workers/dailyWorker').rescheduleDailyWorker();
} catch (err) {
console.error('[notifications] Failed to reschedule daily worker:', err.message);
}
}
res.json({ success: true });
});
// POST /api/notifications/test — send a test email
router.post('/test', requireAuth, requireAdmin, async (req, res) => {
const { to } = req.body;
if (!to) return res.status(400).json({ error: 'Recipient address required' });
try {
await sendTestEmail(to);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
// ── User: notification preferences ───────────────────────────────────────────
// GET /api/notifications/me — user prefs + whether admin has enabled it
router.get('/me', requireAuth, requireUser, (req, res) => {
const db = getDb();
const user = db.prepare(`
SELECT notification_email, notifications_enabled,
notify_3d, notify_1d, notify_due, notify_overdue, notify_amount_change
FROM users WHERE id = ?
`).get(req.user.id);
res.json({
smtp_enabled: getSetting('notify_smtp_enabled') === 'true',
allow_user_config: getSetting('notify_allow_user_config') === 'true',
notification_email: user.notification_email || '',
notifications_enabled: !!user.notifications_enabled,
notify_3d: !!user.notify_3d,
notify_1d: !!user.notify_1d,
notify_due: !!user.notify_due,
notify_overdue: !!user.notify_overdue,
notify_amount_change: user.notify_amount_change !== 0,
});
});
// PUT /api/notifications/me — save user prefs
router.put('/me', requireAuth, requireUser, (req, res) => {
const db = getDb();
const {
notification_email, notifications_enabled,
notify_3d, notify_1d, notify_due, notify_overdue, notify_amount_change,
} = req.body;
db.prepare(`
UPDATE users SET
notification_email = ?,
notifications_enabled = ?,
notify_3d = ?,
notify_1d = ?,
notify_due = ?,
notify_overdue = ?,
notify_amount_change = ?,
updated_at = datetime('now')
WHERE id = ?
`).run(
notification_email || null,
notifications_enabled ? 1 : 0,
notify_3d !== false ? 1 : 0,
notify_1d !== false ? 1 : 0,
notify_due !== false ? 1 : 0,
notify_overdue !== false ? 1 : 0,
notify_amount_change !== false ? 1 : 0,
req.user.id,
);
res.json({ success: true });
});
// POST /api/notifications/test-push — send a test push notification to the current user
router.post('/test-push', requireAuth, requireUser, async (req, res) => {
const db = getDb();
const user = db.prepare(`
SELECT notify_push_enabled, push_channel, push_url, push_token, push_chat_id
FROM users WHERE id = ?
`).get(req.user.id);
if (!user?.push_channel || !user?.push_url) {
return res.status(400).json({ error: 'Push notification channel not configured' });
}
// Decrypt for sending
const safeDecrypt = (v) => { try { return v ? decryptSecret(v) : ''; } catch { return v || ''; } };
const userForPush = {
...user,
notify_push_enabled: 1,
push_url: safeDecrypt(user.push_url),
push_token: safeDecrypt(user.push_token),
};
try {
if (!sendTestPush) throw new Error('Push service not initialised');
await sendTestPush(userForPush);
res.json({ success: true });
} catch (err) {
res.status(500).json({ error: err.message });
}
});
module.exports = router;