71 lines
3.0 KiB
JavaScript
71 lines
3.0 KiB
JavaScript
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'));
|