- Updated CORS middleware to explicitly handle OPTIONS requests, ensuring proper preflight response and improving API request handling.
135 lines
4.9 KiB
JavaScript
135 lines
4.9 KiB
JavaScript
import express from 'express';
|
||
import path from 'path';
|
||
import { fileURLToPath } from 'url';
|
||
import crypto from 'crypto';
|
||
import chatRouter from './routers/chatRouter.js';
|
||
import authRouter from './routers/authRouter.js';
|
||
import navigationRouter from './routers/navigationRouter.js';
|
||
import settingsRouter from './routers/settingsRouter.js';
|
||
import adminRouter from './routers/adminRouter.js';
|
||
import contactRouter from './routers/contactRouter.js';
|
||
import socialnetworkRouter from './routers/socialnetworkRouter.js';
|
||
import forumRouter from './routers/forumRouter.js';
|
||
import falukantRouter from './routers/falukantRouter.js';
|
||
import friendshipRouter from './routers/friendshipRouter.js';
|
||
import modelsProxyRouter from './routers/modelsProxyRouter.js';
|
||
import blogRouter from './routers/blogRouter.js';
|
||
import match3Router from './routers/match3Router.js';
|
||
import taxiRouter from './routers/taxiRouter.js';
|
||
import taxiMapRouter from './routers/taxiMapRouter.js';
|
||
import taxiHighscoreRouter from './routers/taxiHighscoreRouter.js';
|
||
import termineRouter from './routers/termineRouter.js';
|
||
import vocabRouter from './routers/vocabRouter.js';
|
||
import dashboardRouter from './routers/dashboardRouter.js';
|
||
import newsRouter from './routers/newsRouter.js';
|
||
import calendarRouter from './routers/calendarRouter.js';
|
||
import cors from 'cors';
|
||
import './jobs/sessionCleanup.js';
|
||
|
||
const __filename = fileURLToPath(import.meta.url);
|
||
const __dirname = path.dirname(__filename);
|
||
|
||
const app = express();
|
||
|
||
// Request-Timing (aktivierbar per ENV)
|
||
// - LOG_SLOW_REQ_MS=200: Logge Requests, die länger dauern als X ms (Default 500)
|
||
// - LOG_ALL_REQ=1: Logge alle Requests
|
||
const LOG_ALL_REQ = process.env.LOG_ALL_REQ === '1';
|
||
const LOG_SLOW_REQ_MS = Number.parseInt(process.env.LOG_SLOW_REQ_MS || '500', 10);
|
||
const defaultCorsOrigins = [
|
||
'http://localhost:3000',
|
||
'http://localhost:5173',
|
||
'http://127.0.0.1:3000',
|
||
'http://127.0.0.1:5173'
|
||
];
|
||
const corsOrigins = (process.env.CORS_ORIGINS || process.env.FRONTEND_URL || '')
|
||
.split(',')
|
||
.map((origin) => origin.trim())
|
||
.filter(Boolean);
|
||
const effectiveCorsOrigins = corsOrigins.length > 0 ? corsOrigins : defaultCorsOrigins;
|
||
const corsAllowAll = process.env.CORS_ALLOW_ALL === '1';
|
||
|
||
app.use((req, res, next) => {
|
||
const reqId = req.headers['x-request-id'] || (crypto.randomUUID ? crypto.randomUUID() : crypto.randomBytes(8).toString('hex'));
|
||
req.reqId = reqId;
|
||
res.setHeader('x-request-id', reqId);
|
||
const t0 = Date.now();
|
||
res.on('finish', () => {
|
||
const ms = Date.now() - t0;
|
||
if (LOG_ALL_REQ || ms >= LOG_SLOW_REQ_MS) {
|
||
console.log(`⏱️ REQ ${ms}ms ${res.statusCode} ${req.method} ${req.originalUrl} rid=${reqId}`);
|
||
}
|
||
});
|
||
next();
|
||
});
|
||
|
||
const corsOptions = {
|
||
origin(origin, callback) {
|
||
if (!origin) {
|
||
return callback(null, true);
|
||
}
|
||
|
||
if (corsAllowAll || effectiveCorsOrigins.includes(origin)) {
|
||
return callback(null, true);
|
||
}
|
||
|
||
return callback(null, false);
|
||
},
|
||
methods: ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE'],
|
||
allowedHeaders: ['Content-Type', 'Authorization', 'userid', 'authcode', 'userId', 'authCode'],
|
||
credentials: true,
|
||
preflightContinue: false,
|
||
optionsSuccessStatus: 204
|
||
};
|
||
|
||
app.use(cors(corsOptions));
|
||
app.use((req, res, next) => {
|
||
if (req.method === 'OPTIONS') {
|
||
return cors(corsOptions)(req, res, next);
|
||
}
|
||
return next();
|
||
});
|
||
app.use(express.json()); // To handle JSON request bodies
|
||
|
||
app.use('/api/chat', chatRouter);
|
||
app.use('/api/auth', authRouter);
|
||
app.use('/api/navigation', navigationRouter);
|
||
app.use('/api/settings', settingsRouter);
|
||
app.use('/api/admin', adminRouter);
|
||
app.use('/api/match3', match3Router);
|
||
app.use('/api/taxi', taxiRouter);
|
||
app.use('/api/taxi-maps', taxiMapRouter);
|
||
app.use('/api/taxi/highscores', taxiHighscoreRouter);
|
||
app.use('/images', express.static(path.join(__dirname, '../frontend/public/images')));
|
||
app.use('/api/contact', contactRouter);
|
||
app.use('/api/socialnetwork', socialnetworkRouter);
|
||
app.use('/api/vocab', vocabRouter);
|
||
app.use('/api/forum', forumRouter);
|
||
app.use('/api/falukant', falukantRouter);
|
||
app.use('/api/friendships', friendshipRouter);
|
||
app.use('/api/models', modelsProxyRouter);
|
||
app.use('/api/blog', blogRouter);
|
||
app.use('/api/termine', termineRouter);
|
||
app.use('/api/dashboard', dashboardRouter);
|
||
app.use('/api/news', newsRouter);
|
||
app.use('/api/calendar', calendarRouter);
|
||
|
||
// Serve frontend SPA for non-API routes to support history mode clean URLs
|
||
// /models/* nicht statisch ausliefern – nur über /api/models (Proxy mit Komprimierung)
|
||
const frontendDir = path.join(__dirname, '../frontend');
|
||
app.use((req, res, next) => {
|
||
if (req.path.startsWith('/models/')) {
|
||
return res.status(404).send('Use /api/models/ for 3D models (optimized).');
|
||
}
|
||
next();
|
||
});
|
||
app.use(express.static(path.join(frontendDir, 'dist')));
|
||
app.get(/^\/(?!api\/).*/, (req, res) => {
|
||
res.sendFile(path.join(frontendDir, 'dist', 'index.html'));
|
||
});
|
||
|
||
// Fallback 404 for unknown API routes
|
||
app.use('/api/*', (req, res) => res.status(404).send('404 Not Found'));
|
||
|
||
export default app;
|