Compare commits

...

2 Commits

  1. 1
      .gitignore
  2. 17
      config/development.ts
  3. 12
      config/index.ts
  4. 17
      config/production.ts
  5. 10
      config/test.ts
  6. 8
      package.json
  7. 43
      pnpm-lock.yaml
  8. 5
      src/Logger/logger.module.ts
  9. 20
      src/Logger/logger.service.spec.ts
  10. 66
      src/Logger/logger.service.ts
  11. 17
      src/app.module.ts
  12. 7
      src/devInterceptor/devInterceptor.module.ts
  13. 18
      src/devInterceptor/devInterceptor.service.spec.ts
  14. 34
      src/devInterceptor/devInterceptor.service.ts
  15. 50
      src/main.ts
  16. 7
      src/response-interceptor/response-interceptor.module.ts
  17. 18
      src/response-interceptor/response-interceptor.service.spec.ts
  18. 4
      src/response-interceptor/response-interceptor.service.ts
  19. 10
      src/starlight/starlight.controller.ts
  20. 6
      src/starlight/starlight.guard.spec.ts
  21. 22
      src/starlight/starlight.guard.ts
  22. 3
      src/starlight/starlight.module.ts
  23. 20
      src/starlight/starlight.service.spec.ts
  24. 6
      src/starlight/starlight.service.ts
  25. 28
      test/app.e2e-spec.ts

1
.gitignore vendored

