const express = require('express'); const path = require('path'); const fs = require('fs'); require('./db'); const app = express(); app.use(express.json()); // ── Security Headers ────────────────────────────────────────────────────────── app.use((_req, res, next) => { res.setHeader('Content-Security-Policy', [ "default-src 'self'", "script-src 'self'", // unsafe-inline nötig für React inline-styles + @import Google Fonts "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com", "font-src 'self' https://fonts.gstatic.com", // iCal-Feeds werden serverseitig geprosxt → nur 'self' nötig "connect-src 'self'", // data: für Avatare (base64), google.com für Favicons im QuickLinks-Widget "img-src 'self' data: https://www.google.com", "worker-src 'self'", "object-src 'none'", "base-uri 'self'", "form-action 'self'", ].join('; ')); res.setHeader('X-Content-Type-Options', 'nosniff'); res.setHeader('X-Frame-Options', 'DENY'); res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin'); next(); }); // ── API ─────────────────────────────────────────────────────────────────────── app.use('/api/auth', require('./routes/auth')); app.use('/api/admin', require('./routes/admin')); app.use('/api/calendar', require('./routes/calendar')); app.use('/api/dashboard', require('./routes/dashboard')); app.use('/api/system', require('./routes/system')); // Tool-Routen automatisch laden const toolsDir = path.join(__dirname, 'tools'); fs.readdirSync(toolsDir, { withFileTypes: true }) .filter(d => d.isDirectory()) .forEach(d => { const routeFile = path.join(toolsDir, d.name, 'routes.js'); if (fs.existsSync(routeFile)) { app.use(`/api/tools/${d.name}`, require(routeFile)); console.log(` 🔧 Tool geladen: ${d.name}`); } }); // ── Frontend ────────────────────────────────────────────────────────────────── const PUBLIC = path.join(__dirname, '../public'); app.get('/manifest.json', (_req, res) => { res.setHeader('Content-Type', 'application/manifest+json'); res.setHeader('Cache-Control', 'no-cache'); res.sendFile(path.join(PUBLIC, 'manifest.json')); }); app.get('/sw.js', (_req, res) => { res.setHeader('Content-Type', 'application/javascript'); res.setHeader('Service-Worker-Allowed', '/'); res.setHeader('Cache-Control', 'no-cache'); res.sendFile(path.join(PUBLIC, 'sw.js')); }); app.use(express.static(PUBLIC)); app.get('*', (_req, res) => res.sendFile(path.join(PUBLIC, 'index.html'))); app.listen(4000, () => console.log('🚀 Dicken Dock läuft auf Port 4000'));