source: Klonkt/src/routes/newsletter.js@ f9b1c5c

main
Last change on this file since f9b1c5c was 834bcc3, checked in by Robin Genis <roboburr@…>, 3 months ago

i18n: translate Dutch code comments to English across src/

Comments in routes/services/views/config/middleware/assets translated to
English for the public repo. A few dev-facing throw/console message strings
were Englished too. No user-facing UI strings or i18n dictionary values changed
(src/services/i18n.js untouched). Logic unchanged.

Co-Authored-By: Claude <noreply@…>

  • Property mode set to 100644
File size: 3.8 KB
RevLine 
[2e247e4]1/**
[834bcc3]2 * Newsletter — public side (premium feature #1).
[2e247e4]3 *
[834bcc3]4 * GET /nieuwsbrief -> sign-up form (premium; 404 otherwise)
5 * POST /nieuwsbrief -> subscribe (double opt-in if SMTP configured)
6 * GET /nieuwsbrief/bevestigen/:token -> confirm opt-in
7 * GET /nieuwsbrief/uitschrijven/:token -> unsubscribe (ALWAYS allowed)
[2e247e4]8 *
[834bcc3]9 * In hub mode this runs via /user/:slug/nieuwsbrief (resolveSite sets siteUrlBase).
10 * Confirm/unsub links in the mail are absolute (PUBLIC_BASE_URL + siteUrlBase).
[2e247e4]11 */
12
13import express from 'express';
14import { renderPage } from '../middleware/render.js';
15import { premiumUnlocked } from '../services/PatreonService.js';
16import { mailerConfigured, sendMail } from '../config/mailer.js';
17import { addSubscriber, confirm, unsubscribe } from '../services/SubscriberService.js';
18
19const router = express.Router();
20
21function esc(s) {
22 return String(s || '').replace(/[&<>"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]));
23}
24function fullUrl(req, siteUrlBase, p) {
25 const base = (process.env.PUBLIC_BASE_URL || ('https://' + (req.get('host') || ''))).replace(/\/$/, '');
26 return base + (siteUrlBase || '') + p;
27}
28function show(req, res, state, extra = {}) {
29 renderPage(req, res, 'pages/newsletter', {
30 pageTitle: 'Nieuwsbrief' + (res.locals.site ? ' — ' + res.locals.site.title : ''),
31 bodyClass: 'on-newsletter',
32 nlState: state,
33 ...extra,
34 });
35}
36
37router.get('/nieuwsbrief', (req, res, next) => {
38 if (!premiumUnlocked()) return next();
39 if (!res.locals.site) return next();
40 const fan = req.session && req.session.user;
41 const prefill = (fan && fan.email && fan.email.includes('@')) ? fan.email : '';
42 show(req, res, 'form', { nlPrefill: prefill });
43});
44
45router.post('/nieuwsbrief', async (req, res, next) => {
46 if (!premiumUnlocked()) return next();
47 const site = res.locals.site;
48 if (!site) return next();
49 const email = (req.body.email || '').trim();
50 const doubleOptin = mailerConfigured();
51 const r = addSubscriber(site.id, email, 'widget', { doubleOptin });
52 if (!r.ok) return show(req, res, r.error === 'invalid_email' ? 'invalid' : 'error', { nlPrefill: email });
53
54 if (r.status === 'pending') {
[834bcc3]55 // Double opt-in: send the confirmation email.
[2e247e4]56 const link = fullUrl(req, res.locals.siteUrlBase, '/nieuwsbrief/bevestigen/' + r.token);
57 const unsub = fullUrl(req, res.locals.siteUrlBase, '/nieuwsbrief/uitschrijven/' + r.token);
58 try {
59 await sendMail({
60 to: email,
61 subject: 'Bevestig je inschrijving — ' + (site.title || 'nieuwsbrief'),
62 text: 'Bevestig je inschrijving op de nieuwsbrief van ' + (site.title || '') + ':\n' + link +
63 '\n\nNiet aangevraagd? Negeer deze mail. Uitschrijven: ' + unsub,
64 html: '<p>Bevestig je inschrijving op de nieuwsbrief van <strong>' + esc(site.title) + '</strong>:</p>' +
65 '<p><a href="' + link + '">Inschrijving bevestigen</a></p>' +
66 '<p style="color:#888;font-size:12px">Niet aangevraagd? Negeer deze mail. ' +
67 '<a href="' + unsub + '">Uitschrijven</a></p>',
68 });
69 } catch (e) {
70 return show(req, res, 'smtperror');
71 }
72 return show(req, res, 'check', { nlEmail: email });
73 }
74 return show(req, res, 'done', { nlEmail: email });
75});
76
77router.get('/nieuwsbrief/bevestigen/:token', (req, res, next) => {
78 if (!premiumUnlocked()) return next();
79 const ok = confirm(req.params.token);
80 show(req, res, ok ? 'confirmed' : 'badtoken');
81});
82
[834bcc3]83// Unsubscribe is always allowed (even if the premium layer is later disabled): a
84// subscriber must always be able to opt out. Not premium-gated.
[2e247e4]85router.get('/nieuwsbrief/uitschrijven/:token', (req, res) => {
86 const ok = unsubscribe(req.params.token);
87 show(req, res, ok ? 'unsubbed' : 'badtoken');
88});
89
90export default router;
Note: See TracBrowser for help on using the repository browser.