feat: 添加扫描模块

master
luoer 2023-09-13 17:41:51 +08:00
parent 868769880e
commit 69976a67f8
11 changed files with 7127 additions and 1265 deletions

6
.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"printWidth": 120,
"singleQuote": true,
"trailingComma": "all",
"endOfLine": "auto"
}

View File

@ -1,3 +1,4 @@
## 介绍
一个NestJS起始模板正在优化中。 一个NestJS起始模板正在优化中。

5777
graph.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -23,83 +23,72 @@
"orm": "typeorm-ts-node-esm -d ./src/database/datasource/index.ts", "orm": "typeorm-ts-node-esm -d ./src/database/datasource/index.ts",
"g": "plop --plopfile scripts/template/index.js" "g": "plop --plopfile scripts/template/index.js"
}, },
"prettier": {
"printWidth": 120,
"singleQuote": true,
"trailingComma": "all",
"endOfLine": "auto"
},
"dependencies": { "dependencies": {
"@nestjs/axios": "^3.0.0", "@nestjs/axios": "^3.0.0",
"@nestjs/common": "^9.0.0", "@nestjs/common": "^9.4.3",
"@nestjs/core": "^9.0.0", "@nestjs/core": "^9.4.3",
"@nestjs/platform-express": "^9.0.0", "@nestjs/serve-static": "^3.0.1",
"@nestjs/swagger": "^6.3.0",
"@nestjs/typeorm": "^9.0.1",
"@nestjs/config": "^2.3.4",
"@nestjs/jwt": "^10.1.1",
"@nestjs/platform-express": "^9.4.3",
"axios": "^1.5.0", "axios": "^1.5.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rxjs": "^7.2.0", "dayjs": "^1.11.9",
"ua-parser-js": "^1.0.36" "dotenv": "^16.3.1",
"rxjs": "^7.8.1",
"typeorm": "^0.3.17",
"typeorm-naming-strategies": "^4.1.0",
"ua-parser-js": "^1.0.36",
"uuid": "^9.0.1",
"winston": "^3.10.0",
"nodemailer": "^6.9.5",
"mysql2": "^3.6.1",
"winston-daily-rotate-file": "^4.7.1"
}, },
"devDependencies": { "devDependencies": {
"@nestjs/cache-manager": "^2.1.0", "@nestjs/cache-manager": "^2.1.0",
"@nestjs/cli": "^9.0.0", "@nestjs/cli": "^9.5.0",
"@nestjs/config": "^2.3.1", "@nestjs/schematics": "^9.2.0",
"@nestjs/devtools-integration": "^0.1.4", "@nestjs/testing": "^9.4.3",
"@nestjs/jwt": "^10.0.3", "@types/express": "^4.17.17",
"@nestjs/passport": "^9.0.3",
"@nestjs/schematics": "^9.0.0",
"@nestjs/serve-static": "^3.0.1",
"@nestjs/swagger": "^6.3.0",
"@nestjs/testing": "^9.0.0",
"@nestjs/typeorm": "^9.0.1",
"@types/express": "^4.17.13",
"@types/jest": "28.1.4", "@types/jest": "28.1.4",
"@types/lodash": "^4.14.192", "@types/lodash": "^4.14.198",
"@types/lodash-es": "^4.17.7", "@types/lodash-es": "^4.17.9",
"@types/mockjs": "^1.0.7", "@types/mockjs": "^1.0.7",
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
"@types/node": "^16.0.0", "@types/node": "^16.18.50",
"@types/nodemailer": "^6.4.9", "@types/nodemailer": "^6.4.10",
"@types/supertest": "^2.0.11", "@types/supertest": "^2.0.12",
"@types/uuid": "^9.0.1", "@types/uuid": "^9.0.3",
"@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.0.0", "@typescript-eslint/parser": "^5.62.0",
"cache-manager": "^5.2.3", "cache-manager": "^5.2.3",
"cache-manager-redis-store": "^3.0.1", "cache-manager-redis-store": "^3.0.1",
"class-transformer": "^0.5.1", "eslint": "^8.49.0",
"class-validator": "^0.14.0", "eslint-config-prettier": "^8.10.0",
"dayjs": "^1.11.7", "eslint-plugin-prettier": "^4.2.1",
"dotenv": "^16.0.3",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "28.1.2", "jest": "28.1.2",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"multer": "1.4.5-lts.1", "multer": "1.4.5-lts.1",
"mysql2": "^3.2.0",
"nodemailer": "^6.9.4",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"plop": "^3.1.2", "plop": "^3.1.2",
"prettier": "^2.3.2", "prettier": "^2.8.8",
"redis": "^4.6.7", "redis": "^4.6.8",
"source-map-support": "^0.5.20", "source-map-support": "^0.5.21",
"sqlite3": "^5.1.6", "sqlite3": "^5.1.6",
"supertest": "^6.1.3", "supertest": "^6.3.3",
"ts-jest": "28.0.5", "ts-jest": "28.0.5",
"ts-loader": "^9.2.3", "ts-loader": "^9.4.4",
"ts-node": "^10.0.0", "ts-node": "^10.9.1",
"tsconfig-paths": "4.0.0", "tsconfig-paths": "4.0.0",
"typeorm": "^0.3.12", "typescript": "^4.9.5",
"typeorm-naming-strategies": "^4.1.0", "webpack": "^5.88.2"
"typescript": "^4.3.5",
"uuid": "^9.0.0",
"webpack": "5",
"winston": "^3.10.0",
"winston-daily-rotate-file": "^4.7.1"
}, },
"jest": { "jest": {
"moduleFileExtensions": [ "moduleFileExtensions": [

File diff suppressed because it is too large Load Diff

View File

@ -16,11 +16,12 @@ module.exports = function main(/** @type { import('plop').NodePlopAPI } */ plop)
}); });
plop.setGenerator('module', { plop.setGenerator('module', {
description: '创建一个新的模块',
prompts: [ prompts: [
{ {
name: 'name', name: 'name',
message: '请输入模块名称',
type: 'input', type: 'input',
message: '请输入模块名称(支持嵌套,如: x/y)',
validate: (input) => { validate: (input) => {
if (/^(\w+\/)*(\w+)$/.test(input)) { if (/^(\w+\/)*(\w+)$/.test(input)) {
return true; return true;
@ -30,8 +31,8 @@ module.exports = function main(/** @type { import('plop').NodePlopAPI } */ plop)
}, },
{ {
name: 'cnName', name: 'cnName',
message: '请输入模块中文名称',
type: 'input', type: 'input',
message: '请输入模块中文名称',
}, },
], ],
actions: [ actions: [

View File

@ -13,9 +13,14 @@ import { UserModule } from '@/modules/user';
import { ResponseModule } from '@/common/response'; import { ResponseModule } from '@/common/response';
import { SerializationModule } from '@/common/serialization'; import { SerializationModule } from '@/common/serialization';
import { CacheModule } from './common/cache'; import { CacheModule } from './common/cache';
import { ScanModule } from './utils/scan.module';
@Module({ @Module({
imports: [ imports: [
/**
*
*/
ScanModule.forRoot(),
/** /**
* () * ()
* @description .env * @description .env

View File

@ -4,12 +4,13 @@ import { initSwagger } from '@/common/swagger';
import { LoggerService } from '@/common/logger'; import { LoggerService } from '@/common/logger';
import { ConfigService } from '@/config'; import { ConfigService } from '@/config';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
import { ScanModule } from './utils/scan.module';
async function bootstrap() { async function bootstrap() {
/** /**
* *
*/ */
const app = await NestFactory.create(AppModule, { bufferLogs: false }); const app = await NestFactory.create(AppModule, { bufferLogs: false, snapshot: ScanModule.enable });
/** /**
* *
*/ */
@ -42,6 +43,10 @@ async function bootstrap() {
* *
*/ */
await app.listen(config.port, config.host); await app.listen(config.port, config.host);
/**
*
*/
ScanModule.scan(app);
/** /**
* URL * URL
*/ */

View File

@ -1,7 +1,7 @@
import { BaseController } from '@/common/base'; import { BaseController } from '@/common/base';
import { Respond, RespondType } from '@/common/response'; import { Respond, RespondType } from '@/common/response';
import { Body, Controller, Delete, Get, Param, Patch, Post, Query, ParseIntPipe } from '@nestjs/common'; import { Body, Controller, Delete, Get, Param, Patch, Post, Query, ParseIntPipe } from '@nestjs/common';
import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
import { CreateLogDto } from './dto/create-log.dto'; import { CreateLogDto } from './dto/create-log.dto';
import { FindLogDto } from './dto/find-log.dto'; import { FindLogDto } from './dto/find-log.dto';
import { UpdateLogDto } from './dto/update-log.dto'; import { UpdateLogDto } from './dto/update-log.dto';
@ -36,6 +36,7 @@ export class LogController extends BaseController {
@Get('login') @Get('login')
@Respond(RespondType.PAGINATION) @Respond(RespondType.PAGINATION)
@ApiOkResponse({ isArray: true, type: LoginLog }) @ApiOkResponse({ isArray: true, type: LoginLog })
@ApiOperation({ description: '分页查询登陆日志', operationId: 'getLoginLogs' })
getLoginLogs(@Query() query: FindLogDto) { getLoginLogs(@Query() query: FindLogDto) {
return this.logService.findMany(query); return this.logService.findMany(query);
} }

1
src/types/env.d.ts vendored
View File

@ -10,6 +10,7 @@ declare module 'express' {
interface Request { interface Request {
user?: { user?: {
id: number; id: number;
token: string;
username: string; username: string;
nickname: string; nickname: string;
}; };

76
src/utils/scan.module.ts Normal file
View File

@ -0,0 +1,76 @@
import { DynamicModule, INestApplication, Module } from '@nestjs/common';
import { DiscoveryModule, DiscoveryService, MetadataScanner, NestContainer } from '@nestjs/core';
import {
Entrypoint,
HttpEntrypointMetadata,
MiddlewareEntrypointMetadata,
} from '@nestjs/core/inspector/interfaces/entrypoint.interface';
@Module({})
export class ScanModule {
/**
*
*/
static enable = Boolean(process.env.xx);
/**
*
*/
static forRoot(): DynamicModule {
return {
module: ScanModule,
imports: ScanModule.enable ? [DiscoveryModule] : undefined,
};
}
/**
* /
* @param app
*/
static scan(app: INestApplication) {
if (!ScanModule.enable) {
return;
}
const container = (app as any).container as NestContainer;
const graph1 = container.serializedGraph.toJSON();
const entries = Object.values(graph1.entrypoints).flat();
for (const entry of entries) {
if (ScanModule.isHttpEntryPoint(entry)) {
console.log(entry.metadata.path);
}
if (ScanModule.isMiddlewareEntryPoint(entry)) {
console.log(entry.metadata.path);
}
}
const scanner = app.get(MetadataScanner);
const discover = app.get(DiscoveryService);
const controllers = discover.getControllers();
for (const wrapper of controllers) {
const prototype = Object.getPrototypeOf(wrapper.instance);
const methodNames = scanner.getAllMethodNames(prototype);
console.log('method names:', methodNames);
const metakeys = Reflect.getMetadataKeys(prototype);
console.log('controller meta keys:', metakeys);
for (const methodName of methodNames) {
const metadata = Reflect.getMetadataKeys(prototype[methodName]);
console.log('method meta keys:', metadata);
}
}
}
/**
* HTTP
*/
static isHttpEntryPoint(input: Entrypoint<unknown>): input is Entrypoint<HttpEntrypointMetadata> {
return input.type === 'http-endpoint';
}
/**
*
*/
static isMiddlewareEntryPoint(input: Entrypoint<unknown>): input is Entrypoint<MiddlewareEntrypointMetadata> {
return input.type === 'middleware';
}
}