import express from 'express' import cors from 'cors' import helmet from 'helmet' import 'dotenv/config' import { auditRouter } from './routes/audit.js' import { env } from './services/env.js' const app = express() // Behind nginx, so trust exactly one proxy hop. Without this the rate limiter // would see the proxy IP for everyone and throttle all visitors together. app.set('trust proxy', 1) app.use(helmet()) // An allowlist, never '*'. ALLOWED_ORIGINS is comma separated. const allowedOrigins = (env('ALLOWED_ORIGINS', '') ?? '') .split(',') .map((origin) => origin.trim().replace(/\/$/, '')) .filter(Boolean) // An empty allowlist refuses every browser request, which looks exactly like // a broken endpoint. Say so at startup instead of at 3am. if (allowedOrigins.length === 0) { console.warn( 'WARNING: ALLOWED_ORIGINS is empty - all cross-origin requests will be refused.' ) } app.use( cors({ origin(origin, callback) { // Same-origin and curl send no Origin header at all. if (!origin) return callback(null, true) if (allowedOrigins.includes(origin)) return callback(null, true) callback(new Error('Origin not allowed')) }, methods: ['POST'], allowedHeaders: ['Content-Type'], }) ) // The form payload is a few hundred bytes. Anything near this cap is abuse. app.use(express.json({ limit: '10kb' })) app.get('/health', (_req, res) => res.json({ status: 'ok' })) app.use('/api/audit-request', auditRouter) // Keep internals out of the response body. app.use((error, _req, res, _next) => { console.error('Unhandled error:', error.message) res.status(500).json({ success: false, message: 'Internal server error.' }) }) const port = Number(env('PORT', '3001')) app.listen(port, '127.0.0.1', () => { console.log(`Audit endpoint listening on 127.0.0.1:${port}`) })