Files
nest/packages/core/middleware/utils.ts
2024-11-26 09:39:20 +01:00

125 lines
3.5 KiB
TypeScript

import { RequestMethod } from '@nestjs/common';
import { HttpServer, RouteInfo, Type } from '@nestjs/common/interfaces';
import {
addLeadingSlash,
isFunction,
isString,
} from '@nestjs/common/utils/shared.utils';
import { iterate } from 'iterare';
import * as pathToRegexp from 'path-to-regexp';
import { uid } from 'uid';
import { ExcludeRouteMetadata } from '../router/interfaces/exclude-route-metadata.interface';
import { isRouteExcluded } from '../router/utils';
export const mapToExcludeRoute = (
routes: (string | RouteInfo)[],
): ExcludeRouteMetadata[] => {
return routes.map(route => {
if (isString(route)) {
return {
path: route,
requestMethod: RequestMethod.ALL,
pathRegex: pathToRegexp(addLeadingSlash(route)),
};
}
return {
path: route.path,
requestMethod: route.method,
pathRegex: pathToRegexp(addLeadingSlash(route.path)),
};
});
};
export const filterMiddleware = <T extends Function | Type<any> = any>(
middleware: T[],
routes: RouteInfo[],
httpAdapter: HttpServer,
) => {
const excludedRoutes = mapToExcludeRoute(routes);
return iterate([])
.concat(middleware)
.filter(isFunction)
.map((item: T) => mapToClass(item, excludedRoutes, httpAdapter))
.toArray();
};
export const mapToClass = <T extends Function | Type<any>>(
middleware: T,
excludedRoutes: ExcludeRouteMetadata[],
httpAdapter: HttpServer,
) => {
if (isMiddlewareClass(middleware)) {
if (excludedRoutes.length <= 0) {
return middleware;
}
const MiddlewareHost = class extends middleware {
use(...params: unknown[]) {
const [req, _, next] = params as [Record<string, any>, any, Function];
const isExcluded = isMiddlewareRouteExcluded(
req,
excludedRoutes,
httpAdapter,
);
if (isExcluded) {
return next();
}
return super.use(...params);
}
};
return assignToken(MiddlewareHost, middleware.name);
}
return assignToken(
class {
use = (...params: unknown[]) => {
const [req, _, next] = params as [Record<string, any>, any, Function];
const isExcluded = isMiddlewareRouteExcluded(
req,
excludedRoutes,
httpAdapter,
);
if (isExcluded) {
return next();
}
return (middleware as Function)(...params);
};
},
);
};
export function isMiddlewareClass(middleware: any): middleware is Type<any> {
const middlewareStr = middleware.toString();
if (middlewareStr.substring(0, 5) === 'class') {
return true;
}
const middlewareArr = middlewareStr.split(' ');
return (
middlewareArr[0] === 'function' &&
/[A-Z]/.test(middlewareArr[1]?.[0]) &&
isFunction(middleware.prototype?.use)
);
}
export function assignToken(metatype: Type<any>, token = uid(21)): Type<any> {
Object.defineProperty(metatype, 'name', { value: token });
return metatype;
}
export function isMiddlewareRouteExcluded(
req: Record<string, any>,
excludedRoutes: ExcludeRouteMetadata[],
httpAdapter: HttpServer,
): boolean {
if (excludedRoutes.length <= 0) {
return false;
}
const reqMethod = httpAdapter.getRequestMethod(req);
const originalUrl = httpAdapter.getRequestUrl(req);
const queryParamsIndex = originalUrl && originalUrl.indexOf('?');
const pathname =
queryParamsIndex >= 0
? originalUrl.slice(0, queryParamsIndex)
: originalUrl;
return isRouteExcluded(excludedRoutes, pathname, RequestMethod[reqMethod]);
}