UNPKG

@expressots/core

Version:

Expressots - modern, fast, lightweight nodejs web framework (@core)

489 lines (488 loc) 19.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Middleware = exports.ExpressoMiddleware = void 0; const express_1 = require("express"); const error_handler_middleware_1 = __importDefault(require("../error/error-handler-middleware")); const logger_provider_1 = require("../provider/logger/logger.provider"); const middleware_resolver_1 = require("./middleware-resolver"); /** * Abstract class for creating custom Expresso middleware. * Custom middleware classes should extend this class and implement the use method. * */ class ExpressoMiddleware { get name() { return this.constructor.name; } } exports.ExpressoMiddleware = ExpressoMiddleware; /** * MiddlewareType Enum * * The MiddlewareType enum represents the various types of middleware that can be added to the middleware collection. * - Config: Middleware configuration object. * - ExpressHandler: Express request handler function. * - IExpressoMiddleware: Custom Expresso middleware. */ var MiddlewareType; (function (MiddlewareType) { MiddlewareType[MiddlewareType["Config"] = 0] = "Config"; MiddlewareType[MiddlewareType["ExpressHandler"] = 1] = "ExpressHandler"; MiddlewareType[MiddlewareType["IExpressoMiddleware"] = 2] = "IExpressoMiddleware"; })(MiddlewareType || (MiddlewareType = {})); /** * Singleton class that implements the IConfigure interface. * Manages the middleware configuration for the application, * including adding Body Parser and retrieving all configured middlewares. * * @see IConfigure * @public API */ class Middleware { constructor() { this.middlewarePipeline = []; this.logger = new logger_provider_1.Logger(); } /** * Retrieves the type of the middleware. * * @param middleware - The middleware to be checked. * @returns The type of the middleware. */ getMiddlewareType(middleware) { // eslint-disable-next-line no-prototype-builtins if (middleware?.hasOwnProperty("path")) { return MiddlewareType.Config; } else if (middleware instanceof Function) { return MiddlewareType.ExpressHandler; } else { return MiddlewareType.IExpressoMiddleware; } } /** * Checks if a middleware with the given name exists in the middleware collection. * * @param middlewareName - The name of the middleware to be checked. * @returns A boolean value indicating whether the middleware exists or not. */ middlewareExists(middlewareName) { return this.middlewarePipeline.some((m) => { if (m.middleware instanceof Function) { return m.middleware.name === middlewareName; } else if (m.middleware instanceof Object) { return m.middleware.path === middlewareName; } return false; }); } /** * Adds a URL Encoded Parser middleware to the middleware collection. * The URL Encoded Parser is responsible for parsing the URL-encoded data in the incoming request bodies. * * @param options - Optional configuration options for the URL Encoded Parser. */ addUrlEncodedParser(options) { const middlewareExist = this.middlewareExists("urlencodedParser"); if (middlewareExist) { this.logger.warn(`[urlencodedParser] already exists. Skipping...`, "configure-service"); } else { this.middlewarePipeline.push({ timestamp: new Date(), middleware: (0, express_1.urlencoded)(options), }); } } addRateLimiter(options) { const middleware = (0, middleware_resolver_1.middlewareResolver)("rateLimit", options); const middlewareExist = this.middlewareExists("rateLimit"); if (middleware && !middlewareExist) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); } } /** * Adds a Body Parser middleware to the middleware collection using the given options. * * @param options - Optional configuration options for the JSON body parser. */ addBodyParser(options) { const middlewareExist = this.middlewareExists("jsonParser"); if (middlewareExist) { this.logger.warn(`[jsonParser] already exists. Skipping...`, "configure-service"); } else { this.middlewarePipeline.push({ timestamp: new Date(), middleware: (0, express_1.json)(options), }); } } /** * Adds Cross-Origin Resource Sharing (CORS) middleware to enable or control cross-origin requests. * * @param options - Optional configuration options for CORS. Defines the behavior of CORS requests like allowed origins, methods, headers, etc. */ addCors(options) { const middleware = (0, middleware_resolver_1.middlewareResolver)("cors", options); const middlewareExist = this.middlewareExists("cors"); if (middleware && !middlewareExist) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); } } /** * Adds Compression middleware to reduce the size of the response body and improve the speed of the client-server communication. * * @param options - Optional configuration options for Compression. Allows fine-tuning the compression behavior, such as setting the compression level, threshold, and filter functions to determine which requests should be compressed. */ addCompression(options) { const middleware = (0, middleware_resolver_1.middlewareResolver)("compression", options); const middlewareExist = this.middlewareExists("compression"); if (middleware && !middlewareExist) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); } } /** * Adds Morgan middleware to log HTTP requests. * * @param format - The log format. Can be a string or a function. * @param options - Optional configuration options for Morgan. Defines the behavior of the logger like the output stream, buffer duration, etc. */ addMorgan(format, options) { const middleware = (0, middleware_resolver_1.middlewareResolver)("morgan", format, options); const middlewareExist = this.middlewareExists("morgan"); if (middleware && !middlewareExist) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); } } /** * Adds Cookie Parser middleware to parse the cookie header and populate req.cookies with an object keyed by the cookie names. * * @param secret - A string or array used for signing cookies. This is optional and if not specified, the cookie-parser will not parse signed cookies. * @param options - Optional configuration options for Cookie Parser. */ addCookieParser(secret, options) { const middleware = (0, middleware_resolver_1.middlewareResolver)("cookieParser", secret, options); const middlewareExist = this.middlewareExists("cookieParser"); if (middleware && !middlewareExist) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); } } /** * Adds Cookie Session middleware to enable cookie-based sessions. * * @param options - Optional configuration options for Cookie Session. Defines the behavior of cookie sessions like the name of the cookie, keys to sign the cookie, etc. */ addCookieSession(options) { const middleware = (0, middleware_resolver_1.middlewareResolver)("cookieSession", options); const middlewareExist = this.middlewareExists("cookieSession"); if (middleware && !middlewareExist) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); } } /** * Adds a middleware to serve the favicon to the middleware collection. * The favicon is the icon that is displayed in the browser tab for the application. * * @param path - The path to the favicon file. * @param options - Optional configuration options for serving the favicon. Defines the behavior of the favicon middleware like cache control, custom headers, etc. */ addServeFavicon(path, options) { const middleware = (0, middleware_resolver_1.middlewareResolver)("serveFavicon", path, options); const middlewareExist = this.middlewareExists("serveFavicon"); if (middleware && !middlewareExist) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); } } setupMulter(options) { const multerMiddleware = (0, middleware_resolver_1.middlewareResolver)("multer", options); const middlewareExist = this.middlewareExists("multer"); if (multerMiddleware && !middlewareExist) { return multerMiddleware; } return null; } /** * Adds a middleware to enhance security by setting various HTTP headers. * * @param options - Optional configuration options for Helmet. * */ addHelmet(options) { const middleware = (0, middleware_resolver_1.middlewareResolver)("helmet", options); const middlewareExist = this.middlewareExists("helmet"); if (middleware && !middlewareExist) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); } } /** * Add a middleware to enable express-session. * * @param options - Optional configuration options for Session. * */ addSession(options) { const middleware = (0, middleware_resolver_1.middlewareResolver)("session", options); const middlewareExist = this.middlewareExists("session"); if (middleware && !middlewareExist) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); } } /** * Configures the error handling middleware for the application. * * @param options - The object containing the configuration options for the error handler middleware. * @param errorHandler - The Express error handler function that takes care of processing errors and formulating the response. * @param showStackTrace - A boolean value indicating whether to show the stack trace in the response. */ setErrorHandler(options = {}) { const { errorHandler: errorHandling, showStackTrace } = options; if (!errorHandling) { this.errorHandler = (error, req, res, next) => { (0, error_handler_middleware_1.default)(error, res, next, showStackTrace); }; } else { this.errorHandler = errorHandling; } } /** * Adds a middleware to serve static files from the specified root directory. * Allows the application to serve files like images, CSS, JavaScript, etc. * * @param root - The root directory from which the static assets are to be served. * @param options - Optional configuration options for serving static files. Defines behavior like cache control, custom headers, etc. */ serveStatic(root, options) { const middlewareExist = this.middlewareExists("serveStatic"); if (middlewareExist) { this.logger.warn(`[serveStatic] already exists. Skipping...`, "configure-service"); } else { this.middlewarePipeline.push({ timestamp: new Date(), middleware: (0, express_1.static)(root, options), }); } } /** * Helper method to add middleware configuration objects to the middleware collection. * @param middleware - The middleware configuration object to be added to the middleware collection. * @returns void */ addConfigMiddleware(middleware) { // eslint-disable-next-line no-case-declarations const config = middleware; let routeExists = false; if (config.middlewares.length === 0) { this.logger.warn(`No middlewares in the route [${config.path}]. Skipping...`, "configure-service"); return; } if (this.middlewarePipeline.length === 0) { this.middlewarePipeline.push({ timestamp: new Date(), middleware: config, }); } else { this.middlewarePipeline.forEach((m) => { if (m.middleware.path === config.path) { this.logger.warn(`[${config.path}] route already exists. Skipping...`, "configure-service"); routeExists = true; } }); if (!routeExists) { this.middlewarePipeline.push({ timestamp: new Date(), middleware: config, }); } } } /** * Helper method to add express request handler functions to the middleware collection. * @param middleware - The express request handler function to be added to the middleware collection. * @returns void */ addExpressHandlerMiddleware(middleware) { let middlewareExists = false; if (this.middlewarePipeline.length === 0) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); return; } this.middlewarePipeline.forEach((m) => { const mType = this.getMiddlewareType(m.middleware); if (mType === MiddlewareType.ExpressHandler) { if (m.middleware?.name === middleware?.name) { this.logger.warn(`[${middleware?.name}] already exists. Skipping...`, "configure-service"); middlewareExists = true; return; } } }); if (!middlewareExists) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); } } /** * Helper method to add custom Expresso middleware to the middleware collection. * @param middleware - The custom Expresso middleware to be added to the middleware collection. * @returns void */ addIExpressoMiddleware(middleware) { let middlewareExists = false; if (this.middlewarePipeline.length === 0) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); return; } this.middlewarePipeline.forEach((m) => { const mType = this.getMiddlewareType(m.middleware); if (mType === MiddlewareType.IExpressoMiddleware) { if (m.middleware.constructor.name === middleware.constructor.name) { this.logger.warn(`[${middleware.constructor.name}] already exists. Skipping...`, "configure-service"); middlewareExists = true; return; } } }); if (!middlewareExists) { this.middlewarePipeline.push({ timestamp: new Date(), middleware, }); } } /** * Adds a middleware to the middleware collection. * * @param options - The Express request handler function to be added to the middleware collection, or a middleware configuration object * that is composed by a route and an expressjs handler, or a custom Expresso middleware. * * @example Express Handler * const middleware = (req, res, next) => { * // Your middleware logic here * next(); * } * * @example Middleware Configuration Object * const middleware = { * path: "/", * middlewares: [] // Array of Express Handlers * } * * @example Expresso Middleware * class CustomMiddleware implements IExpressoMiddleware { * use(req: Request, res: Response, next: NextFunction): Promise<void> | void { * // Your middleware logic here * next(); * } * } */ addMiddleware(options) { switch (this.getMiddlewareType(options)) { case MiddlewareType.Config: this.addConfigMiddleware(options); break; case MiddlewareType.ExpressHandler: this.addExpressHandlerMiddleware(options); break; case MiddlewareType.IExpressoMiddleware: this.addIExpressoMiddleware(options); break; } } /** * Retrieves middleware pipeline in the order they were added. * * @returns An array of Express request handlers representing the middlewares. */ getMiddlewarePipeline() { return this.middlewarePipeline.sort((a, b) => { return a.timestamp.getTime() - b.timestamp.getTime(); }); } /** * View middleware pipeline formatted. * @returns void */ viewMiddlewarePipeline() { const sortedMiddlewarePipeline = this.getMiddlewarePipeline(); const formattedPipeline = sortedMiddlewarePipeline.map((m) => { const middlewareType = this.getMiddlewareType(m.middleware); if (middlewareType === MiddlewareType.Config) { const middlewareNames = m.middleware.middlewares.map((mw) => mw?.name || "Anonymous"); return { timestamp: m.timestamp.toISOString(), path: m.middleware.path, middleware: `[${middlewareNames.join(", ")}]`, }; } else if (middlewareType === MiddlewareType.IExpressoMiddleware) { return { timestamp: m.timestamp.toISOString(), path: m.middleware.path ?? "Global", middleware: m.middleware.constructor.name, }; } else { return { timestamp: m.timestamp.toISOString(), path: "Global", middleware: m.middleware?.name, }; } }); console.table(formattedPipeline); } /** * Gets the configured error handler middleware. * * @returns The error handler middleware. */ getErrorHandler() { return this.errorHandler; } } exports.Middleware = Middleware;