UNPKG

@seratch_/bolt-fastify

Version:
246 lines 11.8 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const oauth_1 = require("@slack/oauth"); const logger_1 = require("@slack/logger"); const router_1 = __importDefault(require("@koa/router")); const koa_1 = __importDefault(require("koa")); const http_1 = require("http"); const bolt_1 = require("@slack/bolt"); class KoaReceiver { constructor(options) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o; this.signatureVerification = (_a = options.signatureVerification) !== null && _a !== void 0 ? _a : true; this.signingSecretProvider = options.signingSecret; this.customPropertiesExtractor = options.customPropertiesExtractor !== undefined ? options.customPropertiesExtractor : (_) => ({}); this.path = (_b = options.path) !== null && _b !== void 0 ? _b : '/slack/events'; this.unhandledRequestTimeoutMillis = (_c = options.unhandledRequestTimeoutMillis) !== null && _c !== void 0 ? _c : 3001; this.koa = (_d = options.koa) !== null && _d !== void 0 ? _d : new koa_1.default(); this.router = (_e = options.router) !== null && _e !== void 0 ? _e : new router_1.default(); this.logger = (_f = options.logger) !== null && _f !== void 0 ? _f : (() => { const defaultLogger = new logger_1.ConsoleLogger(); if (options.logLevel) { defaultLogger.setLevel(options.logLevel); } return defaultLogger; })(); this.processBeforeResponse = (_g = options.processBeforeResponse) !== null && _g !== void 0 ? _g : false; this.dispatchErrorHandler = (_h = options.dispatchErrorHandler) !== null && _h !== void 0 ? _h : bolt_1.HTTPModuleFunctions.defaultAsyncDispatchErrorHandler; this.processEventErrorHandler = (_j = options.processEventErrorHandler) !== null && _j !== void 0 ? _j : bolt_1.HTTPModuleFunctions.defaultProcessEventErrorHandler; this.unhandledRequestHandler = (_k = options.unhandledRequestHandler) !== null && _k !== void 0 ? _k : bolt_1.HTTPModuleFunctions.defaultUnhandledRequestHandler; this.installerOptions = options.installerOptions; if (this.installerOptions && this.installerOptions.installPath === undefined) { this.installerOptions.installPath = '/slack/install'; } if (this.installerOptions && this.installerOptions.redirectUriPath === undefined) { this.installerOptions.redirectUriPath = '/slack/oauth_redirect'; } if (options.clientId && options.clientSecret) { this.installer = new oauth_1.InstallProvider({ ...this.installerOptions, clientId: options.clientId, clientSecret: options.clientSecret, stateSecret: options.stateSecret, installationStore: options.installationStore, logger: options.logger, logLevel: options.logLevel, installUrlOptions: { scopes: (_l = options.scopes) !== null && _l !== void 0 ? _l : [], userScopes: (_m = this.installerOptions) === null || _m === void 0 ? void 0 : _m.userScopes, metadata: (_o = this.installerOptions) === null || _o === void 0 ? void 0 : _o.metadata, redirectUri: options.redirectUri, }, }); } } async signingSecret() { if (this._sigingSecret === undefined) { this._sigingSecret = typeof this.signingSecretProvider === 'string' ? this.signingSecretProvider : await this.signingSecretProvider(); } return this._sigingSecret; } init(app) { this.app = app; if (this.installer && this.installerOptions && this.installerOptions.installPath && this.installerOptions.redirectUriPath) { this.router.get(this.installerOptions.installPath, async (ctx) => { var _a, _b; try { await ((_a = this.installer) === null || _a === void 0 ? void 0 : _a.handleInstallPath(ctx.req, ctx.res, (_b = this.installerOptions) === null || _b === void 0 ? void 0 : _b.installPathOptions)); } catch (error) { await this.dispatchErrorHandler({ error: error, logger: this.logger, request: ctx.req, response: ctx.res, }); } }); this.router.get(this.installerOptions.redirectUriPath, async (ctx) => { var _a, _b; try { await ((_a = this.installer) === null || _a === void 0 ? void 0 : _a.handleCallback(ctx.req, ctx.res, (_b = this.installerOptions) === null || _b === void 0 ? void 0 : _b.callbackOptions)); } catch (error) { await this.dispatchErrorHandler({ error: error, logger: this.logger, request: ctx.req, response: ctx.res, }); } }); } this.router.post(this.path, async (ctx) => { var _a; const { req, res } = ctx; try { // Verify authenticity let bufferedReq; try { bufferedReq = await bolt_1.HTTPModuleFunctions.parseAndVerifyHTTPRequest({ // If enabled: false, this method returns bufferredReq without verification enabled: this.signatureVerification, signingSecret: await this.signingSecret(), }, req); } catch (err) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const e = err; if (this.signatureVerification) { this.logger.warn(`Failed to parse and verify the request data: ${e.message}`); } else { this.logger.warn(`Failed to parse the request body: ${e.message}`); } bolt_1.HTTPModuleFunctions.buildNoBodyResponse(res, 401); return; } // Parse request body // eslint-disable-next-line @typescript-eslint/no-explicit-any let body; try { body = bolt_1.HTTPModuleFunctions.parseHTTPRequestBody(bufferedReq); } catch (err) { // eslint-disable-next-line @typescript-eslint/no-explicit-any const e = err; this.logger.warn(`Malformed request body: ${e.message}`); bolt_1.HTTPModuleFunctions.buildNoBodyResponse(res, 400); return; } // Handle SSL checks if (body.ssl_check) { bolt_1.HTTPModuleFunctions.buildSSLCheckResponse(res); return; } // Handle URL verification if (body.type === 'url_verification') { bolt_1.HTTPModuleFunctions.buildUrlVerificationResponse(res, body); return; } const ack = new bolt_1.HTTPResponseAck({ logger: this.logger, processBeforeResponse: this.processBeforeResponse, unhandledRequestHandler: this.unhandledRequestHandler, unhandledRequestTimeoutMillis: this.unhandledRequestTimeoutMillis, httpRequest: bufferedReq, httpResponse: res, }); // Structure the ReceiverEvent const event = { body, ack: ack.bind(), retryNum: bolt_1.HTTPModuleFunctions.extractRetryNumFromHTTPRequest(req), retryReason: bolt_1.HTTPModuleFunctions.extractRetryReasonFromHTTPRequest(req), customProperties: this.customPropertiesExtractor(bufferedReq), }; // Send the event to the app for processing try { await ((_a = this.app) === null || _a === void 0 ? void 0 : _a.processEvent(event)); if (ack.storedResponse !== undefined) { // in the case of processBeforeResponse: true bolt_1.HTTPModuleFunctions.buildContentResponse(res, ack.storedResponse); this.logger.debug('stored response sent'); } } catch (error) { const acknowledgedByHandler = await this.processEventErrorHandler({ error: error, logger: this.logger, request: req, response: res, storedResponse: ack.storedResponse, }); if (acknowledgedByHandler) { // If the value is false, we don't touch the value as a race condition // with ack() call may occur especially when processBeforeResponse: false ack.ack(); } } } catch (error) { await this.dispatchErrorHandler({ error: error, logger: this.logger, request: req, response: res, }); } }); } start(port = 3000) { // Enable routes this.koa.use(this.router.routes()).use(this.router.allowedMethods()); if (this.server !== undefined) { return Promise.reject(new bolt_1.ReceiverInconsistentStateError('The receiver cannot be started because it was already started.')); } return new Promise((resolve, reject) => { this.server = (0, http_1.createServer)(this.koa.callback()); this.server.on('error', (error) => { if (this.server) { this.server.close(); } reject(error); }); this.server.on('close', () => { this.server = undefined; }); this.server.listen(port, () => { if (this.server === undefined) { return reject(new bolt_1.ReceiverInconsistentStateError('The HTTP server is unexpectedly missing')); } return resolve(this.server); }); }); } stop() { if (this.server === undefined) { const errorMessage = 'The receiver cannot be stopped because it was not started.'; return Promise.reject(new bolt_1.ReceiverInconsistentStateError(errorMessage)); } return new Promise((resolve, reject) => { var _a; (_a = this.server) === null || _a === void 0 ? void 0 : _a.close((error) => { if (error !== undefined) { return reject(error); } this.server = undefined; return resolve(); }); }); } } exports.default = KoaReceiver; //# sourceMappingURL=KoaReceiver.js.map