Spaces:
Sleeping
Sleeping
| ; | |
| Object.defineProperty(exports, "__esModule", { value: true }); | |
| exports.MiddlewareModule = void 0; | |
| const common_1 = require("@nestjs/common"); | |
| const request_method_enum_1 = require("@nestjs/common/enums/request-method.enum"); | |
| const shared_utils_1 = require("@nestjs/common/utils/shared.utils"); | |
| const invalid_middleware_exception_1 = require("../errors/exceptions/invalid-middleware.exception"); | |
| const runtime_exception_1 = require("../errors/exceptions/runtime.exception"); | |
| const context_id_factory_1 = require("../helpers/context-id-factory"); | |
| const execution_context_host_1 = require("../helpers/execution-context-host"); | |
| const constants_1 = require("../injector/constants"); | |
| const request_constants_1 = require("../router/request/request-constants"); | |
| const router_exception_filters_1 = require("../router/router-exception-filters"); | |
| const router_proxy_1 = require("../router/router-proxy"); | |
| const utils_1 = require("../router/utils"); | |
| const builder_1 = require("./builder"); | |
| const resolver_1 = require("./resolver"); | |
| const route_info_path_extractor_1 = require("./route-info-path-extractor"); | |
| const routes_mapper_1 = require("./routes-mapper"); | |
| class MiddlewareModule { | |
| constructor() { | |
| this.routerProxy = new router_proxy_1.RouterProxy(); | |
| this.exceptionFiltersCache = new WeakMap(); | |
| this.logger = new common_1.Logger(MiddlewareModule.name); | |
| } | |
| async register(middlewareContainer, container, config, injector, httpAdapter, graphInspector, options) { | |
| this.appOptions = options; | |
| const appRef = container.getHttpAdapterRef(); | |
| this.routerExceptionFilter = new router_exception_filters_1.RouterExceptionFilters(container, config, appRef); | |
| this.routesMapper = new routes_mapper_1.RoutesMapper(container, config); | |
| this.resolver = new resolver_1.MiddlewareResolver(middlewareContainer, injector); | |
| this.routeInfoPathExtractor = new route_info_path_extractor_1.RouteInfoPathExtractor(config); | |
| this.injector = injector; | |
| this.container = container; | |
| this.httpAdapter = httpAdapter; | |
| this.graphInspector = graphInspector; | |
| const modules = container.getModules(); | |
| await this.resolveMiddleware(middlewareContainer, modules); | |
| } | |
| async resolveMiddleware(middlewareContainer, modules) { | |
| const moduleEntries = [...modules.entries()]; | |
| const loadMiddlewareConfiguration = async ([moduleName, moduleRef]) => { | |
| await this.loadConfiguration(middlewareContainer, moduleRef, moduleName); | |
| await this.resolver.resolveInstances(moduleRef, moduleName); | |
| }; | |
| await Promise.all(moduleEntries.map(loadMiddlewareConfiguration)); | |
| } | |
| async loadConfiguration(middlewareContainer, moduleRef, moduleKey) { | |
| const { instance } = moduleRef; | |
| if (!instance.configure) { | |
| return; | |
| } | |
| const middlewareBuilder = new builder_1.MiddlewareBuilder(this.routesMapper, this.httpAdapter, this.routeInfoPathExtractor); | |
| try { | |
| await instance.configure(middlewareBuilder); | |
| } | |
| catch (err) { | |
| if (!this.appOptions.preview) { | |
| throw err; | |
| } | |
| const warningMessage = `Warning! "${moduleRef.name}" module exposes a "configure" method that throws an exception in the preview mode` + | |
| ` (possibly due to missing dependencies). Note: you can ignore this message, just be aware that some of those conditional middlewares will not be reflected in your graph.`; | |
| this.logger.warn(warningMessage); | |
| } | |
| if (!(middlewareBuilder instanceof builder_1.MiddlewareBuilder)) { | |
| return; | |
| } | |
| const config = middlewareBuilder.build(); | |
| middlewareContainer.insertConfig(config, moduleKey); | |
| } | |
| async registerMiddleware(middlewareContainer, applicationRef) { | |
| const configs = middlewareContainer.getConfigurations(); | |
| const registerAllConfigs = async (moduleKey, middlewareConfig) => { | |
| for (const config of middlewareConfig) { | |
| await this.registerMiddlewareConfig(middlewareContainer, config, moduleKey, applicationRef); | |
| } | |
| }; | |
| const entriesSortedByDistance = [...configs.entries()].sort(([moduleA], [moduleB]) => { | |
| const moduleARef = this.container.getModuleByKey(moduleA); | |
| const moduleBRef = this.container.getModuleByKey(moduleB); | |
| const isModuleAGlobal = moduleARef.distance === Number.MAX_VALUE; | |
| const isModuleBGlobal = moduleBRef.distance === Number.MAX_VALUE; | |
| if (isModuleAGlobal && isModuleBGlobal) { | |
| return 0; | |
| } | |
| if (isModuleAGlobal) { | |
| return -1; | |
| } | |
| if (isModuleBGlobal) { | |
| return 1; | |
| } | |
| return moduleARef.distance - moduleBRef.distance; | |
| }); | |
| for (const [moduleRef, moduleConfigurations] of entriesSortedByDistance) { | |
| await registerAllConfigs(moduleRef, [...moduleConfigurations]); | |
| } | |
| } | |
| async registerMiddlewareConfig(middlewareContainer, config, moduleKey, applicationRef) { | |
| const { forRoutes } = config; | |
| for (const routeInfo of forRoutes) { | |
| await this.registerRouteMiddleware(middlewareContainer, routeInfo, config, moduleKey, applicationRef); | |
| } | |
| } | |
| async registerRouteMiddleware(middlewareContainer, routeInfo, config, moduleKey, applicationRef) { | |
| const middlewareCollection = [].concat(config.middleware); | |
| const moduleRef = this.container.getModuleByKey(moduleKey); | |
| for (const metatype of middlewareCollection) { | |
| const collection = middlewareContainer.getMiddlewareCollection(moduleKey); | |
| const instanceWrapper = collection.get(metatype); | |
| if ((0, shared_utils_1.isUndefined)(instanceWrapper)) { | |
| throw new runtime_exception_1.RuntimeException(); | |
| } | |
| if (instanceWrapper.isTransient) { | |
| return; | |
| } | |
| this.graphInspector.insertClassNode(moduleRef, instanceWrapper, 'middleware'); | |
| const middlewareDefinition = { | |
| type: 'middleware', | |
| methodName: 'use', | |
| className: instanceWrapper.name, | |
| classNodeId: instanceWrapper.id, | |
| metadata: { | |
| key: routeInfo.path, | |
| path: routeInfo.path, | |
| requestMethod: request_method_enum_1.RequestMethod[routeInfo.method] ?? | |
| 'ALL', | |
| version: routeInfo.version, | |
| }, | |
| }; | |
| this.graphInspector.insertEntrypointDefinition(middlewareDefinition, instanceWrapper.id); | |
| await this.bindHandler(instanceWrapper, applicationRef, routeInfo, moduleRef, collection); | |
| } | |
| } | |
| async bindHandler(wrapper, applicationRef, routeInfo, moduleRef, collection) { | |
| const { instance, metatype } = wrapper; | |
| if ((0, shared_utils_1.isUndefined)(instance?.use)) { | |
| throw new invalid_middleware_exception_1.InvalidMiddlewareException(metatype.name); | |
| } | |
| const isStatic = wrapper.isDependencyTreeStatic(); | |
| if (isStatic) { | |
| const proxy = await this.createProxy(instance); | |
| return this.registerHandler(applicationRef, routeInfo, proxy); | |
| } | |
| const isTreeDurable = wrapper.isDependencyTreeDurable(); | |
| await this.registerHandler(applicationRef, routeInfo, async (req, res, next) => { | |
| try { | |
| const contextId = this.getContextId(req, isTreeDurable); | |
| const contextInstance = await this.injector.loadPerContext(instance, moduleRef, collection, contextId); | |
| const proxy = await this.createProxy(contextInstance, contextId); | |
| return proxy(req, res, next); | |
| } | |
| catch (err) { | |
| let exceptionsHandler = this.exceptionFiltersCache.get(instance.use); | |
| if (!exceptionsHandler) { | |
| exceptionsHandler = this.routerExceptionFilter.create(instance, instance.use, undefined); | |
| this.exceptionFiltersCache.set(instance.use, exceptionsHandler); | |
| } | |
| const host = new execution_context_host_1.ExecutionContextHost([req, res, next]); | |
| exceptionsHandler.next(err, host); | |
| } | |
| }); | |
| } | |
| async createProxy(instance, contextId = constants_1.STATIC_CONTEXT) { | |
| const exceptionsHandler = this.routerExceptionFilter.create(instance, instance.use, undefined, contextId); | |
| const middleware = instance.use.bind(instance); | |
| return this.routerProxy.createProxy(middleware, exceptionsHandler); | |
| } | |
| async registerHandler(applicationRef, routeInfo, proxy) { | |
| const { method } = routeInfo; | |
| const paths = this.routeInfoPathExtractor.extractPathsFrom(routeInfo); | |
| const isMethodAll = (0, utils_1.isRequestMethodAll)(method); | |
| const requestMethod = request_method_enum_1.RequestMethod[method]; | |
| const router = await applicationRef.createMiddlewareFactory(method); | |
| const middlewareFunction = isMethodAll | |
| ? proxy | |
| : (req, res, next) => { | |
| const actualRequestMethod = applicationRef.getRequestMethod?.(req); | |
| if (actualRequestMethod === requestMethod || | |
| (actualRequestMethod === request_method_enum_1.RequestMethod[request_method_enum_1.RequestMethod.HEAD] && | |
| requestMethod === request_method_enum_1.RequestMethod[request_method_enum_1.RequestMethod.GET])) { | |
| return proxy(req, res, next); | |
| } | |
| return next(); | |
| }; | |
| const pathsToApplyMiddleware = []; | |
| paths.some(path => path.match(/^\/?$/)) | |
| ? pathsToApplyMiddleware.push('/') | |
| : pathsToApplyMiddleware.push(...paths); | |
| pathsToApplyMiddleware.forEach(path => router(path, middlewareFunction)); | |
| } | |
| getContextId(request, isTreeDurable) { | |
| const contextId = context_id_factory_1.ContextIdFactory.getByRequest(request); | |
| if (!request[request_constants_1.REQUEST_CONTEXT_ID]) { | |
| Object.defineProperty(request, request_constants_1.REQUEST_CONTEXT_ID, { | |
| value: contextId, | |
| enumerable: false, | |
| writable: false, | |
| configurable: false, | |
| }); | |
| const requestProviderValue = isTreeDurable | |
| ? contextId.payload | |
| : Object.assign(request, contextId.payload); | |
| this.container.registerRequestProvider(requestProviderValue, contextId); | |
| } | |
| return contextId; | |
| } | |
| } | |
| exports.MiddlewareModule = MiddlewareModule; | |