Closer/server/src/index.ts

58 lines
1.5 KiB
TypeScript
Raw Normal View History

import 'dotenv/config'
import express from 'express'
import helmet from 'helmet'
import morgan from 'morgan'
import { initFirebase } from './config/firebase'
import { startAnswerListener } from './listeners/answerListener'
import { validateEnv } from './config/env'
import { webhookLimiter, healthLimiter } from './middleware/rateLimiter'
import healthRouter from './routes/health'
import webhooksRouter from './routes/webhooks'
// Validate required env vars before starting
try {
validateEnv()
} catch (err) {
console.error('[startup] Env validation failed:')
console.error(err)
process.exit(1)
}
initFirebase()
const app = express()
const PORT = parseInt(process.env.PORT ?? '8080', 10)
// Capture raw body BEFORE express.json() for webhook signature verification
app.use(express.json({
verify: (req: express.Request, res: express.Response, body: Buffer) => {
(req as any).rawBody = body
},
}))
app.use(helmet())
app.use(morgan('combined'))
// Rate limiting middleware (applied before routes)
app.use('/webhooks', webhookLimiter)
app.use('/health', healthLimiter)
app.use('/health', healthRouter)
app.use('/webhooks', webhooksRouter)
const server = app.listen(PORT, () => {
console.log(`[server] listening on port ${PORT}`)
startAnswerListener()
})
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('[server] SIGTERM received, shutting down')
server.close(() => process.exit(0))
})
process.on('SIGINT', () => {
console.log('[server] SIGINT received, shutting down')
server.close(() => process.exit(0))
})