source: Klonkt/src/routes/epk.js@ 4dcefa1

main
Last change on this file since 4dcefa1 was e6ad948, checked in by roboburr <roboburr@…>, 3 months ago

feat(epk): choose up to 5 tracks for the press kit (instead of auto top-5)

Press kit editor now has a track picker (checkboxes, max 5). /pers shows the
chosen tracks in that order; no selection → automatically the top 5 most
played. Stored as epk_tracks_<siteId> (validated on site + max 5).

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

  • Property mode set to 100644
File size: 3.1 KB
Line 
1/**
2 * EPK / perskit (premium) — een deelbare perspagina per Klonkt-site.
3 *
4 * GET /pers (solo) of /user/:slug/pers (hub, via resolveSite + siteUrlBase)
5 * -> nette, openbare perskit: hero (foto/titel/tagline), korte bio, topnummers,
6 * recente posts en een contact-knop. Bedoeld om naar boekers/pers te sturen.
7 *
8 * Premium-gated: niet-premium instances hebben GEEN /pers (next() -> 404 via de
9 * catch-all). De PAGINA zelf is openbaar (geen login) zodat pers 'm kan bekijken;
10 * alleen het BESTAAN ervan is premium. Geen login-e-mail lekken: contact loopt via
11 * een expliciet ingesteld pers-adres (epk_contact, per site) of anders de site zelf.
12 */
13
14import express from 'express';
15import db from '../config/database.js';
16import { renderPage } from '../middleware/render.js';
17import { premiumUnlocked } from '../services/PatreonService.js';
18import { getSetting } from '../services/SettingsService.js';
19
20const router = express.Router();
21
22router.get('/pers', (req, res, next) => {
23 if (!premiumUnlocked()) return next(); // geen premium -> geen perskit
24 const site = res.locals.site;
25 if (!site) return next();
26
27 // Nummers op de perskit: een door de admin GEKOZEN selectie (max 5, in eigen
28 // volgorde) als die is ingesteld; anders automatisch de top 5 meest beluisterde.
29 let chosenIds = [];
30 try {
31 const raw = JSON.parse(getSetting('epk_tracks_' + site.id, '') || '[]');
32 if (Array.isArray(raw)) chosenIds = raw.filter((x) => typeof x === 'string').slice(0, 5);
33 } catch (e) { /* ongeldige JSON → val terug op top */ }
34
35 let tracks;
36 if (chosenIds.length) {
37 const ph = chosenIds.map(() => '?').join(',');
38 const rows = db.prepare(
39 `SELECT id, title, artist, duration, cover_url, COALESCE(play_count, 0) AS plays
40 FROM audio_tracks WHERE site_id = ? AND id IN (${ph})`
41 ).all(site.id, ...chosenIds);
42 const byId = new Map(rows.map((r) => [r.id, r]));
43 tracks = chosenIds.map((id) => byId.get(id)).filter(Boolean); // behoud gekozen volgorde
44 } else {
45 tracks = db.prepare(
46 `SELECT title, artist, duration, cover_url, COALESCE(play_count, 0) AS plays
47 FROM audio_tracks
48 WHERE site_id = ?
49 ORDER BY plays DESC, position ASC, created_at ASC
50 LIMIT 5`
51 ).all(site.id);
52 }
53
54 const posts = db.prepare(
55 `SELECT slug, title, created_at
56 FROM posts
57 WHERE site_id = ? AND status = 'published'
58 ORDER BY created_at DESC
59 LIMIT 5`
60 ).all(site.id);
61
62 // Pers-contact: per-site instelling (epk_contact_<siteId>) als die er is, anders
63 // de globale epk_contact. NOOIT automatisch de login-mail tonen.
64 const contact = (getSetting('epk_contact_' + site.id, '') || getSetting('epk_contact', '') || '').trim();
65 // Korte pers-bio: per-site instelling, anders de tagline van de site.
66 const bio = (getSetting('epk_bio_' + site.id, '') || site.tagline || '').trim();
67
68 renderPage(req, res, 'pages/epk', {
69 pageTitle: (site.title || 'Perskit') + ' — Perskit',
70 bodyClass: 'on-epk',
71 epkTracks: tracks,
72 epkPosts: posts,
73 epkContact: contact,
74 epkBio: bio,
75 });
76});
77
78export default router;
Note: See TracBrowser for help on using the repository browser.