source: Klonkt/src/middleware/site.js@ 91094a4

main
Last change on this file since 91094a4 was 94a876f, checked in by roboburr <roboburr@…>, 3 months ago

Hub: removed dead host-based site resolving (slug == hostname could never match)

Slugs may not contain dots (validation [a-z0-9_-]), so the "WHERE slug = host"
lookup never matched — dead code + an unnecessary DB query per hub request. Removed.
Real subdomain routing would match the subdomain label against the slug (separate
feature).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@…>

  • Property mode set to 100644
File size: 4.0 KB
Line 
1/**
2 * Site middleware — resolve which site this request is for.
3 *
4 * Resolution order (hub-modus):
5 * 1. Pad /user/:slug → die site (legacy /sites/:slug → 301 naar /user/)
6 * 2. Anders (solo, of hub-landing): de primaire/hoofd-site
7 *
8 * Sets res.locals.site for all downstream handlers.
9 */
10
11import db from '../config/database.js';
12import { getTenancy } from '../services/SettingsService.js';
13
14/**
15 * De primaire/hoofd-site — ÉÉN bron van waarheid (vervangt de "oudste site ="
16 * hoofd"-aanname die voorheen los in resolveSite/hub/account/admin stond).
17 * Leest de expliciete is_primary-vlag; valt terug op de oudste als die (nog)
18 * nergens staat, zodat bestaand gedrag exact behouden blijft.
19 */
20export function getPrimarySite() {
21 return db.prepare('SELECT * FROM sites WHERE is_primary = 1 LIMIT 1').get()
22 || db.prepare('SELECT * FROM sites ORDER BY created_at ASC LIMIT 1').get()
23 || null;
24}
25
26export function resolveSite(req, res, next) {
27 const tenancy = getTenancy();
28 res.locals.tenancy = tenancy; // ook beschikbaar voor views
29
30 // In HUB-mode mapt /user/:slug naar een specifieke site. In SOLO-mode bestaat
31 // er maar één site: we slaan die routing over en pinnen op de primaire site.
32 if (tenancy === 'hub') {
33 // Een Klonkt-site is canoniek bereikbaar via /user/:slug. /sites/:slug is een
34 // legacy-alias → 301 naar de canonieke vorm zodat er één URL-schema overblijft
35 // (behoudt pad + querystring; raakt /admin/sites NIET, dat begint met /admin/).
36 const m = req.path.match(/^\/(sites|user)\/([a-zA-Z0-9_-]+)(\/.*)?$/);
37 if (m) {
38 if (m[1] === 'sites') {
39 return res.redirect(301, req.originalUrl.replace(/^\/sites\//, '/user/'));
40 }
41 const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get(m[2]);
42 if (site) {
43 res.locals.site = site;
44 req.url = (m[3] || '/'); // strip /user/:slug zodat downstream de rest ziet
45 res.locals.siteUrlBase = `/user/${m[2]}`;
46 return next();
47 }
48 }
49 // (Verwijderd: een dode "slug == hostname"-subdomein-hack. Slugs mogen geen
50 // punten bevatten, dus die kon nooit matchen. Echte subdomein-routing zou de
51 // subdomein-LABEL tegen de slug matchen — een aparte feature, niet dit.)
52 }
53
54 // Solo (of hub zonder match): pin op de primaire/hoofd-site.
55 const defaultSite = getPrimarySite();
56 if (defaultSite) {
57 res.locals.site = defaultSite;
58 res.locals.siteUrlBase = '';
59 }
60
61 next();
62}
63
64/**
65 * Audio tracks loader — pulls site-level tracks for the persistent player widget.
66 * Per Robin: player is separate from the footer, gated by site.enable_audio_player.
67 * Returns empty array if no site or audio is disabled — shell.ejs uses the
68 * length to decide whether to mount audio-player.js.
69 */
70export function loadAudioTracks(req, res, next) {
71 const site = res.locals.site;
72 if (!site || site.enable_audio_player === 0) {
73 res.locals.audioTracks = [];
74 return next();
75 }
76
77 try {
78 res.locals.audioTracks = db.prepare(`
79 SELECT t.id, t.title, t.artist, t.duration, t.position,
80 m.url AS media_url
81 FROM audio_tracks t
82 LEFT JOIN media m ON m.id = t.media_id
83 WHERE t.site_id = ?
84 ORDER BY t.position ASC, t.created_at ASC
85 `).all(site.id);
86 } catch (e) {
87 // media table might not be queryable in some test setups — fall back gracefully
88 res.locals.audioTracks = [];
89 }
90
91 next();
92}
93
94/**
95 * Theme loader — applies user/site theme preferences.
96 */
97export function loadTheme(req, res, next) {
98 const PALETTES = ['sage','paper','ocean','forest','stone','midnight','sunset','cream'];
99
100 const user = req.session?.user;
101 const site = res.locals.site;
102
103 // Priority: user setting > site setting > default
104 const palette = (user && PALETTES.includes(user.palette) ? user.palette : null)
105 || (site && PALETTES.includes(site.palette) ? site.palette : null)
106 || 'sage';
107
108 res.locals.palette = palette;
109 res.locals.theme = (user && ['dark','light'].includes(user.theme)) ? user.theme : 'dark';
110
111 next();
112}
Note: See TracBrowser for help on using the repository browser.