- Introduced new links in ImprintContainer.vue for FAQ, Rules, and Safety pages. - Added FaqView, RulesView, and SafetyView components to handle the new routes. - Implemented SEO metadata for the new pages in routes-seo.js and router/index.js. - Updated server routes to include the new paths for proper handling in production. These changes enhance the site's informational resources and improve SEO visibility for user inquiries.
179 lines
5.8 KiB
JavaScript
179 lines
5.8 KiB
JavaScript
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';
|
|
const PRIMARY_HOST = 'www.ypchat.net';
|
|
const LEGACY_HOSTS = new Set(['ypchat.net']);
|
|
|
|
// 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,
|
|
maxHttpBufferSize: 10 * 1024 * 1024, // 10MB für große Bilder (Base64-kodiert)
|
|
pingTimeout: 60000,
|
|
pingInterval: 25000
|
|
});
|
|
|
|
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)
|
|
|
|
// SEO-Fallback: erzwinge kanonischen Host + HTTPS, falls der Proxy es nicht bereits tut.
|
|
app.use((req, res, next) => {
|
|
const forwardedHost = String(req.headers['x-forwarded-host'] || '')
|
|
.split(',')[0]
|
|
.trim()
|
|
.toLowerCase();
|
|
const rawHost = String(req.headers.host || '')
|
|
.split(':')[0]
|
|
.trim()
|
|
.toLowerCase();
|
|
const host = forwardedHost || rawHost;
|
|
|
|
const forwardedProto = String(req.headers['x-forwarded-proto'] || '')
|
|
.split(',')[0]
|
|
.trim()
|
|
.toLowerCase();
|
|
const isHttps = forwardedProto ? forwardedProto === 'https' : req.secure;
|
|
const isKnownPublicHost = host === PRIMARY_HOST || LEGACY_HOSTS.has(host);
|
|
|
|
if (!isKnownPublicHost) {
|
|
next();
|
|
return;
|
|
}
|
|
|
|
if (!isHttps || host !== PRIMARY_HOST) {
|
|
res.redirect(301, `https://${PRIMARY_HOST}${req.originalUrl || '/'}`);
|
|
return;
|
|
}
|
|
|
|
next();
|
|
});
|
|
}
|
|
|
|
// 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, __dirname);
|
|
|
|
// 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');
|
|
const KNOWN_SPA_ROUTES = new Set(['/', '/partners', '/feedback', '/faq', '/regeln', '/sicherheit', '/mockup-redesign']);
|
|
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' || req.path === '/feedback' || req.path === '/faq' || req.path === '/regeln' || req.path === '/sicherheit')) {
|
|
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 bekannte SPA-Routen als index.html ausliefern, unbekannte Routen mit 404 beantworten
|
|
if (
|
|
!req.path.startsWith('/api') &&
|
|
!req.path.startsWith('/static') &&
|
|
req.path !== '/robots.txt' &&
|
|
req.path !== '/sitemap.xml' &&
|
|
KNOWN_SPA_ROUTES.has(req.path)
|
|
) {
|
|
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');
|
|
}
|
|
});
|
|
|