import express from 'express'; import { createServer } from 'http'; import { Server as SocketIOServer } from 'socket.io'; import cookieParser from 'cookie-parser'; import session from 'express-session'; import cors from 'cors'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import { setupBroadcast } from './broadcast.js'; import { setupRoutes } from './routes.js'; import { setupSEORoutes } from './routes-seo.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const app = express(); const server = createServer(app); // Umgebungsvariablen const NODE_ENV = process.env.NODE_ENV || 'development'; const PORT = process.env.PORT || (NODE_ENV === 'production' ? 4000 : 3300); const IS_PRODUCTION = NODE_ENV === 'production'; // CORS-Origins konfigurieren const allowedOrigins = IS_PRODUCTION ? ['https://ypchat.net', 'https://www.ypchat.net'] : ['http://localhost:5175', 'http://127.0.0.1:5175']; // Socket.IO auf dem gleichen HTTP-Server wie Express const io = new SocketIOServer(server, { cors: { origin: allowedOrigins, credentials: true, methods: ['GET', 'POST'] }, transports: ['websocket', 'polling'], allowEIO3: true }); console.log('Socket.IO Server initialisiert auf Express-Server'); console.log('Umgebung:', NODE_ENV); console.log('CORS erlaubt für:', allowedOrigins); // CORS-Konfiguration app.use(cors({ origin: (origin, callback) => { // Erlaube Requests ohne Origin (z.B. Postman, mobile Apps) if (!origin) return callback(null, true); if (allowedOrigins.includes(origin) || !IS_PRODUCTION) { callback(null, true); } else { callback(new Error('Nicht erlaubt durch CORS')); } }, credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'] })); // Middleware app.use(express.json()); app.use(express.urlencoded({ extended: true })); app.use(cookieParser()); // Session-Konfiguration const sessionSecret = process.env.SESSION_SECRET || 'singlechat-secret-key-change-in-production'; app.use(session({ secret: sessionSecret, resave: false, saveUninitialized: false, cookie: { secure: IS_PRODUCTION, // true für HTTPS in Production httpOnly: true, maxAge: 24 * 60 * 60 * 1000, // 24 Stunden sameSite: IS_PRODUCTION ? 'lax' : false // Lax für HTTPS, false für Development } })); // Trust Proxy für Apache Reverse Proxy (muss vor Routes stehen) if (IS_PRODUCTION) { app.set('trust proxy', 1); // Vertraue dem ersten Proxy (Apache) } // Statische Dateien aus docroot app.use('/static', express.static(join(__dirname, '../docroot'))); // SEO-Routes (robots.txt, sitemap.xml, Pre-Rendering) // Müssen vor anderen Routes stehen, damit sie nicht vom SPA-Fallback abgefangen werden setupSEORoutes(app, __dirname); // API Routes (müssen vor SPA-Fallback stehen) setupRoutes(app, __dirname); // Socket.IO-Handling setupBroadcast(io); // In Production: Serviere auch die gebauten Client-Dateien // SPA-Fallback muss nach allen anderen Routen stehen if (IS_PRODUCTION) { const distPath = join(__dirname, '../docroot/dist'); app.use(express.static(distPath)); // Fallback für Vue Router (SPA) - muss am Ende stehen app.get('*', (req, res) => { // Überspringe SEO-Routes in Production (werden bereits von setupSEORoutes behandelt) if (IS_PRODUCTION && (req.path === '/' || req.path === '/partners')) { return; // Route wurde bereits behandelt } // In Production: /src/ Pfade sollten nicht existieren (404) if (IS_PRODUCTION && req.path.startsWith('/src/')) { res.status(404).send('Not found'); return; } // Nur für nicht-API und nicht-static Requests if (!req.path.startsWith('/api') && !req.path.startsWith('/static') && req.path !== '/robots.txt' && req.path !== '/sitemap.xml') { res.sendFile(join(distPath, 'index.html')); } else { res.status(404).send('Not found'); } }); } // Server starten const HOST = '127.0.0.1'; // Nur localhost, da Apache als Reverse Proxy fungiert server.listen(PORT, HOST, () => { console.log(`Server läuft auf http://${HOST}:${PORT}`); console.log(`Umgebung: ${NODE_ENV}`); console.log(`CORS erlaubt für: ${allowedOrigins.join(', ')}`); if (IS_PRODUCTION) { console.log('Production-Modus: HTTPS über Apache Reverse Proxy'); } });