source: Klonkt/src/routes/epk.js@ 3f9f71d

main
Last change on this file since 3f9f71d 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.0 KB
Line 
1/**
2 * EPK / press kit (premium) — a shareable press page per Klonkt site.
3 *
4 * GET /pers (solo) or /user/:slug/pers (hub, via resolveSite + siteUrlBase)
5 * -> clean, public press kit: hero (photo/title/tagline), short bio, top tracks,
6 * recent posts and a contact button. Intended to share with bookers/press.
7 *
8 * Premium-gated: non-premium instances have NO /pers (next() -> 404 via the
9 * catch-all). The PAGE itself is public (no login) so press can view it;
10 * only its EXISTENCE is premium. No login email leak: contact goes via an
11 * explicitly configured press address (epk_contact, per site) or the site itself.
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(); // no premium -> no press kit
24 const site = res.locals.site;
25 if (!site) return next();
26
27 // Tracks on the press kit: an admin-CHOSEN selection (max 5, in custom order)
28 // if configured; otherwise automatically the top 5 most-listened.
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) { /* invalid JSON → fall back to 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); // preserve chosen order
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 // Press contact: per-site setting (epk_contact_<siteId>) if present, otherwise
63 // the global epk_contact. NEVER auto-expose the login email.
64 const contact = (getSetting('epk_contact_' + site.id, '') || getSetting('epk_contact', '') || '').trim();
65 // Short press bio: per-site setting, otherwise the site's tagline.
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.