import 'dotenv/config'; import express from 'express'; import cookieParser from 'cookie-parser'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import authRoutes from './routes/auth.js'; import apiRoutes from './routes/api.js'; import adminRoutes from './routes/admin.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); const app = express(); const PORT = process.env.PORT || 33033; // nginx terminates HTTPS in front of this app; trust its X-Forwarded-* headers // so secure cookies and req.protocol behave correctly. app.set('trust proxy', 1); app.use(express.json({ limit: '1mb' })); app.use(cookieParser()); // Static assets + uploaded ghost images. app.use(express.static(join(__dirname, 'public'))); app.use('/uploads', express.static(join(__dirname, process.env.UPLOAD_DIR || 'uploads'))); // API app.use('/auth', authRoutes); app.use('/api', apiRoutes); app.use('/api/admin', adminRoutes); app.get('/healthz', (_req, res) => res.json({ ok: true })); // Admin SPA entry (auth handled client-side + enforced by API). app.get('/admin', (_req, res) => { res.sendFile(join(__dirname, 'public', 'admin.html')); }); // JSON error handler (multer + thrown errors). app.use((err, _req, res, _next) => { console.error(err); res.status(err.status || 500).json({ error: err.message || 'server error' }); }); // Only start listening when run directly (`node server.js`), not when imported for tests. if (import.meta.url === `file://${process.argv[1]}`) { app.listen(PORT, () => { console.log(`Newbury Nights listening on http://127.0.0.1:${PORT}`); }); } export default app;