Server
Express 4 + Socket.IO 4 + Prisma 6 in server/. Entry: server/index.js.
Boot sequence
- Load env (
dotenv) and validate with Zod (src/utils/env.js) - Create Express app —
trust proxyfromTRUST_PROXY_HOPS/ production default - Apply security middleware (helmet, rate limiters, CORS), HTTP logger, JSON + cookies
- Create HTTP server + Socket.IO; call
setupSocketListeners(io, users) - Mount
/apirouters and/health - Attach central
errorHandler initServerConstants, simulation stuck-run cleanuplisten(env.WS_PORT)
Container entrypoint (entrypoint.sh):
npx prisma migrate deploy
exec node index.js
HTTP surface
| Mount | File | Notes |
|---|---|---|
/api/auth/* | routes/authRoutes.js | Register / login / refresh / logout / constants helpers |
/api/participant/* | routes/participantRoutes.js | Survey, user details, foraging, arithmetic |
/api/admin/* | routes/adminRoutes.js | Behind adminLimiter + requireAdminAuth |
/api/experiments… | Inline in index.js | Public experiment listing / current / by id |
POST /api/exit-survey | Inline in index.js | Exit survey submit |
/health | Inline | Liveness |
Controllers live under src/controllers/ (auth, admin, experiment, survey, analytics, arithmetic, export, …).
Middleware (src/middleware/)
| Module | Role |
|---|---|
security.js | Helmet, global limiter, login/admin limiters, CORS options |
authMiddleware.js | requireAdminAuth (JWT) |
httpLogger.js | Request logging |
errorHandler.js | Centralized errors (AppError / Winston) |
Services (src/services/)
| Service | Role |
|---|---|
RoomService.js | Experiment / friends / solo rooms, join/rejoin/leave |
foragingSaveService.js | Persist single- and multiplayer foraging results |
exportService.js / exportJobService.js | Sync + async data export |
empaticaService.js | Empatica-related integration |
simulationService.js | Bot simulation runs |
Supporting persistence helpers: src/data/save*.js. Game logic: Game engine. Realtime: Realtime sockets.
Logging and safety
- Winston logger (
src/utils/logger.js) — prefer overconsole.* - Arithmetic expressions go through
safeArithmeticEval(noeval/new Function) - Prisma via tagged
$executeRawonly (no$executeRawUnsafe)