UNPKG

fastify-line

Version:

Fastify plugin for the LINE Messaging API

74 lines (73 loc) 2.7 kB
import { LINE_SIGNATURE_HTTP_HEADER_NAME, messagingApi, validateSignature } from '@line/bot-sdk'; import fp from 'fastify-plugin'; import getRawBody from 'raw-body'; import { InvalidSignatureError, MissingSignatureError } from './error.js'; const plugin = (fastify, opts, done) => { if (fastify.hasDecorator('line')) { done(new Error('fastify-line plugin has already been registered')); return; } const { channelSecret, channelAccessToken, skipVerify = false } = opts; if (!channelSecret) { done(new Error('"channelSecret" option is required')); return; } if (!channelAccessToken) { done(new Error('"channelAccessToken" option is required')); return; } const { MessagingApiClient } = messagingApi; const client = new MessagingApiClient({ channelAccessToken }); fastify.decorate('line', client); fastify.addHook('onRoute', (routeOptions) => { const skip = routeOptions.method !== 'POST' || !routeOptions.config?.lineWebhook; if (skip) return; const existingPreParsing = routeOptions.preParsing || []; routeOptions.preParsing = [ parseRawBody, ...(Array.isArray(existingPreParsing) ? existingPreParsing : [existingPreParsing]), ]; if (!skipVerify) { const existingPreHandler = routeOptions.preHandler || []; routeOptions.preHandler = [ verifySignature, ...(Array.isArray(existingPreHandler) ? existingPreHandler : [existingPreHandler]), ]; } }); const parseRawBody = (request, _reply, payload, done) => { const { bodyLimit } = request.routeOptions; getRawBody(payload, { length: null, limit: bodyLimit, // avoid memory leak encoding: 'utf8', // ensure the body is a string }, (err, string) => { if (err) { /** * the error is managed by fastify server */ return; } request.rawBody = string; }); done(null, payload); }; const verifySignature = (request, _reply, done) => { const signature = request.headers[LINE_SIGNATURE_HTTP_HEADER_NAME]; if (!signature) { done(new MissingSignatureError()); return; } if (request.rawBody && !validateSignature(request.rawBody, channelSecret, signature)) { done(new InvalidSignatureError(signature)); return; } done(); }; done(); }; export const fastifyLine = fp(plugin, { name: 'fastify-line', fastify: '5.x', });