UNPKG

@geek-fun/serverlessinsight

Version:

Full life cycle cross providers serverless application management for your fast-growing business.

146 lines (145 loc) 6.88 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.eventsHandler = void 0; const types_1 = require("../../types"); const lodash_1 = require("lodash"); const common_1 = require("../../common"); const function_1 = require("./function"); const aliyunFc_1 = require("./aliyunFc"); const functionRunner_1 = require("./functionRunner"); const node_path_1 = __importDefault(require("node:path")); const node_fs_1 = __importDefault(require("node:fs")); const utils_1 = require("./utils"); const matchTrigger = (req, trigger) => { if (req.method !== 'ANY' && req.method !== trigger.method) { return false; } const normalize = (s) => s.replace(/^\/+|\/+$/g, ''); const [pathSegments, triggerSegments] = [ normalize(req.path).split('/'), normalize(trigger.path).split('/'), ]; const hasWildcard = triggerSegments[triggerSegments.length - 1] === '*'; const prefixSegments = hasWildcard ? triggerSegments.slice(0, -1) : triggerSegments; const minRequiredSegments = prefixSegments.length; if (pathSegments.length < minRequiredSegments) return false; return prefixSegments.every((triggerSegment, index) => { const pathSegment = pathSegments[index]; if (triggerSegment.startsWith('[') && triggerSegment.endsWith(']')) { return pathSegment !== ''; } return triggerSegment === pathSegment; }); }; const servEvent = async (req, parsed, iac) => { const startTime = new Date(); const requestId = (0, aliyunFc_1.generateRequestId)(); const sourceIp = req.socket?.remoteAddress || '127.0.0.1'; const isAliyun = iac.provider.name === common_1.ProviderEnum.ALIYUN; const event = iac.events?.find((event) => event.type === types_1.EventTypes.API_GATEWAY && event.key === parsed.identifier); if ((0, lodash_1.isEmpty)(event)) { return { statusCode: 404, body: { error: 'API Gateway event not found', event: parsed.identifier }, }; } common_1.logger.info(`Event trigger ${JSON.stringify(event.triggers)}, req method: ${req.method}, req url${req.url}`); const matchedTrigger = event.triggers.find((trigger) => matchTrigger({ method: parsed.method, path: parsed.url }, trigger)); if (!matchedTrigger) { const endTime = new Date(); if (isAliyun) { (0, aliyunFc_1.logApiGatewayRequest)(requestId, parsed.url, 404, startTime, endTime, sourceIp); } return { statusCode: 404, body: { error: 'No matching trigger found' } }; } if (matchedTrigger.backend) { const backendDef = (0, common_1.getIacDefinition)(iac, matchedTrigger.backend); if (!backendDef) { const endTime = new Date(); if (isAliyun) { (0, aliyunFc_1.logApiGatewayRequest)(requestId, parsed.url, 500, startTime, endTime, sourceIp); } return { statusCode: 500, body: { error: 'Backend definition missing', backend: matchedTrigger.backend }, }; } // For Aliyun, handle the function execution with proper event transformation if (isAliyun && backendDef.code) { let tempDir = null; try { const { event: aliyunEvent } = await (0, aliyunFc_1.transformToAliyunEvent)(req, parsed.url, parsed.query); const codePath = node_path_1.default.resolve(process.cwd(), backendDef.code.path); let codeDir; if (codePath.endsWith('.zip') && node_fs_1.default.existsSync(codePath)) { tempDir = await (0, utils_1.extractZipFile)(codePath); codeDir = tempDir; } else if (node_fs_1.default.existsSync(codePath) && node_fs_1.default.statSync(codePath).isDirectory()) { codeDir = codePath; } else { codeDir = node_path_1.default.dirname(codePath); } const funOptions = { codeDir, functionKey: backendDef.key, handler: backendDef.code.handler, servicePath: '', timeout: backendDef.timeout * 1000, }; const aliyunContext = (0, aliyunFc_1.createAliyunContextSerializable)(iac, backendDef.name, backendDef.code.handler, backendDef.memory, backendDef.timeout, requestId); const env = { ...backendDef.environment, }; common_1.logger.debug(`Invoking FC function with Aliyun event format`); const result = await (0, functionRunner_1.invokeFunction)(funOptions, env, aliyunEvent, aliyunContext); const endTime = new Date(); const transformed = (0, aliyunFc_1.transformFCResponse)(result); // Log API Gateway request (0, aliyunFc_1.logApiGatewayRequest)(requestId, parsed.url, transformed.statusCode, startTime, endTime, sourceIp); return { statusCode: transformed.statusCode, headers: transformed.headers, body: transformed.body, }; } catch (error) { const endTime = new Date(); (0, aliyunFc_1.logApiGatewayRequest)(requestId, parsed.url, 500, startTime, endTime, sourceIp); common_1.logger.error(`Function execution error: ${error}`); return { statusCode: 500, body: { error: 'Function execution failed', message: error instanceof Error ? error.message : String(error), }, }; } finally { if (tempDir && node_fs_1.default.existsSync(tempDir)) { node_fs_1.default.rmSync(tempDir, { recursive: true, force: true }); } } } // For non-Aliyun or when using functionsHandler const result = await (0, function_1.functionsHandler)(req, { ...parsed, identifier: backendDef?.key }, iac); const endTime = new Date(); if (isAliyun) { (0, aliyunFc_1.logApiGatewayRequest)(requestId, parsed.url, result.statusCode, startTime, endTime, sourceIp); } return result; } return { statusCode: 202, body: { message: 'Trigger matched but no backend configured' }, }; }; const eventsHandler = async (req, parsed, iac) => { return await servEvent(req, parsed, iac); }; exports.eventsHandler = eventsHandler;