| 1 | /**
|
|---|
| 2 | * Show agenda + notify-me (premium feature #8) — public side.
|
|---|
| 3 | *
|
|---|
| 4 | * GET /shows -> upcoming gigs + "keep me posted" form
|
|---|
| 5 | * POST /shows/notify -> subscribe to show announcements (subscribers, source
|
|---|
| 6 | * 'notify'; double opt-in if SMTP configured)
|
|---|
| 7 | *
|
|---|
| 8 | * The notify confirm/unsubscribe reuses the generic subscriber links
|
|---|
| 9 | * (/nieuwsbrief/bevestigen|uitschrijven/:token). Hub: /user/:slug/shows.
|
|---|
| 10 | */
|
|---|
| 11 |
|
|---|
| 12 | import express from 'express';
|
|---|
| 13 | import db from '../config/database.js';
|
|---|
| 14 | import { renderPage } from '../middleware/render.js';
|
|---|
| 15 | import { premiumUnlocked } from '../services/PatreonService.js';
|
|---|
| 16 | import { mailerConfigured, sendMail } from '../config/mailer.js';
|
|---|
| 17 | import { addSubscriber } from '../services/SubscriberService.js';
|
|---|
| 18 | import { getSetting } from '../services/SettingsService.js';
|
|---|
| 19 |
|
|---|
| 20 | const router = express.Router();
|
|---|
| 21 |
|
|---|
| 22 | // Agenda is opt-in: only accessible once the admin has enabled it.
|
|---|
| 23 | function agendaOn() { return getSetting('agenda_enabled') === '1'; }
|
|---|
| 24 |
|
|---|
| 25 | function esc(s) { return String(s || '').replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])); }
|
|---|
| 26 | function fullUrl(req, p) {
|
|---|
| 27 | const base = (process.env.PUBLIC_BASE_URL || ('https://' + (req.get('host') || ''))).replace(/\/$/, '');
|
|---|
| 28 | return base + (req.res.locals.siteUrlBase || '') + p;
|
|---|
| 29 | }
|
|---|
| 30 | function upcoming(siteId) {
|
|---|
| 31 | const today = new Date().toISOString().slice(0, 10);
|
|---|
| 32 | return db.prepare('SELECT * FROM shows WHERE site_id = ? AND date >= ? ORDER BY date ASC, time ASC').all(siteId, today);
|
|---|
| 33 | }
|
|---|
| 34 |
|
|---|
| 35 | router.get('/shows', (req, res, next) => {
|
|---|
| 36 | if (!premiumUnlocked() || !agendaOn()) return next();
|
|---|
| 37 | const site = res.locals.site;
|
|---|
| 38 | if (!site) return next();
|
|---|
| 39 | renderPage(req, res, 'pages/shows', {
|
|---|
| 40 | pageTitle: 'Agenda — ' + (site.title || ''),
|
|---|
| 41 | bodyClass: 'on-shows',
|
|---|
| 42 | shows: upcoming(site.id),
|
|---|
| 43 | notifyState: req.query.ok ? 'done' : (req.query.check ? 'check' : null),
|
|---|
| 44 | });
|
|---|
| 45 | });
|
|---|
| 46 |
|
|---|
| 47 | router.post('/shows/notify', async (req, res, next) => {
|
|---|
| 48 | if (!premiumUnlocked() || !agendaOn()) return next();
|
|---|
| 49 | const site = res.locals.site;
|
|---|
| 50 | if (!site) return next();
|
|---|
| 51 | const email = (req.body.email || '').trim();
|
|---|
| 52 | const doubleOptin = mailerConfigured();
|
|---|
| 53 | const r = addSubscriber(site.id, email, 'notify', { doubleOptin });
|
|---|
| 54 | if (!r.ok) {
|
|---|
| 55 | return renderPage(req, res, 'pages/shows', {
|
|---|
| 56 | pageTitle: 'Agenda', bodyClass: 'on-shows', shows: upcoming(site.id),
|
|---|
| 57 | notifyState: 'error', notifyMsg: r.error === 'invalid_email' ? 'Controleer je e-mailadres.' : 'Er ging iets mis.',
|
|---|
| 58 | });
|
|---|
| 59 | }
|
|---|
| 60 | if (r.status === 'pending') {
|
|---|
| 61 | const link = fullUrl(req, '/nieuwsbrief/bevestigen/' + r.token);
|
|---|
| 62 | const unsub = fullUrl(req, '/nieuwsbrief/uitschrijven/' + r.token);
|
|---|
| 63 | try {
|
|---|
| 64 | await sendMail({
|
|---|
| 65 | to: email,
|
|---|
| 66 | subject: 'Bevestig — show-updates van ' + (site.title || ''),
|
|---|
| 67 | text: 'Bevestig dat je show-aankondigingen wilt ontvangen: ' + link + '\n\nUitschrijven: ' + unsub,
|
|---|
| 68 | html: '<p>Bevestig dat je show-aankondigingen van <strong>' + esc(site.title) + '</strong> wilt ontvangen:</p>' +
|
|---|
| 69 | '<p><a href="' + link + '">Bevestigen</a></p><p style="color:#888;font-size:12px"><a href="' + unsub + '">Uitschrijven</a></p>',
|
|---|
| 70 | });
|
|---|
| 71 | } catch { return res.redirect((res.locals.siteUrlBase || '') + '/shows'); }
|
|---|
| 72 | return res.redirect((res.locals.siteUrlBase || '') + '/shows?check=1');
|
|---|
| 73 | }
|
|---|
| 74 | res.redirect((res.locals.siteUrlBase || '') + '/shows?ok=1');
|
|---|
| 75 | });
|
|---|
| 76 |
|
|---|
| 77 | export default router;
|
|---|