import express, { type NextFunction, type Request, type Response } from 'express'; import cors from 'cors'; import notesRouter from './routes/notes.routes.js'; import ingestRouter from './routes/ingest.routes.js'; import webhooksRouter from './routes/webhooks.routes.js'; import slackRouter from './routes/slack.routes.js'; const app = express(); app.use(cors()); // Order matters: the raw parser must claim webhook bodies before express.json // consumes them, because signature verification needs the exact bytes sent. // Moving this below express.json silently breaks every signature check. app.use('/v1/webhooks', express.raw({ type: '*/*', limit: '2mb' }), webhooksRouter); // Slash commands are form-encoded and signed over the same raw bytes, so this // router sits above express.json for the reason described above. app.use('/v1/slack', express.raw({ type: '*/*', limit: '2mb' }), slackRouter); app.use(express.json({ limit: '2mb' })); app.use('/v1/notes', ingestRouter); app.use('/v1/notes', notesRouter); app.use((req: Request, res: Response) => { res.status(404).json({ error: `Requested path is invalid or does not exist: ${req.method} ${req.originalUrl}` }); }); app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => { console.error(`Unhandled error: ${err.stack ?? err.message}`); if (res.headersSent) return; res.status(500).json({ error: 'Internal server error' }); }); export default app;