@expressots/core
Version:
Expressots - modern, fast, lightweight nodejs web framework (@core)
489 lines (488 loc) • 19.2 kB
JavaScript
"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;