diff --git a/services/mam-api/src/index.js b/services/mam-api/src/index.js index 0586689..4092aaf 100644 --- a/services/mam-api/src/index.js +++ b/services/mam-api/src/index.js @@ -1,75 +1,83 @@ import 'dotenv/config'; import express from 'express'; -import cors from 'cors'; +import cors from 'cors'; import session from 'express-session'; import ConnectPgSimple from 'connect-pg-simple'; -import pool from './db/pool.js'; +import pool from './db/pool.js'; import { errorHandler } from './middleware/errors.js'; +import { requireAuth } from './middleware/auth.js'; // Routes -import authRouter from './routes/auth.js'; -import assetsRouter from './routes/assets.js'; -import projectsRouter from './routes/projects.js'; -import binsRouter from './routes/bins.js'; -import jobsRouter from './routes/jobs.js'; -import captureRouter from './routes/capture.js'; -import uploadRouter from './routes/upload.js'; +import authRouter from './routes/auth.js'; +import assetsRouter from './routes/assets.js'; +import projectsRouter from './routes/projects.js'; +import binsRouter from './routes/bins.js'; +import jobsRouter from './routes/jobs.js'; +import captureRouter from './routes/capture.js'; +import uploadRouter from './routes/upload.js'; import recordersRouter from './routes/recorders.js'; -import settingsRouter from './routes/settings.js'; -import amppRouter from './routes/ampp.js'; +import settingsRouter from './routes/settings.js'; +import amppRouter from './routes/ampp.js'; +import usersRouter from './routes/users.js'; +import groupsRouter from './routes/groups.js'; +import tokensRouter from './routes/tokens.js'; const app = express(); const PORT = process.env.PORT || 3000; -// ── Middleware ──────────────────────────────────────────────────────────────── +// ── Middleware ───────────────────────────────────────────────────────────────── app.use(cors({ origin: true, credentials: true })); app.use(express.json({ limit: '50mb' })); const PgSession = ConnectPgSimple(session); - -app.use( - session({ - store: new PgSession({ - pool, - tableName: 'sessions', - // Prune expired sessions every hour - pruneSessionInterval: 3600, - }), - secret: process.env.SESSION_SECRET || 'change-me-in-production', - resave: false, - saveUninitialized: false, - cookie: { - secure: process.env.NODE_ENV === 'production', - httpOnly: true, - maxAge: 1000 * 60 * 60 * 24, // 24 h - }, - }) -); +app.use(session({ + store: new PgSession({ + pool, + tableName: 'sessions', + pruneSessionInterval: 3600, + }), + secret: process.env.SESSION_SECRET || 'change-me-in-production', + resave: false, + saveUninitialized: false, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 1000 * 60 * 60 * 24, // 24 h + }, +})); // ── Health (no auth) ────────────────────────────────────────────────────────── app.get('/health', (_req, res) => res.json({ status: 'ok' })); -// ── API Routes ──────────────────────────────────────────────────────────────── -// Auth routes are always open (login/logout don't require a session) -app.use('/api/v1/auth', authRouter); +// ── Auth routes (always open) ───────────────────────────────────────────────── +app.use('/api/v1/auth', authRouter); -// All other routes are gated by requireAuth (no-op unless AUTH_ENABLED=true) -app.use('/api/v1/assets', assetsRouter); -app.use('/api/v1/projects', projectsRouter); -app.use('/api/v1/bins', binsRouter); -app.use('/api/v1/jobs', jobsRouter); -app.use('/api/v1/capture', captureRouter); -app.use('/api/v1/upload', uploadRouter); -app.use('/api/v1/recorders', recordersRouter); -app.use('/api/v1/settings', settingsRouter); -app.use('/api/v1/ampp', amppRouter); +// ── Protected routes (requireAuth is a no-op unless AUTH_ENABLED=true) ──────── +app.use('/api/v1/assets', requireAuth, assetsRouter); +app.use('/api/v1/projects', requireAuth, projectsRouter); +app.use('/api/v1/bins', requireAuth, binsRouter); +app.use('/api/v1/jobs', requireAuth, jobsRouter); +app.use('/api/v1/capture', requireAuth, captureRouter); +app.use('/api/v1/upload', requireAuth, uploadRouter); +app.use('/api/v1/recorders', requireAuth, recordersRouter); +app.use('/api/v1/settings', requireAuth, settingsRouter); +app.use('/api/v1/ampp', requireAuth, amppRouter); + +// ── Admin routes (requireAuth + requireAdmin applied inside each router) ─────── +app.use('/api/v1/users', usersRouter); +app.use('/api/v1/groups', groupsRouter); + +// ── Personal token management ───────────────────────────────────────────────── +app.use('/api/v1/tokens', requireAuth, tokensRouter); // ── Error handler ───────────────────────────────────────────────────────────── app.use(errorHandler); -// ── Start ──────────────────────────────────────────────────────────────────── +// ── Start ───────────────────────────────────────────────────────────────────── app.listen(PORT, () => { - const authMode = process.env.AUTH_ENABLED === 'true' ? 'ENABLED' : 'DISABLED (set AUTH_ENABLED=true for production)'; + const authMode = process.env.AUTH_ENABLED === 'true' + ? 'ENABLED' + : 'DISABLED (set AUTH_ENABLED=true to require login)'; console.log(`MAM API listening on port ${PORT}`); console.log(`Authentication: ${authMode}`); });