@geek-fun/serverlessinsight
Version:
Full life cycle cross providers serverless application management for your fast-growing business.
146 lines (145 loc) • 6.88 kB
JavaScript
;
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;