add route module
This commit is contained in:
parent
fc9b8f7f2a
commit
a5b7a9bd5d
@ -1,4 +1,5 @@
|
|||||||
import { createApp, listened, startApp } from './modules/core/helpers/app';
|
import { createApp, startApp } from './modules/core/helpers/app';
|
||||||
|
import { listened } from './modules/restful/utils';
|
||||||
import { createOptions } from './options';
|
import { createOptions } from './options';
|
||||||
|
|
||||||
startApp(createApp(createOptions), listened);
|
startApp(createApp(createOptions), listened);
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import { BadGatewayException, Global, Module, ModuleMetadata, Type } from '@nestjs/common';
|
import { BadGatewayException, Global, Module, ModuleMetadata, Type } from '@nestjs/common';
|
||||||
|
|
||||||
import { APP_FILTER, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
|
import { APP_FILTER, APP_INTERCEPTOR, APP_PIPE } from '@nestjs/core';
|
||||||
import { NestFastifyApplication } from '@nestjs/platform-fastify';
|
|
||||||
import chalk from 'chalk';
|
|
||||||
import { useContainer } from 'class-validator';
|
import { useContainer } from 'class-validator';
|
||||||
|
|
||||||
import { isNil, omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
|
|
||||||
import { ConfigModule } from '@/modules/config/config.module';
|
import { ConfigModule } from '@/modules/config/config.module';
|
||||||
import { Configure } from '@/modules/config/configure';
|
import { Configure } from '@/modules/config/configure';
|
||||||
@ -104,20 +102,3 @@ export async function startApp(
|
|||||||
const { port, host } = await configure.get<AppConfig>('app');
|
const { port, host } = await configure.get<AppConfig>('app');
|
||||||
await container.listen(port, host, listened(app, startTime));
|
await container.listen(port, host, listened(app, startTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function echoApi(configure: Configure, container: NestFastifyApplication) {
|
|
||||||
const appUrl = await configure.get<string>('app.url');
|
|
||||||
const urlPrefix = await configure.get<string>('api.prefix', undefined);
|
|
||||||
const apiUrl = isNil(urlPrefix)
|
|
||||||
? appUrl
|
|
||||||
: `${appUrl}${urlPrefix.length > 0 ? `/${urlPrefix}` : urlPrefix}`;
|
|
||||||
console.log(`- RestAPI: ${chalk.green.underline(apiUrl)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const listened: (app: App, startyTime: Date) => () => Promise<void> =
|
|
||||||
({ configure, container }, startTime) =>
|
|
||||||
async () => {
|
|
||||||
console.log();
|
|
||||||
await echoApi(configure, container);
|
|
||||||
console.log('used time: ', chalk.cyan(`${new Date().getTime() - startTime.getTime()}`));
|
|
||||||
};
|
|
||||||
|
24
src/modules/restful/restful.module.ts
Normal file
24
src/modules/restful/restful.module.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { DynamicModule } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { Configure } from '../config/configure';
|
||||||
|
|
||||||
|
import { Restful } from './restful';
|
||||||
|
|
||||||
|
export class RestfulModule {
|
||||||
|
static async forRoot(configure: Configure): Promise<DynamicModule> {
|
||||||
|
const restful = new Restful(configure);
|
||||||
|
await restful.create(await configure.get('api'));
|
||||||
|
return {
|
||||||
|
module: RestfulModule,
|
||||||
|
global: true,
|
||||||
|
imports: restful.getModuleImports(),
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: Restful,
|
||||||
|
useValue: restful,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
exports: [Restful],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,18 @@
|
|||||||
import { Type } from '@nestjs/common';
|
import { INestApplication, Type } from '@nestjs/common';
|
||||||
import { RouterModule } from '@nestjs/core';
|
import { RouterModule } from '@nestjs/core';
|
||||||
|
|
||||||
import { omit } from 'lodash';
|
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||||
|
import { omit, trim } from 'lodash';
|
||||||
|
|
||||||
import { BaseRestful } from './base';
|
import { BaseRestful } from './base';
|
||||||
import { ApiConfig, ApiDocOption, ApiDocSource, RouteOption, SwaggerOption } from './types';
|
import {
|
||||||
|
ApiConfig,
|
||||||
|
ApiDocOption,
|
||||||
|
ApiDocSource,
|
||||||
|
RouteOption,
|
||||||
|
SwaggerOption,
|
||||||
|
VersionOption,
|
||||||
|
} from './types';
|
||||||
import { trimPath } from './utils';
|
import { trimPath } from './utils';
|
||||||
|
|
||||||
export class Restful extends BaseRestful {
|
export class Restful extends BaseRestful {
|
||||||
@ -71,6 +79,80 @@ export class Restful extends BaseRestful {
|
|||||||
(module) => !excludeModules.find((emodule) => emodule === module),
|
(module) => !excludeModules.find((emodule) => emodule === module),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected getDocOption(name: string, voption: VersionOption, isDefault = false) {
|
||||||
|
const docConfig: ApiDocOption = {};
|
||||||
|
const defaultDoc = {
|
||||||
|
title: voption.title!,
|
||||||
|
description: voption.description!,
|
||||||
|
tags: voption.tags ?? [],
|
||||||
|
auth: voption.auth ?? false,
|
||||||
|
version: name,
|
||||||
|
path: trim(`${this.config.docuri}${isDefault ? '' : `/${name}`}`, '/'),
|
||||||
|
};
|
||||||
|
|
||||||
|
const routesDoc = isDefault
|
||||||
|
? this.getRouteDocs(defaultDoc, voption.routes ?? [])
|
||||||
|
: this.getRouteDocs(defaultDoc, voption.routes ?? [], name);
|
||||||
|
if (Object.keys(routesDoc).length > 0) {
|
||||||
|
docConfig.routes = routesDoc;
|
||||||
|
}
|
||||||
|
const routeModules = isDefault
|
||||||
|
? this.getRouteModules(voption.routes ?? [])
|
||||||
|
: this.getRouteModules(voption.routes ?? [], name);
|
||||||
|
const include = this.filterExcludeModules(routeModules);
|
||||||
|
if (include.length > 0 || !docConfig.routes) {
|
||||||
|
docConfig.default = { ...defaultDoc, include };
|
||||||
|
}
|
||||||
|
return docConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createDocs() {
|
||||||
|
const versionMaps = Object.entries(this.config.versions);
|
||||||
|
const vDocs = versionMaps.map(([name, version]) => [
|
||||||
|
name,
|
||||||
|
this.getDocOption(name, version),
|
||||||
|
]);
|
||||||
|
this._docs = Object.fromEntries(vDocs);
|
||||||
|
const defaultVersion = this.config.versions[this._default];
|
||||||
|
this._docs.default = this.getDocOption(this._default, defaultVersion, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
async factoryDocs<T extends INestApplication>(container: T) {
|
||||||
|
const docs = Object.values(this._docs)
|
||||||
|
.map((doc) => [doc.default, ...Object.values(doc.routes ?? [])])
|
||||||
|
.reduce((o, n) => [...o, ...n], [])
|
||||||
|
.filter((i) => !!i);
|
||||||
|
|
||||||
|
for (const voption of docs) {
|
||||||
|
const { title, description, version, auth, include, tags } = voption!;
|
||||||
|
const builder = new DocumentBuilder();
|
||||||
|
if (title) {
|
||||||
|
builder.setTitle(title);
|
||||||
|
}
|
||||||
|
if (description) {
|
||||||
|
builder.setDescription(description);
|
||||||
|
}
|
||||||
|
if (auth) {
|
||||||
|
builder.addBearerAuth();
|
||||||
|
}
|
||||||
|
if (tags) {
|
||||||
|
tags.forEach((tag) =>
|
||||||
|
typeof tag === 'string'
|
||||||
|
? builder.addTag(tag)
|
||||||
|
: builder.addTag(tag.name, tag.description, tag.externalDocs),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
builder.setVersion(version);
|
||||||
|
|
||||||
|
const document = SwaggerModule.createDocument(container, builder.build(), {
|
||||||
|
include: include.length > 0 ? include : [() => undefined as any],
|
||||||
|
ignoreGlobalPrefix: true,
|
||||||
|
deepScanRoutes: true,
|
||||||
|
});
|
||||||
|
SwaggerModule.setup(voption!.path, container, document);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function genDocPath(routePath: string, prefix?: string, version?: string) {
|
export function genDocPath(routePath: string, prefix?: string, version?: string) {
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
import { Type } from '@nestjs/common';
|
import { Type } from '@nestjs/common';
|
||||||
import { Routes, RouteTree } from '@nestjs/core';
|
import { Routes, RouteTree } from '@nestjs/core';
|
||||||
|
import { NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import chalk from 'chalk';
|
||||||
import { camelCase, isNil, omit, trim, upperFirst } from 'lodash';
|
import { camelCase, isNil, omit, trim, upperFirst } from 'lodash';
|
||||||
|
|
||||||
import { Configure } from '../config/configure';
|
import { Configure } from '../config/configure';
|
||||||
|
|
||||||
import { CreateModule } from '../core/helpers';
|
import { CreateModule } from '../core/helpers';
|
||||||
|
|
||||||
|
import { App } from '../core/types';
|
||||||
|
|
||||||
import { CONTROLLER_DEPENDS } from './constants';
|
import { CONTROLLER_DEPENDS } from './constants';
|
||||||
import { RouteOption } from './types';
|
import { Restful } from './restful';
|
||||||
|
import { ApiDocOption, RouteOption } from './types';
|
||||||
|
|
||||||
export const trimPath = (routePath: string, addPrefix = true) =>
|
export const trimPath = (routePath: string, addPrefix = true) =>
|
||||||
`${addPrefix ? '/' : ''}${trim(routePath.replace('//', '/'), '/')}`;
|
`${addPrefix ? '/' : ''}${trim(routePath.replace('//', '/'), '/')}`;
|
||||||
@ -84,3 +89,53 @@ export function createRouteModuleTree(
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function echoApi(configure: Configure, container: NestFastifyApplication) {
|
||||||
|
const appUrl = await configure.get<string>('app.url');
|
||||||
|
const urlPrefix = await configure.get<string>('api.prefix', undefined);
|
||||||
|
const apiUrl = isNil(urlPrefix)
|
||||||
|
? appUrl
|
||||||
|
: `${appUrl}${urlPrefix.length > 0 ? `/${urlPrefix}` : urlPrefix}`;
|
||||||
|
console.log(`- RestAPI: ${chalk.green.underline(apiUrl)}`);
|
||||||
|
console.log('- RestDocs');
|
||||||
|
const factory = container.get(Restful);
|
||||||
|
const { default: defaultDoc, ...docs } = factory.docs;
|
||||||
|
await echoApiDocs('default', defaultDoc, appUrl);
|
||||||
|
for (const [name, doc] of Object.entries(docs)) {
|
||||||
|
console.log();
|
||||||
|
echoApiDocs(name, doc, appUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const listened: (app: App, startyTime: Date) => () => Promise<void> =
|
||||||
|
({ configure, container }, startTime) =>
|
||||||
|
async () => {
|
||||||
|
console.log();
|
||||||
|
await echoApi(configure, container);
|
||||||
|
console.log('used time: ', chalk.cyan(`${new Date().getTime() - startTime.getTime()}`));
|
||||||
|
};
|
||||||
|
|
||||||
|
async function echoApiDocs(name: string, doc: ApiDocOption, appUrl: string) {
|
||||||
|
const getDocPath = (path: string) => `${appUrl}/${path}`;
|
||||||
|
if (!doc.routes && doc.default) {
|
||||||
|
console.log(
|
||||||
|
`[${chalk.blue(name.toUpperCase())}]:${chalk.green.underline(
|
||||||
|
getDocPath(doc.default.path),
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.log(`[${chalk.blue(name.toUpperCase())}]`);
|
||||||
|
if (doc.default) {
|
||||||
|
console.log(`default:${chalk.green.underline(getDocPath(doc.default.path))}`);
|
||||||
|
}
|
||||||
|
if (doc.routes) {
|
||||||
|
Object.entries(doc.routes).forEach(([routeName, docs]) => {
|
||||||
|
console.log(
|
||||||
|
`<${chalk.yellowBright.bold(docs.title)}>: ${chalk.green.underline(
|
||||||
|
getDocPath(docs.path),
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user