diff --git a/packages/backend/src/app.ts b/packages/backend/src/app.ts index 4cca60e..733e79e 100644 --- a/packages/backend/src/app.ts +++ b/packages/backend/src/app.ts @@ -1,24 +1,41 @@ +import express, { type Express, type NextFunction, type Request, type Response } from 'express'; +import cookieParser from 'cookie-parser'; import { existsSync } from 'node:fs'; import { resolve } from 'node:path'; -import express, { type Express, type NextFunction, type Request, type Response } from 'express'; import { ZodError } from 'zod'; import type { Db } from './db/client.js'; import { ApiError } from './lib/errors.js'; -import { cardsRouter } from './routes/cards.js'; +import { currentUserOrNull, requireAuth, requireRole } from './middleware/auth.js'; +import { ensureCsrfToken, verifyCsrf } from './middleware/csrf.js'; +import { authRouter } from './routes/auth.js'; +import { adminUsersRouter } from './routes/admin-users.js'; import { lessonsRouter } from './routes/lessons.js'; +import { cardsRouter } from './routes/cards.js'; import { sessionsRouter } from './routes/sessions.js'; import { statsRouter } from './routes/stats.js'; export function createApp(db: Db): Express { const app = express(); + app.set('trust proxy', 1); + app.use(cookieParser()); app.use(express.json({ limit: '5mb' })); + app.use(ensureCsrfToken); + app.use(currentUserOrNull(db)); app.get('/api/health', (_req, res) => res.json({ ok: true })); - app.use('/api/lessons', lessonsRouter(db)); - app.use('/api', cardsRouter(db)); - app.use('/api/sessions', sessionsRouter(db)); - app.use('/api/stats', statsRouter(db)); + // Public auth endpoints (logout/profile/change-password have their own requireAuth + verifyCsrf + // applied inside the router; public ones bootstrap cookies so CSRF cannot apply yet). + app.use('/api/auth', authRouter(db)); + + // Protected app routes + app.use('/api/lessons', requireAuth, verifyCsrf, lessonsRouter(db)); + app.use('/api', requireAuth, verifyCsrf, cardsRouter(db)); + app.use('/api/sessions', requireAuth, verifyCsrf, sessionsRouter(db)); + app.use('/api/stats', requireAuth, statsRouter(db)); + app.use('/api/admin/users', requireAuth, requireRole('sysadmin'), verifyCsrf, adminUsersRouter(db)); + + // Static frontend in production const frontendDist = resolve(import.meta.dirname, '../../frontend/dist'); if (existsSync(frontendDist)) { app.use(express.static(frontendDist));