Changeset 834bcc3 in Klonkt for src/routes/admin-settings.js
- Timestamp:
- 06/23/2026 06:14:27 PM (3 months ago)
- Branches:
- main
- Children:
- d774679
- Parents:
- bb42dfb
- File:
-
- 1 edited
-
src/routes/admin-settings.js (modified) (14 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/admin-settings.js
rbb42dfb r834bcc3 1 1 /** 2 * Admin: global e instellingen.3 * - tenancy -modus(Solo/Hub)4 * - hub -branding (naam/tagline/intro/hero van de generieke hub-hoofdpagina)2 * Admin: global settings. 3 * - tenancy mode (Solo/Hub) 4 * - hub branding (name/tagline/intro/hero of the generic hub home page) 5 5 * 6 * GET /admin/settings -> toon huidige instellingen7 * POST /admin/settings -> s la op (god-only). Accepteert nu ook een geuploade8 * hero -afbeelding (multipart); een upload wint van het9 * URL -tekstveld. Zonder upload blijft het URL-veld leidend.6 * GET /admin/settings -> show current settings 7 * POST /admin/settings -> save (god-only). Also accepts an uploaded 8 * hero image (multipart); an upload wins over the 9 * URL text field. Without an upload the URL field is leading. 10 10 * 11 * De hub-pagina is generiek (van geen enkele user); deze branding leeftin12 * global e settings, niet in eensite.11 * The hub page is generic (belonging to no user); this branding lives in 12 * global settings, not in a site. 13 13 */ 14 14 … … 30 30 const router = express.Router(); 31 31 32 // Hero dark -overlay: percentage 0-100 (0 = geen overlay, 100 = volledig zwart).33 // Default 45 = de oude hardgecodeerde waarde, zodat bestaande hubs niet wijzigen.32 // Hero dark overlay: percentage 0-100 (0 = no overlay, 100 = fully black). 33 // Default 45 = the old hard-coded value, so existing hubs don't change appearance. 34 34 function clampOverlay(raw) { 35 35 const v = parseInt(raw, 10); … … 38 38 39 39 const __dirname = path.dirname(fileURLToPath(import.meta.url)); 40 // Hero -uploads landen in storage/media/hero → bereikbaar als /media/hero/<file>41 // ( de /media static handler serveert storage/media). Zelfde model als avatars.40 // Hero uploads land in storage/media/hero → accessible as /media/hero/<file> 41 // (the /media static handler serves storage/media). Same model as avatars. 42 42 const HERO_DIR = path.resolve( 43 43 process.env.HERO_PATH || path.join(__dirname, '..', '..', 'storage', 'media', 'hero') … … 45 45 fs.mkdirSync(HERO_DIR, { recursive: true }); 46 46 47 // Alleen raster-formaten voor de upload. SVG mag bewust NIET via upload (raw48 // SVG kan script bevatten → opgeslagen-XSS bij direct openen); een SVG-hero kan49 // nog steeds via het URL-veld (zoals de meegeleverde demo-placeholder).47 // Only raster formats for upload. SVG is intentionally NOT allowed via upload 48 // (raw SVG can contain scripts → stored-XSS when opened directly); an SVG hero 49 // can still be set via the URL field (like the bundled demo placeholder). 50 50 const ALLOWED_HERO_EXT = new Set(['.jpg', '.jpeg', '.png', '.webp', '.gif']); 51 51 const MAX_HERO_BYTES = 5 * 1024 * 1024; … … 95 95 96 96 router.post('/', requireGod, (req, res) => { 97 // multer.single verwerkt multipart (hub-branding form). Bij een gewone98 // urlencoded POST (tenancy -form) doet multer niets en blijft req.bodyintact.97 // multer.single processes multipart (hub branding form). For a plain 98 // urlencoded POST (tenancy form) multer does nothing and req.body stays intact. 99 99 heroUpload.single('hub_hero_file')(req, res, (err) => { 100 100 if (err) { … … 103 103 104 104 if (typeof req.body.tenancy !== 'undefined') { 105 // Hub -modus is een premium-feature: alleen naar hub schakelen als premium106 // ontgrendeld is (premium-laag uit = vrij; aan = Patreon vereist). Al-hub107 // blijven mag altijd, zodat een instance nooit vastloopt.105 // Hub mode is a premium feature: only switch to hub if premium is 106 // unlocked (premium layer off = free; on = Patreon required). Staying on 107 // hub is always allowed, so an instance can never get stuck. 108 108 if (req.body.tenancy === 'hub' && !premiumUnlocked() && getTenancy() !== 'hub') { 109 109 return res.redirect('/admin/settings?error=' + encodeURIComponent('Hub-modus is een premium-functie — koppel Patreon in Beheer → Instellingen.')); … … 112 112 } 113 113 if (typeof req.body.default_lang !== 'undefined') { 114 // Standaardtaal voor bezoekers (leeg = volg env/browser). Valideert tegenNL/EN/DE.114 // Default language for visitors (empty = follow env/browser). Validated against NL/EN/DE. 115 115 const dl = (req.body.default_lang || '').toString().toLowerCase(); 116 116 setSetting('default_lang', SUPPORTED.includes(dl) ? dl : ''); 117 117 } 118 118 if (typeof req.body.timezone !== 'undefined') { 119 // Site -tijdzone (IANA, bv. Europe/Amsterdam). Leeg = server-default (UTC).120 // Valid eer met Intl zodat een onzin-waarde nooit de datum-rendering breekt.119 // Site timezone (IANA, e.g. Europe/Amsterdam). Empty = server default (UTC). 120 // Validate with Intl so a nonsense value never breaks date rendering. 121 121 const tz = (req.body.timezone || '').toString().trim(); 122 122 let valid = ''; … … 134 134 } 135 135 136 // Hero: een geüploade afbeelding wint; anders het URL-tekstveld.136 // Hero: an uploaded image wins; otherwise the URL text field. 137 137 if (req.file) { 138 138 const newUrl = `/media/hero/${toWebp(req.file)}`; 139 // Ruim een vorige geüploade hero op (alleen als die uit onze hero-map kwam).139 // Clean up a previously uploaded hero (only if it came from our hero dir). 140 140 const old = getSetting('hub_hero_image') || ''; 141 141 if (old.startsWith('/media/hero/')) { … … 155 155 }); 156 156 157 // Google -login op een eigen Beheer-pagina (los van de algemene instellingen).157 // Google login on its own admin page (separate from the general settings). 158 158 router.get('/google', requireGod, (req, res) => { 159 159 renderPage(req, res, 'pages/admin-google', { … … 171 171 }); 172 172 173 // Google-login (luisteraars) configureren— Client ID + Secret in app_settings.174 // De redirect-URI leiden we af van PUBLIC_BASE_URL (zie config/google.js).173 // Configure Google login (listeners) — Client ID + Secret in app_settings. 174 // The redirect URI is derived from PUBLIC_BASE_URL (see config/google.js). 175 175 router.post('/google', requireGod, (req, res) => { 176 176 if (req.body.clear === '1') { … … 180 180 } 181 181 setSetting('google_client_id', (req.body.google_client_id || '').toString().trim()); 182 // Secret alleen overschrijven als er een nieuwe waarde is ingevoerd (leeg = laat staan).182 // Only overwrite the secret if a new value was entered (empty = leave as-is). 183 183 const secret = (req.body.google_client_secret || '').toString().trim(); 184 184 if (secret) setSetting('google_client_secret', secret); … … 197 197 setSetting('smtp_user', (b.smtp_user || '').toString().trim()); 198 198 setSetting('smtp_from', (b.smtp_from || '').toString().trim()); 199 // Wachtwoord alleen overschrijven als er een nieuwe waarde is ingevoerd.199 // Only overwrite the password if a new value was entered. 200 200 const pass = (b.smtp_pass || '').toString(); 201 201 if (pass) setSetting('smtp_pass', pass); … … 203 203 }); 204 204 205 // N ieuwsbrief-aanmelding in de footer aan/uit.205 // Newsletter sign-up in the footer on/off. 206 206 router.post('/footer', requireGod, (req, res) => { 207 207 setSetting('footer_newsletter', req.body.footer_newsletter ? '1' : '0'); … … 209 209 }); 210 210 211 // Testmail sturen naar een opgegeven adres (of de ingelogde gebruiker).211 // Send a test email to a specified address (or the logged-in user). 212 212 router.post('/smtp/test', requireGod, async (req, res) => { 213 213 const to = ((req.body && req.body.to) || (req.session.user && req.session.user.email) || '').toString().trim();
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)