UNPKG

@midwayjs/faas

Version:

![](https://img.alicdn.com/tfs/TB1HdniCSf2gK0jSZFPXXXsopXa-1000-353.png)

457 lines 19.3 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MidwayFaaSFramework = void 0; const core_1 = require("@midwayjs/core"); const simple_lock_1 = require("@midwayjs/simple-lock"); const logger_1 = require("@midwayjs/logger"); const serverless_http_parser_1 = require("@midwayjs/serverless-http-parser"); const util_1 = require("util"); const { isAnyArrayBuffer, isUint8Array } = util_1.types; const LOCK_KEY = '_faas_starter_start_key'; let MidwayFaaSFramework = class MidwayFaaSFramework extends core_1.BaseFramework { constructor() { super(...arguments); this.defaultHandlerMethod = 'handler'; this.funMappingStore = new Map(); this.lock = new simple_lock_1.default(); this.isReplaceLogger = process.env['MIDWAY_SERVERLESS_REPLACE_LOGGER'] === 'true'; this.developmentRun = false; this.httpMiddlewareManager = this.createMiddlewareManager(); this.eventMiddlewareManager = this.createMiddlewareManager(); this.legacyVersion = false; this.loadedFunction = false; } configure(options) { var _a; const faasConfig = (_a = this.configService.getConfiguration('faas')) !== null && _a !== void 0 ? _a : {}; if (options || faasConfig['developmentRun']) { this.developmentRun = true; this.configurationOptions = options; } else { return faasConfig; } } isEnable() { return !this.developmentRun; } async applicationInitialize(options) { var _a, _b, _c; if (!this.logger) { this.logger = options.logger || logger_1.loggers.getLogger('appLogger'); } this.applicationAdapter = this.configurationOptions.applicationAdapter || {}; if (this.applicationAdapter.getApplication) { this.legacyVersion = true; } this.app = ((_b = (_a = this.applicationAdapter).getApplication) === null || _b === void 0 ? void 0 : _b.call(_a)) || new serverless_http_parser_1.Application(); this.defineApplicationProperties({ /** * return init context value such as aliyun fc */ getInitializeContext: () => { return this.configurationOptions.initializeContext; }, /** * @deprecated * @param middlewareId */ generateMiddleware: async (middlewareId) => { return this.generateMiddleware(middlewareId); }, getFunctionName: () => { var _a; return (process.env.MIDWAY_SERVERLESS_FUNCTION_NAME || ((_a = this.configurationOptions.applicationAdapter) === null || _a === void 0 ? void 0 : _a.getFunctionName()) || ''); }, /** * get function service/group in runtime */ getFunctionServiceName: () => { var _a; return (process.env.MIDWAY_SERVERLESS_SERVICE_NAME || ((_a = this.configurationOptions.applicationAdapter) === null || _a === void 0 ? void 0 : _a.getFunctionServiceName()) || ''); }, useEventMiddleware: middleware => { return this.useEventMiddleware(middleware); }, getEventMiddleware: () => { return this.getEventMiddleware(); }, getServerlessInstance: async () => { throw new core_1.MidwayFeatureNotImplementedError('Please use it in by @midwayjs/mock in test.'); }, invokeTriggerFunction: (context, handlerMapping, options) => { return this.invokeTriggerFunction(context, handlerMapping, options); }, }); // hack use method this.app.originUse = this.app.use; this.app.use = this.app.useMiddleware; if ((_c = this.configurationOptions.applicationAdapter) === null || _c === void 0 ? void 0 : _c.runAppHook) { this.configurationOptions.applicationAdapter.runAppHook(this.app); } } async run() { if (this.legacyVersion) { return this.loadFunction(); } } async loadFunction() { if (!this.loadedFunction) { this.loadedFunction = true; return this.lock.sureOnce(async () => { var _a; // set app keys this.app['keys'] = (_a = this.configService.getConfiguration('keys')) !== null && _a !== void 0 ? _a : ''; // store all http function entry this.serverlessFunctionService = await this.applicationContext.getAsync(core_1.MidwayServerlessFunctionService); const functionList = await this.serverlessFunctionService.getFunctionList(); for (const funcInfo of functionList) { // store handler this.funMappingStore.set(funcInfo.funcHandlerName, funcInfo); } this.respond = this.app.callback(); }, LOCK_KEY); } } getFrameworkType() { return core_1.MidwayFrameworkType.FAAS; } /** * @deprecated * @param handlerMapping */ handleInvokeWrapper(handlerMapping) { const funOptions = this.funMappingStore.get(handlerMapping); return async (...args) => { if (args.length === 0) { throw new Error('first parameter must be function context'); } if (!funOptions) { throw new Error(`function handler = ${handlerMapping} not found`); } const context = this.getContext(args.shift()); const isHttpFunction = !!(context.headers && context.get); const globalMiddlewareFn = await this.applyMiddleware(); const middlewareManager = new core_1.ContextMiddlewareManager(); middlewareManager.insertLast(globalMiddlewareFn); middlewareManager.insertLast(async (ctx, next) => { const fn = await this.middlewareService.compose([ ...(isHttpFunction ? this.httpMiddlewareManager : this.eventMiddlewareManager), ...funOptions.controllerMiddleware, ...funOptions.middleware, async (ctx, next) => { if (funOptions.controllerClz && typeof funOptions.method === 'string') { const isPassed = await this.app .getFramework() .runGuard(ctx, funOptions.controllerClz, funOptions.method); if (!isPassed) { throw new core_1.httpError.ForbiddenError(); } } if (isHttpFunction) { args = [ctx]; } // invoke handler const result = await this.invokeHandler(funOptions, ctx, args, isHttpFunction); if (isHttpFunction && result !== undefined) { if (result === null) { // 这样设置可以绕过 koa 的 _explicitStatus 赋值机制 ctx.response._body = null; } else { ctx.body = result; } } return result; }, ], this.app); return await fn(ctx, next); }); const composeMiddleware = await this.middlewareService.compose(middlewareManager, this.app); return await composeMiddleware(context); }; } async invokeTriggerFunction(context, handlerMapping, options) { var _a, _b; let funOptions = this.funMappingStore.get(handlerMapping); const isHttpFunction = options.isHttpFunction; if (!funOptions && isHttpFunction) { funOptions = await this.serverlessFunctionService.getMatchedRouterInfo(context.path, context.method); if (funOptions) { const matchRes = core_1.PathToRegexpUtil.match(funOptions.fullUrlFlattenString)(context.path); context.req.pathParameters = matchRes['params'] || {}; } else { // options request pass throuth to middleware if (((_a = context.method) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'options') { funOptions = { url: context.path, method: 'options', requestMethod: 'options', controllerMiddleware: [], middleware: [], }; } } } if (!funOptions) { throw new Error(`function handler = ${handlerMapping} not found`); } context = this.getContext(context); if ((_b = this.configurationOptions.applicationAdapter) === null || _b === void 0 ? void 0 : _b.runContextHook) { this.configurationOptions.applicationAdapter.runContextHook(context); } const result = await (await this.applyMiddleware(async (ctx, next) => { const fn = await this.middlewareService.compose([ ...(isHttpFunction ? this.httpMiddlewareManager : this.eventMiddlewareManager), ...funOptions.controllerMiddleware, ...funOptions.middleware, async (ctx, next) => { let args; if (isHttpFunction) { args = [ctx]; } else { args = [ctx.originEvent, ctx.originContext]; } // invoke handler const result = await this.invokeHandler(funOptions, ctx, args, isHttpFunction); if (isHttpFunction && result !== undefined) { if (result === null) { // 这样设置可以绕过 koa 的 _explicitStatus 赋值机制 ctx.response._body = null; } else { ctx.body = result; } } // http 靠 ctx.body,否则会出现状态码不正确的问题 if (!isHttpFunction) { return result; } }, ], this.app); return await fn(ctx, next); }))(context); if (isHttpFunction) { if (options.isCustomHttpResponse) { return context.body; } else { return this.formatHttpResponse(context, options); } } else { return result; } } formatHttpResponse(context, options = {}) { var _a; if (!((_a = context.response) === null || _a === void 0 ? void 0 : _a._explicitStatus)) { if (context.body === null || context.body === 'undefined') { context.body = ''; context.type = 'text'; context.status = 204; } } let encoded = false; const data = context.body; if (typeof data === 'string') { if (!context.type) { context.type = 'text/plain'; } context.body = data; } else if (isAnyArrayBuffer(data) || isUint8Array(data)) { if (!context.type) { context.type = 'application/octet-stream'; } if (options.supportBufferResponse) { context.body = data; } else { encoded = true; // data is reserved as buffer context.body = Buffer.from(data).toString('base64'); context.length = data.byteLength; } } else if (typeof data === 'object') { if (!context.type) { context.type = 'application/json'; } // set data to string context.body = JSON.stringify(data); } else { if (!context.type) { context.type = 'text/plain'; } // set data to string context.body = data + ''; } // middleware return value and will be got 204 status if (context.body === undefined && !context.response._explicitStatus && context._matchedRoute) { // 如果进了路由,重新赋值,防止 404 context.body = undefined; } return { isBase64Encoded: encoded, statusCode: context.status, headers: context.res.headers, body: context.body, }; } async wrapHttpRequest(req, res, options) { const newReq = res ? new serverless_http_parser_1.HTTPRequest(req, res) : req; const newRes = new serverless_http_parser_1.HTTPResponse(options); return this.createHttpContext(newReq, newRes); } /** * @deprecated * @param middlewareId */ async generateMiddleware(middlewareId) { const mwIns = await this.getApplicationContext().getAsync(middlewareId); return mwIns.resolve(); } getContext(context = {}) { if (!context.env) { context.env = this.environmentService.getCurrentEnvironment(); } if (this.isReplaceLogger || !context.logger) { context._serverlessLogger = this.createContextLogger(context); /** * 由于 fc 的 logger 有 bug,FC公有云环境我们会默认替换掉,其他平台后续视情况而定 */ Object.defineProperty(context, 'logger', { get() { return context._serverlessLogger; }, }); } this.app.createAnonymousContext(context); return context; } async invokeHandler(routerInfo, context, args, isHttpFunction) { if (typeof routerInfo.method !== 'string') { if (!isHttpFunction) { args.unshift(context); } return routerInfo.method(...args); } else { const funModule = await context.requestContext.getAsync(routerInfo.controllerId); const handlerName = this.getFunctionHandler(context, args, funModule, routerInfo.method) || this.defaultHandlerMethod; if (funModule[handlerName]) { // invoke real method const result = await funModule[handlerName](...args); // implement response decorator const routerResponseData = routerInfo.responseMetadata; if (isHttpFunction) { for (const routerRes of routerResponseData) { switch (routerRes.type) { case core_1.WEB_RESPONSE_HTTP_CODE: context.status = routerRes.code; break; case core_1.WEB_RESPONSE_HEADER: for (const key in (routerRes === null || routerRes === void 0 ? void 0 : routerRes.setHeaders) || {}) { context.set(key, routerRes.setHeaders[key]); } break; case core_1.WEB_RESPONSE_CONTENT_TYPE: context.type = routerRes.contentType; break; case core_1.WEB_RESPONSE_REDIRECT: context.status = routerRes.code; context.redirect(routerRes.url); return; } } } return result; } } } getFunctionHandler(ctx, args, target, method) { if (method && typeof target[method] !== 'undefined') { return method; } const handlerMethod = this.defaultHandlerMethod; if (handlerMethod && typeof target[handlerMethod] !== 'undefined') { return handlerMethod; } throw new Error(`no handler setup on ${target.name}#${method || this.defaultHandlerMethod}`); } createLogger(name, option = {}) { // 覆盖基类的创建日志对象,函数场景下的日志,即使自定义,也只启用控制台输出 return (0, logger_1.createConsoleLogger)(name, option); } getFrameworkName() { return 'midway:faas'; } getServer() { return this.server; } async beforeStop() { if (this.server) { new Promise(resolve => { this.server.close(resolve); }); } } async createHttpContext(req, res) { return new Promise(resolve => { this.respond(req, res, resolve); }); } useMiddleware(middleware) { this.httpMiddlewareManager.insertLast(middleware); } useEventMiddleware(middleware) { this.eventMiddlewareManager.insertLast(middleware); } getEventMiddleware() { return this.eventMiddlewareManager; } getAllHandlerNames() { return Array.from(this.funMappingStore.keys()); } }; __decorate([ (0, core_1.Inject)(), __metadata("design:type", core_1.MidwayEnvironmentService) ], MidwayFaaSFramework.prototype, "environmentService", void 0); __decorate([ (0, core_1.Inject)(), __metadata("design:type", core_1.MidwayMiddlewareService) ], MidwayFaaSFramework.prototype, "middlewareService", void 0); MidwayFaaSFramework = __decorate([ (0, core_1.Framework)() ], MidwayFaaSFramework); exports.MidwayFaaSFramework = MidwayFaaSFramework; //# sourceMappingURL=framework.js.map