import express from 'express'
import crypto from 'crypto'
const app = express()
const SECRET = process.env.SF_WEBHOOK_SECRET!
const seen = new Set<string>() // replace with Redis in prod
app.post('/sf-hook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.header('x-sf-signature') ?? ''
const deliveryId = req.header('x-sf-delivery-id') ?? ''
if (!verify(SECRET, sig, req.body)) return res.status(401).send('bad sig')
if (seen.has(deliveryId)) return res.status(200).send('ok (dedupe)')
seen.add(deliveryId)
const event = JSON.parse(req.body.toString('utf8'))
console.log(event.type, event.data)
res.status(200).send('ok')
})
function verify(secret: string, sigHeader: string, body: Buffer): boolean {
const [tPart, v1Part] = sigHeader.split(',')
const t = tPart?.split('=')[1]
const v1 = v1Part?.split('=')[1]
if (!t || !v1) return false
const expected = crypto.createHmac('sha256', secret).update(`${t}.${body.toString()}`).digest('hex')
if (Math.abs(Date.now()/1000 - Number(t)) > 300) return false
return crypto.timingSafeEqual(Buffer.from(v1, 'hex'), Buffer.from(expected, 'hex'))
}