source: Klonkt/src/middleware/site.js@ 1b4d5dd

main
Last change on this file since 1b4d5dd was 8453812, checked in by roboburr <roboburr@…>, 3 months ago

Branding: 100% PrutCMS/PrutFolio-free — Klonkt as original product

All fork/lineage references (README, package description, server/comments,
PrutFolio-as-noun -> Klonkt-site) removed. Plus a small, mysterious wink
at a certain Bart in CircleFederation.js. Internal package name (prutfolio)
+ PWA id + server paths left untouched (stability).

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

  • Property mode set to 100644
File size: 3.4 KB
Line 
1/**
2 * Site middleware — resolve which site this request is for.
3 *
4 * Resolution order:
5 * 1. Path /sites/:slug → that site
6 * 2. (Future) Subdomain bedrijf1.example.com → matching site
7 * 3. Default site (first one in DB)
8 *
9 * Sets res.locals.site for all downstream handlers.
10 */
11
12import db from '../config/database.js';
13import { getTenancy } from '../services/SettingsService.js';
14
15export function resolveSite(req, res, next) {
16 const tenancy = getTenancy();
17 res.locals.tenancy = tenancy; // ook beschikbaar voor views
18
19 // In HUB-mode mapt /sites/:slug en (later) een subdomein naar een specifieke
20 // site. In SOLO-mode bestaat er maar één site: we slaan die routing over en
21 // pinnen altijd op de primaire site.
22 if (tenancy === 'hub') {
23 // Een Klonkt-site is bereikbaar via /user/:slug (canoniek) én /sites/:slug (legacy).
24 const m = req.path.match(/^\/(sites|user)\/([a-zA-Z0-9_-]+)(\/.*)?$/);
25 if (m) {
26 const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get(m[2]);
27 if (site) {
28 res.locals.site = site;
29 req.url = (m[3] || '/'); // strip /<prefix>/:slug zodat downstream de rest ziet
30 res.locals.siteUrlBase = `/${m[1]}/${m[2]}`;
31 return next();
32 }
33 }
34 const host = req.get('host')?.toLowerCase().replace(/:\d+$/, '');
35 if (host) {
36 const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get(host);
37 if (site) {
38 res.locals.site = site;
39 res.locals.siteUrlBase = '';
40 return next();
41 }
42 }
43 }
44
45 // Solo (of hub zonder match): de primaire/owner-site (eerste aangemaakte).
46 const defaultSite = db.prepare('SELECT * FROM sites ORDER BY created_at ASC LIMIT 1').get();
47 if (defaultSite) {
48 res.locals.site = defaultSite;
49 res.locals.siteUrlBase = '';
50 }
51
52 next();
53}
54
55/**
56 * Audio tracks loader — pulls site-level tracks for the persistent player widget.
57 * Per Robin: player is separate from the footer, gated by site.enable_audio_player.
58 * Returns empty array if no site or audio is disabled — shell.ejs uses the
59 * length to decide whether to mount audio-player.js.
60 */
61export function loadAudioTracks(req, res, next) {
62 const site = res.locals.site;
63 if (!site || site.enable_audio_player === 0) {
64 res.locals.audioTracks = [];
65 return next();
66 }
67
68 try {
69 res.locals.audioTracks = db.prepare(`
70 SELECT t.id, t.title, t.artist, t.duration, t.position,
71 m.url AS media_url
72 FROM audio_tracks t
73 LEFT JOIN media m ON m.id = t.media_id
74 WHERE t.site_id = ?
75 ORDER BY t.position ASC, t.created_at ASC
76 `).all(site.id);
77 } catch (e) {
78 // media table might not be queryable in some test setups — fall back gracefully
79 res.locals.audioTracks = [];
80 }
81
82 next();
83}
84
85/**
86 * Theme loader — applies user/site theme preferences.
87 */
88export function loadTheme(req, res, next) {
89 const PALETTES = ['sage','paper','ocean','forest','stone','midnight','sunset','cream'];
90
91 const user = req.session?.user;
92 const site = res.locals.site;
93
94 // Priority: user setting > site setting > default
95 const palette = (user && PALETTES.includes(user.palette) ? user.palette : null)
96 || (site && PALETTES.includes(site.palette) ? site.palette : null)
97 || 'sage';
98
99 res.locals.palette = palette;
100 res.locals.theme = (user && ['dark','light'].includes(user.theme)) ? user.theme : 'dark';
101
102 next();
103}
Note: See TracBrowser for help on using the repository browser.