@ -1,6 +1,7 @@
# compiled output
/dist
/node_modules
/Log/*
# Logs
logs

@ -0,0 +1,17 @@
export default {
// 主服务
master: {
host: '0.0.0.0',
port: '3000',
},
swagger: {
enable: true,
},
dev: {
// 开发模式拦截器,用以提供接口请求时间和响应大小
devInterceptor: true,
},
log: {
level: 'all',
},
};

@ -0,0 +1,12 @@
import developmentConfig from './development';
import testConfig from './test';
import productionConfig from './production';
const configs = {
development: developmentConfig,
test: testConfig,
production: productionConfig,
};
const env = process.env.NODE_ENV || 'development';
export default () => configs[env];

@ -0,0 +1,17 @@
export default {
// 主服务
master: {
host: '127.0.0.1',
port: '3000',
},
swagger: {
enable: false,
},
dev: {
// 开发模式拦截器,用以提供接口请求时间和响应大小
devInterceptor: false,
},
log: {
level: 'info',
},
};

@ -0,0 +1,10 @@
export default {
// 主服务
master: {
host: '0.0.0.0',
port: '3000',
},
swagger: {
enable: true,
},
};

@ -8,9 +8,9 @@
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start": "cross-env NODE_ENV=production nest start",
"start:dev": "cross-env NODE_ENV=development nest start --watch",
"start:debug": "cross-env NODE_ENV=development nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
@ -23,6 +23,7 @@
"@fastify/multipart": "^7.4.2",
"@fastify/static": "^6.9.0",
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.3.1",
"@nestjs/core": "^9.0.0",
"@nestjs/mapped-types": "*",
"@nestjs/platform-fastify": "^9.3.9",
@ -44,6 +45,7 @@
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"cross-env": "^7.0.3",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",

@ -5,6 +5,7 @@ specifiers:
'@fastify/static': ^6.9.0
'@nestjs/cli': ^9.0.0
'@nestjs/common': ^9.0.0
'@nestjs/config': ^2.3.1
'@nestjs/core': ^9.0.0
'@nestjs/mapped-types': '*'
'@nestjs/platform-fastify': ^9.3.9
@ -19,6 +20,7 @@ specifiers:
'@typescript-eslint/parser': ^5.0.0
class-transformer: ^0.5.1
class-validator: ^0.14.0
cross-env: ^7.0.3
eslint: ^8.0.1
eslint-config-prettier: ^8.3.0
eslint-plugin-prettier: ^4.0.0
@ -40,6 +42,7 @@ dependencies:
'@fastify/multipart': 7.4.2
'@fastify/static': 6.9.0
'@nestjs/common': 9.3.9_welcnyot5bzd5wa2aovbkxpi4i
'@nestjs/config': 2.3.1_jrq2rdgfp2sx67wmylmrqliwxe
'@nestjs/core': 9.3.9_jrq2rdgfp2sx67wmylmrqliwxe
'@nestjs/mapped-types': 1.2.2_sm5si6oczf3vcbfl6qa276t67m
'@nestjs/platform-fastify': 9.3.9_tdsjr37n247zbypv5fnzqwaj2q
@ -61,6 +64,7 @@ devDependencies:
'@types/supertest': 2.0.12
'@typescript-eslint/eslint-plugin': 5.54.1_4rfaf6mlw2mmutqjcopwvbftpu
'@typescript-eslint/parser': 5.54.1_vgl77cfdswitgr47lm5swmv43m
cross-env: 7.0.3
eslint: 8.36.0
eslint-config-prettier: 8.7.0_eslint@8.36.0
eslint-plugin-prettier: 4.2.1_eqzx3hpkgx5nnvxls3azrcc7dm
@ -1032,6 +1036,22 @@ packages:
tslib: 2.5.0
uid: 2.0.1
/@nestjs/config/2.3.1_jrq2rdgfp2sx67wmylmrqliwxe:
resolution: {integrity: sha512-Ckzel0NZ9CWhNsLfE1hxfDuxJuEbhQvGxSlmZ1/X8awjRmAA/g3kT6M1+MO1SHj1wMtPyUfd9WpwkiqFbiwQgA==}
peerDependencies:
'@nestjs/common': ^7.0.0 || ^8.0.0 || ^9.0.0
reflect-metadata: ^0.1.13
rxjs: ^6.0.0 || ^7.2.0
dependencies:
'@nestjs/common': 9.3.9_welcnyot5bzd5wa2aovbkxpi4i
dotenv: 16.0.3
dotenv-expand: 10.0.0
lodash: 4.17.21
reflect-metadata: 0.1.13
rxjs: 7.8.0
uuid: 9.0.0
dev: false
/@nestjs/core/9.3.9_jrq2rdgfp2sx67wmylmrqliwxe:
resolution: {integrity: sha512-9g1A1G9eirLXEpH21rc6dKb08zHc2+adhCRz8NW39hbejcsxxD72FApJzt4QBQAKvu862ixt/tdpStnFT7lOSw==}
requiresBuild: true
@ -2223,6 +2243,14 @@ packages:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
dev: true
/cross-env/7.0.3:
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
hasBin: true
dependencies:
cross-spawn: 7.0.3
dev: true
/cross-spawn/7.0.3:
resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
engines: {node: '>= 8'}
@ -2333,6 +2361,16 @@ packages:
esutils: 2.0.3
dev: true
/dotenv-expand/10.0.0:
resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==}
engines: {node: '>=12'}
dev: false
/dotenv/16.0.3:
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
engines: {node: '>=12'}
dev: false
/ee-first/1.1.1:
resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
dev: false
@ -5155,6 +5193,11 @@ packages:
/util-deprecate/1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
/uuid/9.0.0:
resolution: {integrity: sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==}
hasBin: true
dev: false
/v8-compile-cache-lib/3.0.1:
resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==}
dev: true

@ -1,8 +1,9 @@
import { Module } from '@nestjs/common';
import { LoggerService } from './logger.service';
import { ConfigService } from "@nestjs/config";
@Module({
providers: [LoggerService],
providers: [LoggerService, ConfigService],
exports: [LoggerService],
})
export class LoggerModule {}
export class Logger {}

@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing';
import { LoggerService } from './logger.service';
describe('LoggerService', () => {
let service: LoggerService;
let service: LoggerService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [LoggerService],
}).compile();
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [LoggerService],
}).compile();
service = module.get<LoggerService>(LoggerService);
});
service = module.get<LoggerService>(LoggerService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

@ -1,12 +1,20 @@
import { ConsoleLogger } from '@nestjs/common';
import { Injectable, Scope, ConsoleLogger } from '@nestjs/common';
import { Logger as log4jsLogger, configure, getLogger } from 'log4js';
import config from '../../config';
@Injectable({ scope: Scope.TRANSIENT })
export class LoggerService extends ConsoleLogger {
defaultLog: log4jsLogger;
fileLog: log4jsLogger;
context = '';
constructor(logsDir: string) {
constructor(context = '') {
super();
const logConfig = config().log;
if (context) {
this.context = context;
}
// this.configService
configure({
appenders: {
default: {
@ -17,7 +25,7 @@ export class LoggerService extends ConsoleLogger {
},
},
file: {
filename: `${logsDir}/nestjs.services.log`,
filename: `Log/nestjs.services.log`,
type: 'dateFile',
// 配置 layout,此处使用自定义模式 pattern
layout: {
@ -37,12 +45,12 @@ export class LoggerService extends ConsoleLogger {
categories: {
default: {
appenders: ['default'],
level: 'all',
level: logConfig.level || 'all',
enableCallStack: true,
},
fileAll: {
appenders: ['file'],
level: 'all',
level: logConfig.level || 'all',
enableCallStack: true,
},
},
@ -52,33 +60,47 @@ export class LoggerService extends ConsoleLogger {
this.fileLog = getLogger('fileAll');
}
log(...arg: any[]) {
// super.log(message, optionalParams);
this.defaultLog.info(arg.join(' '));
this.fileLog.info(arg.join(' '));
log(message: any, ...optionalParams: any[]) {
const context = this.context;
this.defaultLog.info(context, message, ...optionalParams);
this.fileLog.info(context, message, ...optionalParams);
}
error(...arg: any[]) {
// super.error(message, trace);
this.defaultLog.error(arg.join(' '));
this.fileLog.error(arg.join(' '));
info(message: any, ...optionalParams: any[]) {
const context = this.context;
this.defaultLog.info(context, message, ...optionalParams);
this.fileLog.info(context, message, ...optionalParams);
}
warn(...arg: any[]) {
error(message: any, ...optionalParams: any[]) {
const context = this.context;
this.defaultLog.error(context, message, ...optionalParams);
this.fileLog.error(context, message, ...optionalParams);
}
warn(message: any, ...optionalParams: any[]) {
// super.warn(message, trace);
this.defaultLog.warn(arg.join(' '));
this.fileLog.warn(arg.join(' '));
const context = this.context;
this.defaultLog.warn(context, message, ...optionalParams);
this.fileLog.warn(context, message, ...optionalParams);
}
debug(...arg: any[]) {
debug(message: any, ...optionalParams: any[]) {
// super.debug(message, trace);
this.defaultLog.debug(arg.join(' '));
this.fileLog.debug(arg.join(' '));
const context = this.context;
this.defaultLog.debug(context, message, ...optionalParams);
this.fileLog.debug(context, message, ...optionalParams);
}
verbose(...arg: any[]) {
verbose(message: any, ...optionalParams: any[]) {
// super.verbose(message, trace);
this.defaultLog.info(arg.join(' '));
this.fileLog.info(arg.join(' '));
const context = this.context;
this.defaultLog.info(context, message, ...optionalParams);
this.fileLog.info(context, message, ...optionalParams);
}
setContext(context: string) {
this.context = context;
this.debug('加载日志');
}
}

@ -1,9 +1,22 @@
import { Module } from '@nestjs/common';
import { StarlightModule } from './starlight/starlight.module';
import { LoggerModule } from './logger/logger.module';
import { Logger } from './logger/logger.module';
import { DevInterceptorModule } from './devInterceptor/devInterceptor.module';
import { ConfigModule } from '@nestjs/config';
import { ResponseInterceptorModule } from './response-interceptor/response-interceptor.module';
import config from '../config';
@Module({
imports: [StarlightModule, LoggerModule],
imports: [
ConfigModule.forRoot({
isGlobal: true, // 作用于全局
load: [config], // 加载自定义配置项
}),
StarlightModule,
Logger,
DevInterceptorModule,
// ResponseInterceptorModule,
],
controllers: [],
providers: [],
})

@ -0,0 +1,7 @@
import { Module } from '@nestjs/common';
import { DevInterceptorService } from './devInterceptor.service';
@Module({
providers: [DevInterceptorService],
})
export class DevInterceptorModule {}

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { DevInterceptorService } from './devInterceptor.service';
describe('GlobalInterceptorService', () => {
let service: DevInterceptorService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [DevInterceptorService],
}).compile();
service = module.get<DevInterceptorService>(DevInterceptorService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

@ -0,0 +1,34 @@
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { LoggerService } from '../Logger/logger.service';
@Injectable()
export class DevInterceptorService implements NestInterceptor {
logger: LoggerService;
constructor() {
this.logger = new LoggerService();
this.logger.setContext(`[DevlInterceptor]`);
}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
this.logger.error(request.method, request.url, '<==');
const now = Date.now();
return next.handle().pipe(
tap((data) => {
this.logger.error(
request.method,
request.url,
'==>',
`${Date.now() - now}ms`,
`${Buffer.from(data).length}b`,
);
}),
);
}
}

@ -8,6 +8,9 @@ import { ValidationPipe } from '@nestjs/common';
import fastifyMultipart from '@fastify/multipart';
import { LoggerService } from './logger/logger.service';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import * as process from 'process';
import { DevInterceptorService } from './devInterceptor/devInterceptor.service';
import { ConfigService } from '@nestjs/config';
async function bootstrap() {
const app = await NestFactory.create<NestFastifyApplication>(
@ -18,31 +21,52 @@ async function bootstrap() {
logger: false,
}),
);
// 配置服务
const appConfig = app.get(ConfigService);
// 日志 Log为文件路径
const logger = new LoggerService('Log');
const logger = new LoggerService('[BOOTSTRAP]');
app.useLogger(logger);
// 全局路由前缀
app.setGlobalPrefix('api');
// 全局开启参数验证--
// 需要安装class-validator 又依赖 class-transformer
// 全局开启参数验证--需要安装class-validator 又依赖 class-transformer
app.useGlobalPipes(new ValidationPipe());
// 文件上传中间件
// fastify文件上传中间件
await app.register(fastifyMultipart);
// 开发者工具
const swaggerState = appConfig.get('swagger').enable;
Swagger(app, swaggerState, logger);
const devInterceptorState = appConfig.get('dev').devInterceptor;
DevInterceptor(app, devInterceptorState, logger);
const listenConfig = appConfig.get('master');
await app.listen(listenConfig.port, listenConfig.host);
// Swagger
logger.log(
'🚀 服务应用已经成功启动!',
`http://${listenConfig.host}:${listenConfig.port}`,
);
}
// Swagger接口测试和说明文档
function Swagger(app, state, logger) {
if (!state) return;
const config = new DocumentBuilder()
.setTitle('Vue Admin Plus 管理系统接口文档')
.setDescription('这是一份关于 Vue Admin Plus 管理系统的接口文档')
.setTitle('系统接口文档')
.setDescription('这是一份接口文档')
.setVersion('1.0.0')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('docs', app, document);
await app.listen(3000, '0.0.0.0');
logger.log(`设置应用程序端口号:3000`, 'bootstrap');
logger.log(`应用程序接口地址: http://localhost:3000/api`, 'bootstrap');
logger.log(`应用程序文档地址: http://localhost:3000/docs`, 'bootstrap');
logger.log('🚀 服务应用已经成功启动!', 'bootstrap');
logger.warn('Swagger外接程序已加载!');
logger.warn('应用程序文档地址: http://localhost:3000/docs');
}
// DevInterceptorService 输出请求处理时间和响应数据大小的拦截器
function DevInterceptor(app, state, logger) {
// 全局拦截器
if (!state) return;
app.useGlobalInterceptors(new DevInterceptorService());
logger.warn('DevInterceptorService请求响应拦截器已开启!');
}
bootstrap();

@ -0,0 +1,7 @@
import { Module } from '@nestjs/common';
import { ResponseInterceptorService } from './response-interceptor.service';
@Module({
providers: [ResponseInterceptorService]
})
export class ResponseInterceptorModule {}

@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ResponseInterceptorService } from './response-interceptor.service';
describe('ResponseInterceptorService', () => {
let service: ResponseInterceptorService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [ResponseInterceptorService],
}).compile();
service = module.get<ResponseInterceptorService>(ResponseInterceptorService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

@ -0,0 +1,4 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export class ResponseInterceptorService {}

@ -14,13 +14,19 @@ import { CreateStarlightDto } from './dto/create-starlight.dto';
import { UpdateStarlightDto } from './dto/update-starlight.dto';
import { StarlightGuard } from './starlight.guard';
import { ApiTags } from '@nestjs/swagger';
import { LoggerService } from '../Logger/logger.service';
import { ConfigService } from '@nestjs/config';
@ApiTags('starlight')
@Controller('starlight')
// 拦截器
@UseGuards(StarlightGuard)
export class StarlightController {
constructor(private readonly starlightService: StarlightService) {}
constructor(
private readonly starlightService: StarlightService,
private readonly logger: LoggerService,
private readonly config: ConfigService,
) {}
@Post()
create(@Body() createStarlightDto: CreateStarlightDto) {
@ -29,6 +35,8 @@ export class StarlightController {
@Get()
findAll() {
this.logger.error(this.config.get('master'));
this.logger.warn('xsxs');
return this.starlightService.findAll();
}

@ -1,7 +1,7 @@
import { StarlightGuard } from './starlight.guard';
describe('StarlightGuard', () => {
it('should be defined', () => {
expect(new StarlightGuard()).toBeDefined();
});
it('should be defined', () => {
expect(new StarlightGuard()).toBeDefined();
});
});

@ -5,23 +5,25 @@ import {
UnauthorizedException,
} from '@nestjs/common';
import { Observable } from 'rxjs';
// 此文件为拦截器
import { LoggerService } from '../Logger/logger.service';
// 此文件为守卫
@Injectable()
export class StarlightGuard implements CanActivate {
constructor(private readonly logger: LoggerService) {}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
console.log(context);
// console.log(context);
const request = context.switchToHttp().getRequest();
// console.log(request);
console.log(request.url);
console.log(request.headers);
// console.log(request.url);request.headers
// this.logger.info('GUARG', request.url, "TEST");
// 自定义返回消息
throw new UnauthorizedException({
statusCode: 403,
message: '你没有访问权限',
error: 'Forbidden',
});
return false;
// throw new UnauthorizedException({
// statusCode: 403,
// message: '你没有访问权限',
// error: 'Forbidden',
// });
return true;
}
}

@ -1,9 +1,10 @@
import { Module } from '@nestjs/common';
import { StarlightService } from './starlight.service';
import { StarlightController } from './starlight.controller';
import { LoggerService } from '../Logger/logger.service';
@Module({
controllers: [StarlightController],
providers: [StarlightService],
providers: [StarlightService, LoggerService],
})
export class StarlightModule {}

@ -2,17 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing';
import { StarlightService } from './starlight.service';
describe('StarlightService', () => {
let service: StarlightService;
let service: StarlightService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [StarlightService],
}).compile();
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [StarlightService],
}).compile();
service = module.get<StarlightService>(StarlightService);
});
service = module.get<StarlightService>(StarlightService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

@ -9,7 +9,11 @@ export class StarlightService {
}
findAll() {
return `This action returns all starlight`;
return JSON.stringify({
name: '何夕',
age: 32,
data: 'xsxs',
});
}
findOne(id: number) {

@ -4,21 +4,21 @@ import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});

Loading…
Cancel
Save