| 254 | } |
| 255 | |
| 256 | async function webhookHandler(event: HandlerEvent<"HTTP">, logger: Logger, integration: Linear) { |
| 257 | logger.debug("[@trigger.dev/linear] Handling webhook payload"); |
| 258 | |
| 259 | const { rawEvent: request, source } = event; |
| 260 | |
| 261 | const LINEAR_IPS = ["35.231.147.226", "35.243.134.228"]; |
| 262 | |
| 263 | const clientIp = |
| 264 | request.headers.get("cf-connecting-ip") ?? |
| 265 | ( |
| 266 | request.headers.get("x-real-ip") ?? |
| 267 | request.headers.get("x-forwarded-for") ?? |
| 268 | // default to allowing request if expected headers missing |
| 269 | LINEAR_IPS[0] |
| 270 | ).split(",")[0]; |
| 271 | |
| 272 | if (!LINEAR_IPS.includes(clientIp)) { |
| 273 | logger.error("[@trigger.dev/linear] Error validating webhook source, IP invalid."); |
| 274 | throw Error("[@trigger.dev/linear] Invalid source IP."); |
| 275 | } |
| 276 | |
| 277 | const payloadUuid = request.headers.get("Linear-Delivery"); |
| 278 | const payloadEvent = request.headers.get("Linear-Event"); |
| 279 | |
| 280 | if (!payloadUuid || !payloadEvent) { |
| 281 | logger.debug("[@trigger.dev/linear] Missing required Linear headers"); |
| 282 | return { events: [] }; |
| 283 | } |
| 284 | |
| 285 | if (!request.body) { |
| 286 | logger.debug("[@trigger.dev/linear] No body found"); |
| 287 | return { events: [] }; |
| 288 | } |
| 289 | |
| 290 | const signature = request.headers.get(LINEAR_WEBHOOK_SIGNATURE_HEADER); |
| 291 | |
| 292 | if (!signature) { |
| 293 | logger.error("[@trigger.dev/linear] Error validating webhook signature, no signature found"); |
| 294 | throw Error("[@trigger.dev/linear] No signature found"); |
| 295 | } |
| 296 | |
| 297 | const rawBody = await request.text(); |
| 298 | const body = JSON.parse(rawBody); |
| 299 | const webhookHelper = new LinearWebhooks(source.secret); |
| 300 | |
| 301 | if (!webhookHelper.verify(Buffer.from(rawBody), signature, body[LINEAR_WEBHOOK_TS_FIELD])) { |
| 302 | logger.error("[@trigger.dev/linear] Error validating webhook signature, they don't match"); |
| 303 | throw Error("[@trigger.dev/linear] Invalid signature"); |
| 304 | } |
| 305 | |
| 306 | const webhookPayload = WebhookPayloadSchema.parse(body); |
| 307 | |
| 308 | return { |
| 309 | events: [ |
| 310 | { |
| 311 | id: payloadUuid, |
| 312 | name: payloadEvent, |
| 313 | source: "linear.app", |