Changes in src/routes/admin-settings.js [63edb0d:0091cb7] in Klonkt
- File:
-
- 1 edited
-
src/routes/admin-settings.js (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/routes/admin-settings.js
r63edb0d r0091cb7 2 2 * Admin: globale instellingen. 3 3 * - tenancy-modus (Solo/Hub) 4 * - hub-branding (naam/tagline/intro /herovan de generieke hub-hoofdpagina)4 * - hub-branding (naam/tagline/intro van de generieke hub-hoofdpagina) 5 5 * 6 6 * GET /admin/settings -> toon huidige instellingen 7 * POST /admin/settings -> sla op (god-only). Accepteert nu ook een geuploade 8 * hero-afbeelding (multipart); een upload wint van het 9 * URL-tekstveld. Zonder upload blijft het URL-veld leidend. 7 * POST /admin/settings -> sla op (god-only) 10 8 * 11 9 * De hub-pagina is generiek (van geen enkele user); deze branding leeft in … … 14 12 15 13 import express from 'express'; 16 import path from 'path';17 import fs from 'fs';18 import { fileURLToPath } from 'url';19 import multer from 'multer';20 import { v4 as uuid } from 'uuid';21 14 import { renderPage } from '../middleware/render.js'; 22 15 import { requireGod } from '../middleware/auth.js'; … … 24 17 25 18 const router = express.Router(); 26 27 // Hero dark-overlay: percentage 0-100 (0 = geen overlay, 100 = volledig zwart).28 // Default 45 = de oude hardgecodeerde waarde, zodat bestaande hubs niet wijzigen.29 function clampOverlay(raw) {30 const v = parseInt(raw, 10);31 return Number.isFinite(v) ? Math.max(0, Math.min(100, v)) : 45;32 }33 34 const __dirname = path.dirname(fileURLToPath(import.meta.url));35 // Hero-uploads landen in storage/media/hero → bereikbaar als /media/hero/<file>36 // (de /media static handler serveert storage/media). Zelfde model als avatars.37 const HERO_DIR = path.resolve(38 process.env.HERO_PATH || path.join(__dirname, '..', '..', 'storage', 'media', 'hero')39 );40 fs.mkdirSync(HERO_DIR, { recursive: true });41 42 // Alleen raster-formaten voor de upload. SVG mag bewust NIET via upload (raw43 // SVG kan script bevatten → opgeslagen-XSS bij direct openen); een SVG-hero kan44 // nog steeds via het URL-veld (zoals de meegeleverde demo-placeholder).45 const ALLOWED_HERO_EXT = new Set(['.jpg', '.jpeg', '.png', '.webp', '.gif']);46 const MAX_HERO_BYTES = 5 * 1024 * 1024;47 48 const heroUpload = multer({49 storage: multer.diskStorage({50 destination: (req, file, cb) => cb(null, HERO_DIR),51 filename: (req, file, cb) => {52 const ext = path.extname(file.originalname).toLowerCase();53 cb(null, `${uuid()}${ext}`);54 },55 }),56 limits: { fileSize: MAX_HERO_BYTES },57 fileFilter: (req, file, cb) => {58 const ext = path.extname(file.originalname).toLowerCase();59 if (!ALLOWED_HERO_EXT.has(ext)) {60 return cb(new Error('Hero-afbeelding moet jpg/png/webp/gif zijn'));61 }62 cb(null, true);63 },64 });65 19 66 20 router.get('/', requireGod, (req, res) => { … … 73 27 hubIntro: getSetting('hub_intro') || '', 74 28 hubHeroImage: getSetting('hub_hero_image') || '', 75 hubHeroOverlay: clampOverlay(getSetting('hub_hero_overlay')),76 29 success: req.query.success || null, 77 error: req.query.error || null,78 30 }); 79 31 }); 80 32 81 33 router.post('/', requireGod, (req, res) => { 82 // multer.single verwerkt multipart (hub-branding form). Bij een gewone 83 // urlencoded POST (tenancy-form) doet multer niets en blijft req.body intact. 84 heroUpload.single('hub_hero_file')(req, res, (err) => { 85 if (err) { 86 return res.redirect('/admin/settings?error=' + encodeURIComponent(err.message)); 87 } 88 89 if (typeof req.body.tenancy !== 'undefined') { 90 setTenancy(req.body.tenancy === 'hub' ? 'hub' : 'solo'); 91 } 92 if (typeof req.body.hub_title !== 'undefined') { 93 setSetting('hub_title', (req.body.hub_title || '').toString().slice(0, 80).trim()); 94 } 95 if (typeof req.body.hub_tagline !== 'undefined') { 96 setSetting('hub_tagline', (req.body.hub_tagline || '').toString().slice(0, 120).trim()); 97 } 98 if (typeof req.body.hub_intro !== 'undefined') { 99 setSetting('hub_intro', (req.body.hub_intro || '').toString().slice(0, 400).trim()); 100 } 101 102 // Hero: een geüploade afbeelding wint; anders het URL-tekstveld. 103 if (req.file) { 104 const newUrl = `/media/hero/${req.file.filename}`; 105 // Ruim een vorige geüploade hero op (alleen als die uit onze hero-map kwam). 106 const old = getSetting('hub_hero_image') || ''; 107 if (old.startsWith('/media/hero/')) { 108 try { fs.unlinkSync(path.join(HERO_DIR, path.basename(old))); } catch {} 109 } 110 setSetting('hub_hero_image', newUrl); 111 } else if (typeof req.body.hub_hero_image !== 'undefined') { 112 setSetting('hub_hero_image', (req.body.hub_hero_image || '').toString().slice(0, 300).trim()); 113 } 114 115 if (typeof req.body.hub_hero_overlay !== 'undefined') { 116 setSetting('hub_hero_overlay', String(clampOverlay(req.body.hub_hero_overlay))); 117 } 118 119 res.redirect('/admin/settings?success=' + encodeURIComponent('Opgeslagen')); 120 }); 34 if (typeof req.body.tenancy !== 'undefined') { 35 setTenancy(req.body.tenancy); // valideert naar solo | hub | circle 36 } 37 if (typeof req.body.hub_title !== 'undefined') { 38 setSetting('hub_title', (req.body.hub_title || '').toString().slice(0, 80).trim()); 39 } 40 if (typeof req.body.hub_tagline !== 'undefined') { 41 setSetting('hub_tagline', (req.body.hub_tagline || '').toString().slice(0, 120).trim()); 42 } 43 if (typeof req.body.hub_intro !== 'undefined') { 44 setSetting('hub_intro', (req.body.hub_intro || '').toString().slice(0, 400).trim()); 45 } 46 if (typeof req.body.hub_hero_image !== 'undefined') { 47 setSetting('hub_hero_image', (req.body.hub_hero_image || '').toString().slice(0, 300).trim()); 48 } 49 res.redirect('/admin/settings?success=' + encodeURIComponent('Opgeslagen')); 121 50 }); 122 51
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)