Files
Papo/src/web/server.ts
Pepe44DEV 0d3f0c5ae4
Some checks failed
Deploy Discord Bot / deploy (push) Has been cancelled
feat: SPA build + serving im Dockerfile und server.ts
2026-07-01 04:14:27 +02:00

102 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import express from 'express';
import session from 'express-session';
import cookieParser from 'cookie-parser';
import path from 'path';
import fs from 'fs';
import authRouter from './routes/auth';
import dashboardRouter from './routes/dashboard';
import apiRouter from './routes/api';
import { env } from '../config/env';
export function createWebServer() {
const app = express();
const basePath = env.webBasePath || '/ucp';
const dashboardPath = `${basePath}/dashboard`;
app.use(express.json({ limit: '5mb' }));
app.use(cookieParser());
app.use(
session({
secret: env.sessionSecret,
resave: false,
saveUninitialized: false
})
);
const mount = (suffix: string) => (basePath ? `${basePath}${suffix}` : suffix);
app.use(mount('/auth'), authRouter);
app.use(mount('/api'), apiRouter);
// fallback mounts if proxy strips base path
if (basePath) {
app.use('/api', apiRouter);
}
// Redirect bare auth calls to the prefixed path when a base path is set
if (basePath) {
app.use('/auth', (_req, res) => res.redirect(`${basePath}${_req.originalUrl}`));
}
// Serve React SPA static assets
const frontendDist = path.join(process.cwd(), 'frontend', 'dist');
// If SPA exists, it takes precedence for GET dashboard routes
if (fs.existsSync(path.join(frontendDist, 'index.html'))) {
const spaHtml = fs.readFileSync(path.join(frontendDist, 'index.html'), 'utf-8');
const configScript = `window.__PAPO__ = ${JSON.stringify({
baseRoot: basePath,
baseApi: mount('/api'),
baseAuth: mount('/auth'),
baseDashboard: dashboardPath
})}`;
app.use(basePath || '/', express.static(frontendDist));
app.get(`${dashboardPath}(/*)?`, (_req, res) => {
res.type('html').send(spaHtml.replace('__PAPO_CONFIG__', configScript));
});
app.get(mount('/'), (_req, res) => {
res.redirect(dashboardPath);
});
} else {
// Legacy landing page when SPA is not built
app.get(mount('/'), (_req, res) => {
res.send(`
<!doctype html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Papo Dashboard</title>
<style>
:root { --bg:#0b0f17; --card:rgba(18,20,30,0.72); --text:#f8fafc; --muted:#a5b4c3; --accent:#f97316; --border:rgba(255,255,255,0.06); }
body { margin:0; min-height:100vh; display:flex; align-items:center; justify-content:center; background:radial-gradient(circle at 18% 20%, rgba(249,115,22,0.16), transparent 32%), radial-gradient(circle at 82% -8%, rgba(255,166,99,0.12), transparent 28%), linear-gradient(140deg, #080c15 0%, #0c1220 48%, #080c15 100%); font-family:'Inter', system-ui, sans-serif; color:var(--text); }
.shell { padding:32px 36px; border-radius:18px; background:var(--card); border:1px solid var(--border); box-shadow:0 20px 50px rgba(0,0,0,0.45); backdrop-filter:blur(12px); max-width:520px; width:calc(100% - 32px); text-align:center; }
h1 { margin:0 0 10px; font-size:28px; letter-spacing:0.4px; }
p { margin:0 0 18px; color:var(--muted); }
a { display:inline-flex; align-items:center; gap:10px; padding:12px 18px; border-radius:14px; text-decoration:none; font-weight:800; color:white; background:linear-gradient(130deg, #ff9b3d, #f97316); border:1px solid rgba(249,115,22,0.45); box-shadow:0 14px 34px rgba(249,115,22,0.35); transition:transform 140ms ease, box-shadow 140ms ease; }
a:hover { transform:translateY(-1px); box-shadow:0 16px 40px rgba(249,115,22,0.4); }
</style>
</head>
<body>
<div class="shell">
<h1>Papo Dashboard</h1>
<p>Verwalte Tickets, Module und Automod.</p>
<a href="${dashboardPath}/">Zum Dashboard</a>
</div>
</body>
</html>
`);
});
}
// Old dashboard router only handles POST/non-GET routes when SPA is active,
// or all routes when SPA is inactive
app.use(dashboardPath, dashboardRouter);
if (basePath) {
app.use('/dashboard', dashboardRouter);
}
app.use(mount('/static'), express.static(path.join(process.cwd(), 'static')));
return app;
}