mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
136 lines
3.9 KiB
TypeScript
136 lines
3.9 KiB
TypeScript
import { iterate } from 'iterare';
|
|
import { RouteInfoPathExtractor } from './route-info-path-extractor.js';
|
|
import { RoutesMapper } from './routes-mapper.js';
|
|
import { filterMiddleware } from './utils.js';
|
|
import type { HttpServer, MiddlewareConsumer, Type } from '@nestjs/common';
|
|
import {
|
|
type MiddlewareConfigProxy,
|
|
type MiddlewareConfiguration,
|
|
type RouteInfo,
|
|
stripEndSlash,
|
|
} from '@nestjs/common/internal';
|
|
|
|
export class MiddlewareBuilder implements MiddlewareConsumer {
|
|
private readonly middlewareCollection = new Set<MiddlewareConfiguration>();
|
|
|
|
constructor(
|
|
private readonly routesMapper: RoutesMapper,
|
|
private readonly httpAdapter: HttpServer,
|
|
private readonly routeInfoPathExtractor: RouteInfoPathExtractor,
|
|
) {}
|
|
|
|
public apply(
|
|
...middleware: Array<Type<any> | Function | Array<Type<any> | Function>>
|
|
): MiddlewareConfigProxy {
|
|
return new MiddlewareBuilder.ConfigProxy(
|
|
this,
|
|
middleware.flat(),
|
|
this.routeInfoPathExtractor,
|
|
);
|
|
}
|
|
|
|
public build(): MiddlewareConfiguration[] {
|
|
return [...this.middlewareCollection];
|
|
}
|
|
|
|
public getHttpAdapter(): HttpServer {
|
|
return this.httpAdapter;
|
|
}
|
|
|
|
private static readonly ConfigProxy = class implements MiddlewareConfigProxy {
|
|
private excludedRoutes: RouteInfo[] = [];
|
|
|
|
constructor(
|
|
private readonly builder: MiddlewareBuilder,
|
|
private readonly middleware: Array<Type<any> | Function>,
|
|
private routeInfoPathExtractor: RouteInfoPathExtractor,
|
|
) {}
|
|
|
|
public getExcludedRoutes(): RouteInfo[] {
|
|
return this.excludedRoutes;
|
|
}
|
|
|
|
public exclude(
|
|
...routes: Array<string | RouteInfo>
|
|
): MiddlewareConfigProxy {
|
|
this.excludedRoutes = [
|
|
...this.excludedRoutes,
|
|
...this.getRoutesFlatList(routes).reduce((excludedRoutes, route) => {
|
|
for (const routePath of this.routeInfoPathExtractor.extractPathFrom(
|
|
route,
|
|
)) {
|
|
excludedRoutes.push({
|
|
...route,
|
|
path: routePath,
|
|
});
|
|
}
|
|
|
|
return excludedRoutes;
|
|
}, [] as RouteInfo[]),
|
|
];
|
|
|
|
return this;
|
|
}
|
|
|
|
public forRoutes(
|
|
...routes: Array<string | Type<any> | RouteInfo>
|
|
): MiddlewareConsumer {
|
|
const { middlewareCollection } = this.builder;
|
|
|
|
const flattedRoutes = this.getRoutesFlatList(routes);
|
|
const forRoutes = this.removeOverlappedRoutes(flattedRoutes);
|
|
const configuration = {
|
|
middleware: filterMiddleware(
|
|
this.middleware,
|
|
this.excludedRoutes,
|
|
this.builder.getHttpAdapter(),
|
|
),
|
|
forRoutes,
|
|
};
|
|
middlewareCollection.add(configuration);
|
|
return this.builder;
|
|
}
|
|
|
|
private getRoutesFlatList(
|
|
routes: Array<string | Type<any> | RouteInfo>,
|
|
): RouteInfo[] {
|
|
const { routesMapper } = this.builder;
|
|
|
|
return iterate(routes)
|
|
.map(route => routesMapper.mapRouteToRouteInfo(route))
|
|
.flatten()
|
|
.toArray();
|
|
}
|
|
|
|
private removeOverlappedRoutes(routes: RouteInfo[]) {
|
|
const regexMatchParams = /(:[^/]*)/g;
|
|
const wildcard = '([^/]*)';
|
|
const routesWithRegex = routes
|
|
.filter(route => route.path.includes(':'))
|
|
.map(route => ({
|
|
method: route.method,
|
|
path: route.path,
|
|
regex: new RegExp(
|
|
'^(' + route.path.replace(regexMatchParams, wildcard) + ')$',
|
|
'g',
|
|
),
|
|
}));
|
|
|
|
return routes.filter(route => {
|
|
const isOverlapped = (item: { regex: RegExp } & RouteInfo): boolean => {
|
|
if (route.method !== item.method) {
|
|
return false;
|
|
}
|
|
const normalizedRoutePath = stripEndSlash(route.path);
|
|
return (
|
|
normalizedRoutePath !== item.path &&
|
|
item.regex.test(normalizedRoutePath)
|
|
);
|
|
};
|
|
const routeMatch = routesWithRegex.find(isOverlapped);
|
|
return routeMatch === undefined;
|
|
});
|
|
}
|
|
};
|
|
}
|