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(`