60 lines
1.8 KiB
JavaScript
60 lines
1.8 KiB
JavaScript
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}`)
|
|
})
|