Changeset 6351545 in Klonkt
- Timestamp:
- 06/14/2026 08:18:50 AM (3 months ago)
- Branches:
- main
- Children:
- 07775b7
- Parents:
- 9e27d64
- Location:
- src
- Files:
-
- 3 added
- 5 edited
-
config/database.js (modified) (1 diff)
-
middleware/site.js (modified) (1 diff)
-
routes/admin-settings.js (added)
-
routes/admin.js (modified) (3 diffs)
-
server.js (modified) (2 diffs)
-
services/SettingsService.js (added)
-
views/pages/admin-settings.ejs (added)
-
views/pages/admin.ejs (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/config/database.js
r9e27d64 r6351545 93 93 ON playlist_tracks(playlist_id, position); 94 94 `); 95 96 // Globale app-instellingen (key/value singleton). O.a. de tenancy-modus 97 // (solo = één site, hub = bedrijfssite + /gebruikers). Default = solo. 98 db.exec(` 99 CREATE TABLE IF NOT EXISTS app_settings ( 100 key TEXT PRIMARY KEY, 101 value TEXT, 102 updated_at DATETIME DEFAULT CURRENT_TIMESTAMP 103 ); 104 `); 105 db.prepare("INSERT OR IGNORE INTO app_settings (key, value) VALUES ('tenancy', 'solo')").run(); 95 106 } 96 107 -
src/middleware/site.js
r9e27d64 r6351545 11 11 12 12 import db from '../config/database.js'; 13 import { getTenancy } from '../services/SettingsService.js'; 13 14 14 15 export function resolveSite(req, res, next) { 15 // Try /sites/:slug pattern 16 const m = req.path.match(/^\/sites\/([a-zA-Z0-9_-]+)(\/.*)?$/); 17 if (m) { 18 const slug = m[1]; 19 const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get(slug); 20 if (site) { 21 res.locals.site = site; 22 // Strip /sites/:slug from req.url so downstream routes see the rest 23 req.url = (m[2] || '/'); 24 // Also rewrite originalUrl for redirect targets to keep the prefix 25 res.locals.siteUrlBase = `/sites/${slug}`; 26 return 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 const m = req.path.match(/^\/sites\/([a-zA-Z0-9_-]+)(\/.*)?$/); 24 if (m) { 25 const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get(m[1]); 26 if (site) { 27 res.locals.site = site; 28 req.url = (m[2] || '/'); // strip /sites/:slug zodat downstream de rest ziet 29 res.locals.siteUrlBase = `/sites/${m[1]}`; 30 return next(); 31 } 32 } 33 const host = req.get('host')?.toLowerCase().replace(/:\d+$/, ''); 34 if (host) { 35 const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get(host); 36 if (site) { 37 res.locals.site = site; 38 res.locals.siteUrlBase = ''; 39 return next(); 40 } 27 41 } 28 42 } 29 43 30 // Future: subdomain mapping 31 const host = req.get('host')?.toLowerCase().replace(/:\d+$/, ''); 32 if (host) { 33 const site = db.prepare('SELECT * FROM sites WHERE slug = ?').get(host); 34 if (site) { 35 res.locals.site = site; 36 res.locals.siteUrlBase = ''; 37 return next(); 38 } 39 } 40 41 // Default: pick first site 44 // Solo (of hub zonder match): de primaire/owner-site (eerste aangemaakte). 42 45 const defaultSite = db.prepare('SELECT * FROM sites ORDER BY created_at ASC LIMIT 1').get(); 43 46 if (defaultSite) { -
src/routes/admin.js
r9e27d64 r6351545 9 9 import { renderPage } from '../middleware/render.js'; 10 10 import { requireGod } from '../middleware/auth.js'; 11 import { getTenancy } from '../services/SettingsService.js'; 11 12 12 13 const router = express.Router(); 13 14 14 15 router.get('/', requireGod, (req, res) => { 16 const tenancy = getTenancy(); 17 18 // De primaire/owner-site — in solo dé site, in hub de hoofdsite. Geeft de 19 // "Uiterlijk"-tegel z'n edit-link. 20 const primarySite = db.prepare( 21 'SELECT slug, title FROM sites ORDER BY created_at ASC LIMIT 1' 22 ).get() || null; 23 15 24 const stats = { 16 25 users: db.prepare('SELECT COUNT(*) AS c FROM users').get().c, … … 22 31 }; 23 32 24 const sites = db.prepare(` 33 // Sites/users-tabellen zijn alleen in hub relevant; in solo besparen we de query. 34 const sites = tenancy === 'hub' ? db.prepare(` 25 35 SELECT s.slug, s.title, s.created_at, u.username AS owner_username 26 36 FROM sites s … … 28 38 ORDER BY s.created_at DESC 29 39 LIMIT 50 30 `).all() ;40 `).all() : []; 31 41 32 const users = db.prepare(`42 const users = tenancy === 'hub' ? db.prepare(` 33 43 SELECT username, email, role, created_at 34 44 FROM users 35 45 ORDER BY created_at DESC 36 46 LIMIT 50 37 `).all() ;47 `).all() : []; 38 48 39 49 renderPage(req, res, 'pages/admin', { 40 pageTitle: ' Admin',50 pageTitle: 'Beheer', 41 51 bodyClass: 'on-admin', 52 tenancy, 53 primarySite, 42 54 stats, 43 55 sites, -
src/server.js
r9e27d64 r6351545 29 29 import adminUsersRoutes from './routes/admin-users.js'; 30 30 import adminCommentsRoutes from './routes/admin-comments.js'; 31 import adminSettingsRoutes from './routes/admin-settings.js'; 31 32 import prutterRoutes from './routes/prutter.js'; 32 33 import audioRoutes from './routes/audio.js'; … … 169 170 app.use('/admin/users', adminUsersRoutes); 170 171 app.use('/admin/comments', adminCommentsRoutes); 172 app.use('/admin/settings', adminSettingsRoutes); 171 173 app.use('/admin', adminRoutes); 172 174 app.use('/prutter', prutterRoutes); -
src/views/pages/admin.ejs
r9e27d64 r6351545 1 1 <div class="container admin-page"> 2 <h1>Admin</h1> 3 <p class="admin-tagline">God-level overview. <em>(Stub — full dashboard later.)</em></p> 2 <h1>Beheer</h1> 3 <% if (tenancy === 'hub') { %> 4 <p class="admin-tagline">Hub-modus — bedrijfssite met gebruikers, elk hun eigen PrutFolio.</p> 5 <% } else { %> 6 <p class="admin-tagline">Solo-modus — jouw site.</p> 7 <% } %> 4 8 5 9 <nav class="admin-quick-links" aria-label="Beheer-snelkoppelingen"> 6 <a href="/admin/sites" class="btn">🌐 Sites</a> 7 <a href="/admin/users" class="btn">👥 Users</a> 8 <a href="/admin/audio" class="btn">🎵 Audio tracks</a> 9 <a href="/admin/playlists" class="btn">📃 Playlists</a> 10 <a href="/admin/comments" class="btn">💬 Comments</a> 10 <% if (tenancy === 'hub') { %> 11 <a href="/admin/sites" class="btn">🌐 Sites</a> 12 <a href="/admin/users" class="btn">👥 Gebruikers</a> 13 <a href="/admin/audio" class="btn">🎵 Audio</a> 14 <a href="/admin/playlists" class="btn">📃 Playlists</a> 15 <a href="/admin/comments" class="btn">💬 Reacties</a> 16 <a href="/admin/settings" class="btn">⚙️ Instellingen</a> 17 <% } else { %> 18 <a href="/posts/new" class="btn">✍️ Nieuwe post</a> 19 <a href="/admin/audio" class="btn">🎵 Audio</a> 20 <a href="/admin/comments" class="btn">💬 Reacties</a> 21 <% if (primarySite) { %> 22 <a href="/admin/sites/<%= primarySite.slug %>/edit" class="btn">🎨 Uiterlijk</a> 23 <% } %> 24 <a href="/account" class="btn">👤 Account</a> 25 <a href="/admin/settings" class="btn">⚙️ Instellingen</a> 26 <% } %> 11 27 </nav> 12 28 13 29 <div class="admin-stats"> 14 <div class="stat-card"> 15 <div class="stat-num"><%= stats.users %></div> 16 <div class="stat-label">Users</div> 17 </div> 18 <div class="stat-card"> 19 <div class="stat-num"><%= stats.sites %></div> 20 <div class="stat-label">Sites</div> 21 </div> 22 <div class="stat-card"> 23 <div class="stat-num"><%= stats.posts %></div> 24 <div class="stat-label">Posts</div> 25 </div> 26 <div class="stat-card"> 27 <div class="stat-num"><%= stats.published %></div> 28 <div class="stat-label">Published</div> 29 </div> 30 <% if (tenancy === 'hub') { %> 31 <div class="stat-card"><div class="stat-num"><%= stats.users %></div><div class="stat-label">Gebruikers</div></div> 32 <div class="stat-card"><div class="stat-num"><%= stats.sites %></div><div class="stat-label">Sites</div></div> 33 <% } %> 34 <div class="stat-card"><div class="stat-num"><%= stats.posts %></div><div class="stat-label">Posts</div></div> 35 <div class="stat-card"><div class="stat-num"><%= stats.published %></div><div class="stat-label">Gepubliceerd</div></div> 30 36 </div> 31 37 … … 68 74 <% } %> 69 75 70 <p class="admin-todo">71 <strong>Coming later:</strong> create/delete sites, manage users, theme picker, audio toggles, Prutter settings.72 </p>73 76 </div> 74 77
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)