A minimal Node.js backend for BroCode Spot backed by a persistent JSON file database.
npm run backendServer starts at http://localhost:4000 by default.
The server now sends strict security headers on every API response, including:
Content-Security-PolicyStrict-Transport-SecurityX-Content-Type-OptionsX-Frame-OptionsReferrer-PolicyCross-Origin-*hardening headers
Configure CSP via SECURITY_HEADERS_CSP.
CORS is applied to all endpoints with these defaults:
- Allowed origin from
CORS_ALLOW_ORIGIN(defaults to*) - Allowed headers:
Content-Type,Authorization - Allowed methods:
GET,POST,DELETE,OPTIONS
Two limits are active:
- Global API limiter per IP (
GLOBAL_RATE_LIMIT_MAX_REQUESTSinGLOBAL_RATE_LIMIT_WINDOW_MS) - Login brute-force limiter per
IP + username(LOGIN_RATE_LIMIT_MAX_ATTEMPTSinLOGIN_RATE_LIMIT_WINDOW_MS, temporary block forLOGIN_RATE_LIMIT_BLOCK_MS)
Both return HTTP 429 and Retry-After headers.
User credentials are stored as salted hashes (using Node crypto scrypt) and never as plaintext.
Legacy plaintext records auto-migrate to hashed values at successful login.
- Uses a local JSON database file at
backend/data/brocode.json. - You can override the location with
BROCODE_DB_PATH=/custom/path.json npm run backend. - On first start, seed data is inserted for users, spots, catalog items, and a sample order.
- New orders are validated against DB data (known
spotId,userId,productId) and item pricing is always derived from catalog prices in the database.
- Added optional Redis integration (
REDIS_URL) with automatic in-memory fallback when Redis is unavailable. - Active auth sessions are now stored in cache (token hash), and protected routes require an active session.
- Added cache-backed rate limiting primitives for login attempts (window + temporary block).
- Added short-lived cache for read-heavy endpoints:
GET /api/catalog(cached)GET /api/spots(cached)
- Added real-time presence endpoints:
POST /api/presence/heartbeatGET /api/presence/active?spotId=...
- Added temporary event state endpoints:
PUT|POST /api/events/state/:eventKeyGET /api/events/state/:eventKey
- New env vars:
REDIS_URLREDIS_KEY_PREFIXCACHE_DEFAULT_TTL_SECONDSPRESENCE_TTL_SECONDSEVENT_STATE_DEFAULT_TTL_SECONDS
See backend/deployment.md for step-by-step deployment options and env setup for:
- Backend hosting: Render, Railway, AWS EC2
- PostgreSQL: Supabase or Neon
- Redis: Upstash
- File storage: AWS S3 or Cloudinary
- The backend initializes BullMQ queues for:
- email notification jobs (
email-notifications) when a new order is created. - scheduled reminder jobs (
spot-reminders) for upcoming spots/events. - recurring cleanup jobs (
expired-spot-cleanup) for expired events.
- email notification jobs (
- Redis connection settings:
REDIS_HOST(default127.0.0.1)REDIS_PORT(default6379)REDIS_PASSWORD(optional)
- Reminder timing:
EVENT_REMINDER_BEFORE_HOURS(default2)
- If BullMQ or Redis dependencies are unavailable, backend continues to run with jobs disabled and logs a warning.
- OpenAPI JSON is available at
GET /api/docs/openapi.json. - Swagger UI is available at
GET /api/docs.
GET /api/healthPOST /api/auth/loginGET /api/docsGET /api/docs/openapi.jsonGET /api/catalogPOST /api/auth/logoutGET /api/catalog(cached)GET /api/catalog/:category(drinks,food,cigarettes)GET /api/spots(cached)GET /api/orders?spotId=...&userId=...(auth required)GET /api/orders/:id(auth required)POST /api/orders(auth required)GET /api/bills/:spotId(admin only)DELETE /api/users/:userId(admin only; removes the user and all related records)POST /api/jobs/reminders/run(admin only; manually queue reminder jobs)POST /api/jobs/cleanup/run(admin only; manually queue expired-event cleanup)POST /api/presence/heartbeat(auth required)GET /api/presence/active?spotId=...(auth required)PUT|POST /api/events/state/:eventKey(auth required)GET /api/events/state/:eventKey(auth required)
{
"username": "brocode",
"password": "changeme"
}