import 'dotenv/config'; import http from 'node:http'; import express from 'express'; import cors from 'cors'; import helmet from 'helmet'; import morgan from 'morgan'; import rateLimit from 'express-rate-limit'; import { pool } from './config/db.js'; import authRoutes from './routes/auth.js'; import setupRoutes, { isInstalled } from './routes/setup.js'; import workspaceRoutes from './routes/workspaces.js'; import pageRoutes from './routes/pages.js'; import userRoutes from './routes/users.js'; import bookRoutes from './routes/books.js'; import taskRoutes from './routes/tasks.js'; import timeRoutes from './routes/time.js'; import adminRoutes from './routes/admin.js'; import { initSocket } from './socket.js'; const app = express(); const PORT = Number(process.env.PORT || 3000); const CORS_ORIGIN = process.env.CORS_ORIGIN || 'http://localhost:5173'; app.use(helmet()); app.use(morgan('dev')); app.use(express.json({ limit: '2mb' })); app.use( cors({ origin: CORS_ORIGIN.split(',').map((s) => s.trim()), credentials: true, }), ); app.use('/api/setup', rateLimit({ windowMs: 15 * 60 * 1000, max: 20 }), setupRoutes); // First-run guard: until the web installer completes, every API route // except /api/setup/* and /api/health answers 503 { setupRequired: true }. app.use('/api', async (req, res, next) => { if (req.path === '/health' || req.path.startsWith('/setup')) return next(); if (!(await isInstalled())) { return res.status(503).json({ error: 'Initial setup required', setupRequired: true }); } return next(); }); app.use('/api/auth', rateLimit({ windowMs: 15 * 60 * 1000, max: 100 }), authRoutes); app.use('/api/workspaces', workspaceRoutes); app.use('/api/pages', pageRoutes); app.use('/api/users', userRoutes); app.use('/api/books', bookRoutes); app.use('/api/tasks', taskRoutes); app.use('/api/time', timeRoutes); app.use('/api/admin', adminRoutes); app.get('/api/health', async (_req, res) => { try { await pool.query('SELECT 1'); res.json({ ok: true, db: 'up' }); } catch { res.status(503).json({ ok: false, db: 'down' }); } }); app.use((err, _req, res, _next) => { console.error('[unhandled]', err); res.status(500).json({ error: 'Internal server error' }); }); const server = http.createServer(app); const io = initSocket(server, CORS_ORIGIN); app.set('io', io); server.listen(PORT, () => { console.log(`[api] listening on :${PORT} (CORS: ${CORS_ORIGIN})`); });