diff --git a/.env b/.env index 68cc99e..4d5edfd 100644 --- a/.env +++ b/.env @@ -18,3 +18,10 @@ SWAGGER_VERSION=1.0 SWAGGER_TITLE=Eoapi-remote-server API文档 SWAGGER_DESC=Eoapi remote server API document。 +# other +# eoapi web服务端口 +EOAPI_WEB_SERVER_POST=3002 +# http测试服务端口 +EOAPI_TEST_SERVER_PORT=42011 +# socket服务端口 +EOAPI_WEBSOCKET_POST=42022 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index a4e3679..38f21d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,39 +3,46 @@ # 使用 as 来为某一阶段命名 FROM node:lts-alpine as builder + +ENV PROJECT_DIR=/eoapi-remote-server \ + MYSQL_HOST=mysql \ + MYSQL_PORT=3306 + # WORKDIR指令用于设置Dockerfile中的RUN、CMD和ENTRYPOINT指令执行命令的工作目录(默认为/目录),该指令在Dockerfile文件中可以出现多次, # 如果使用相对路径则为相对于WORKDIR上一次的值, # 例如WORKDIR /data,WORKDIR logs,RUN pwd最终输出的当前目录是/data/logs。 # cd到 /eoapi-remote-server -WORKDIR /eoapi-remote-server +WORKDIR $PROJECT_DIR # set timezone -RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime -RUN echo 'Asia/Shanghai' > /etc/timezone +RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ + && echo 'Asia/Shanghai' > /etc/timezone # mirror acceleration # RUN npm config set registry https://registry.npmmirror.com # RUN yarn config set registry https://registry.npmmirror.com # RUN npm config rm proxy && npm config rm https-proxy -# install & build -COPY ./ /eoapi-remote-server -RUN chmod +x ./wait-for-it.sh -RUN apk update && apk add bash +# install +COPY package.json $PROJECT_DIR RUN yarn install -RUN yarn build +# build +COPY ./ $PROJECT_DIR +RUN chmod +x ./wait-for-it.sh \ + && apk update \ + && apk add bash \ + && yarn build \ + && yarn global add pm2 # clean dev dep # RUN rm -rf node_modules # RUN yarn install --production # RUN yarn cache clean -RUN yarn global add pm2 - # 容器对外暴露的端口号 EXPOSE 3000 # 容器启动时执行的命令,类似npm run start # CMD ["yarn", "start:prod"] # CMD ["pm2-runtime", "ecosystem.config.js"] -ENTRYPOINT ./wait-for-it.sh mysql:3306 -- yarn migration:run && pm2-runtime ecosystem.config.js \ No newline at end of file +ENTRYPOINT ./wait-for-it.sh $MYSQL_HOST:$MYSQL_PORT -- yarn migration:run && pm2-runtime ecosystem.config.js \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index 8e4b632..7a1c9c6 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -16,9 +16,6 @@ services: - 'host.docker.internal:host-gateway' ports: - '${EOAPI_SERVER_PORT}:3000' - # 当前服务启动之前先要把depends_on指定的服务启动起来才行 - depends_on: - - mysql networks: - eoapi_net @@ -29,9 +26,9 @@ services: restart_policy: condition: on-failure max_attempts: 10 - command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci #设置utf8字符集 env_file: - .env + command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci #设置utf8字符集 ports: - '${MYSQL_PORT}:3306' volumes: @@ -49,7 +46,7 @@ services: extra_hosts: - 'host.docker.internal:host-gateway' ports: - - '3002:80' + - '${EOAPI_WEB_SERVER_POST}:80' # volumes: # - ./nginx.conf:/etc/nginx/conf.d/default.conf networks: @@ -65,8 +62,8 @@ services: extra_hosts: - 'host.docker.internal:host-gateway' ports: - - '42011:4201' - - '42022:4202' + - '${EOAPI_TEST_SERVER_PORT}:4201' + - '${EOAPI_WEBSOCKET_POST}:4202' networks: - eoapi_net diff --git a/package.json b/package.json index 667aba8..4dc0956 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eoapi-remote-server", - "version": "1.9.1", + "version": "1.9.2", "description": "Storage api data in remote server", "author": "eoapi", "private": true, @@ -32,13 +32,13 @@ }, "dependencies": { "@nestjs/axios": "^1.0.0", - "@nestjs/common": "^9.1.6", + "@nestjs/common": "^9.2.0", "@nestjs/config": "^2.2.0", - "@nestjs/core": "^9.1.6", + "@nestjs/core": "^9.2.0", "@nestjs/jwt": "^9.0.0", "@nestjs/mapped-types": "*", "@nestjs/passport": "^9.0.0", - "@nestjs/platform-express": "^9.1.6", + "@nestjs/platform-express": "^9.2.0", "@nestjs/swagger": "^6.1.3", "@nestjs/typeorm": "^9.0.1", "ajv": "^8.11.0", @@ -60,22 +60,22 @@ "devDependencies": { "@nestjs/cli": "^9.1.5", "@nestjs/schematics": "^9.0.3", - "@nestjs/testing": "^9.1.6", + "@nestjs/testing": "^9.2.0", "@types/crypto-js": "^4.1.1", "@types/express": "^4.17.14", - "@types/jest": "29.2.1", + "@types/jest": "29.2.2", "@types/node": "^18.11.9", "@types/passport": "^1.0.11", "@types/passport-http": "^0.3.9", "@types/supertest": "^2.0.12", - "@typescript-eslint/eslint-plugin": "^5.42.0", - "@typescript-eslint/parser": "^5.42.0", + "@typescript-eslint/eslint-plugin": "^5.42.1", + "@typescript-eslint/parser": "^5.42.1", "dotenv": "^16.0.3", - "eslint": "^8.26.0", + "eslint": "^8.27.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-prettier": "^4.2.1", - "jest": "29.2.2", + "jest": "29.3.1", "prettier": "^2.7.1", "source-map-support": "^0.5.21", "supertest": "^6.3.1", diff --git a/src/common/class/res.class.ts b/src/common/class/res.class.ts index 39ac2fd..1b04c3b 100644 --- a/src/common/class/res.class.ts +++ b/src/common/class/res.class.ts @@ -1,26 +1,96 @@ -export class ResOp { - readonly data: any; - readonly statusCode: number; - readonly message: string; +import { applyDecorators, Type } from '@nestjs/common'; +import { + ApiExtraModels, + ApiProperty, + ApiResponse, + ApiResponseSchemaHost, + getSchemaPath, +} from '@nestjs/swagger'; - constructor(code: number, data?: any, message = 'success') { - this.statusCode = code; +class EmptyClass {} + +export class ResponseDto { + data: T; + + @ApiProperty({ default: 200 }) + statusCode: number; + + @ApiProperty({ default: 'success' }) + message: string; + + constructor(statusCode: number, data?: any, message = 'success') { + this.statusCode = statusCode; this.data = data; this.message = message; } static success(data?: any) { - return new ResOp(200, data); + return new ResponseDto(200, data); } } -export class Pagination { +export class PaginatedDto { + @ApiProperty() total: number; - page: number; - size: number; -} -export class PageResult { - list?: Array; - pagination: Pagination; + @ApiProperty() + limit: number; + + @ApiProperty() + offset: number; + + results: TData[]; } + +export const CustomApiResponse = < + DataDto extends Type, + WrapperDataDto extends Type, +>( + dataDto: DataDto, + wrapperDataDto: WrapperDataDto, + dataType = 'object', + options?: Partial, +) => + applyDecorators( + ApiExtraModels(wrapperDataDto, dataDto), + ApiResponse({ + status: 200, + schema: { + allOf: [ + { $ref: getSchemaPath(wrapperDataDto) }, + { + properties: { + data: { + type: dataType, + [dataType === 'object' ? '$ref' : 'items']: + dataType === 'object' + ? getSchemaPath(dataDto) + : { $ref: getSchemaPath(dataDto) }, + }, + }, + }, + ], + }, + ...options, + }), + ); + +export const ApiOkResponseData = >( + dataDto?: DataDto, + dataType: 'object' | 'array' = 'object', + options?: Partial, +) => CustomApiResponse(dataDto ?? EmptyClass, ResponseDto, dataType, options); + +export const ApiCreatedResponseData = >( + dataDto?: DataDto, + dataType: 'object' | 'array' = 'object', + options?: Partial, +) => + CustomApiResponse(dataDto ?? EmptyClass, ResponseDto, dataType, { + status: 201, + ...options, + }); + +export const ApiOkResponsePaginated = >( + dataDto: DataDto, +) => CustomApiResponse(dataDto, PaginatedDto); diff --git a/src/common/filters/api-exception.filter.ts b/src/common/filters/api-exception.filter.ts index b4d75fc..043a3bb 100644 --- a/src/common/filters/api-exception.filter.ts +++ b/src/common/filters/api-exception.filter.ts @@ -6,7 +6,7 @@ import { HttpStatus, } from '@nestjs/common'; import { ApiException } from '../exceptions/api.exception'; -import { ResOp } from '../class/res.class'; +import { ResponseDto } from '../class/res.class'; import { isDev } from '@/utils'; const errorTips = { @@ -41,7 +41,7 @@ export class ApiExceptionFilter implements ExceptionFilter { message = exception instanceof HttpException ? exception.message : `${exception}`; // } - const result = new ResOp(code, null, message); + const result = new ResponseDto(code, null, message); response.status(status).send(result); } } diff --git a/src/common/interceptors/api-transform.interceptor.ts b/src/common/interceptors/api-transform.interceptor.ts index eca3336..51e40fc 100644 --- a/src/common/interceptors/api-transform.interceptor.ts +++ b/src/common/interceptors/api-transform.interceptor.ts @@ -3,7 +3,7 @@ import { Reflector } from '@nestjs/core'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { TRANSFORM_KEEP_KEY_METADATA } from '../contants/decorator.contants'; -import { ResOp } from '../class/res.class'; +import { ResponseDto } from '../class/res.class'; /** * 统一处理返回接口结果,如果不需要则添加@Keep装饰器 @@ -25,7 +25,7 @@ export class ApiTransformInterceptor implements NestInterceptor { } else { const response = context.switchToHttp().getResponse(); response.header('Content-Type', 'application/json; charset=utf-8'); - return new ResOp(200, data); + return new ResponseDto(200, data); } }), ); diff --git a/src/config/configuration.ts b/src/config/configuration.ts index fa46536..c3d262f 100644 --- a/src/config/configuration.ts +++ b/src/config/configuration.ts @@ -1,10 +1,4 @@ -import { DataSource, DataSourceOptions } from 'typeorm'; - -export let appDataSource: DataSource; - -export const getAppDataSource = (): DataSource => { - return appDataSource; -}; +import { DataSourceOptions } from 'typeorm'; export const getConfiguration = () => { const config = { @@ -18,7 +12,7 @@ export const getConfiguration = () => { host: process.env.MYSQL_HOST, port: Number.parseInt(process.env.MYSQL_PORT, 10), username: process.env.MYSQL_USERNAME, - password: process.env.MYSQL_ROOT_PASSWORD, + password: process.env.MYSQL_PASSWORD || process.env.MYSQL_ROOT_PASSWORD, database: process.env.MYSQL_DATABASE, entities: [__dirname + '/../**/entities/*.entity.{ts,js}'], autoLoadEntities: true, @@ -40,8 +34,7 @@ export const getConfiguration = () => { version: process.env.SWAGGER_VERSION, }, }; - appDataSource = new DataSource(config.database); - // appDataSource.initialize(); + return config; }; diff --git a/src/entities/apiData.entity.ts b/src/entities/apiData.entity.ts index ee026a7..21aa9c3 100644 --- a/src/entities/apiData.entity.ts +++ b/src/entities/apiData.entity.ts @@ -1,3 +1,4 @@ +import { ApiHideProperty } from '@nestjs/swagger'; import { Column, Entity, Generated, ManyToOne, OneToMany } from 'typeorm'; import { Exclude } from 'class-transformer'; import { Base } from './base.entity'; @@ -58,12 +59,14 @@ export class ApiData extends Base { @Column({ default: 0 }) weight: number; + @ApiHideProperty() @Exclude() @ManyToOne(() => Project, (project) => project.apiData, { onDelete: 'CASCADE', }) project: Project; + @ApiHideProperty() @Exclude() @OneToMany(() => Mock, (mock) => mock.apiData) mock: Mock[]; diff --git a/src/entities/apiGroup.entity.ts b/src/entities/apiGroup.entity.ts index 6bab2de..9f9ca70 100644 --- a/src/entities/apiGroup.entity.ts +++ b/src/entities/apiGroup.entity.ts @@ -1,3 +1,4 @@ +import { ApiHideProperty } from '@nestjs/swagger'; import { Column, Entity, ManyToOne } from 'typeorm'; import { Exclude } from 'class-transformer'; import { Project } from './project.entity'; @@ -14,6 +15,7 @@ export class ApiGroup extends Base { @Column({ default: 0 }) weight: number; + @ApiHideProperty() @Exclude() @ManyToOne(() => Project, (project) => project.apiGroup, { onDelete: 'CASCADE', diff --git a/src/entities/apiTestHistory.entity.ts b/src/entities/apiTestHistory.entity.ts index b1d6ad7..996b8be 100644 --- a/src/entities/apiTestHistory.entity.ts +++ b/src/entities/apiTestHistory.entity.ts @@ -1,3 +1,4 @@ +import { ApiHideProperty } from '@nestjs/swagger'; import { Exclude } from 'class-transformer'; import { Column, Entity, ManyToOne } from 'typeorm'; import { Project } from './project.entity'; @@ -20,6 +21,7 @@ export class ApiTestHistory extends OperatorBase { @Column({ type: 'json' }) response: string; + @ApiHideProperty() @Exclude() @ManyToOne(() => Project, (project) => project.apiTestHistory, { onDelete: 'CASCADE', diff --git a/src/entities/environment.entity.ts b/src/entities/environment.entity.ts index 8aa23bf..b858a5a 100644 --- a/src/entities/environment.entity.ts +++ b/src/entities/environment.entity.ts @@ -1,3 +1,4 @@ +import { ApiHideProperty } from '@nestjs/swagger'; import { Exclude } from 'class-transformer'; import { Column, Entity, ManyToOne } from 'typeorm'; import { Project } from './project.entity'; @@ -14,6 +15,7 @@ export class Environment extends Base { @Column({ type: 'json', nullable: true }) parameters: string; + @ApiHideProperty() @Exclude() @ManyToOne(() => Project, (project) => project.environment, { onDelete: 'CASCADE', diff --git a/src/entities/mock.entity.ts b/src/entities/mock.entity.ts index 405fa6a..d542219 100644 --- a/src/entities/mock.entity.ts +++ b/src/entities/mock.entity.ts @@ -1,3 +1,4 @@ +import { ApiHideProperty } from '@nestjs/swagger'; import { Exclude } from 'class-transformer'; import { Column, Entity, ManyToOne } from 'typeorm'; import { ApiData } from './apiData.entity'; @@ -17,6 +18,7 @@ export class Mock extends Base { @Column() createWay: string; + @ApiHideProperty() @Exclude() @ManyToOne(() => ApiData, (apiData) => apiData.mock, { onDelete: 'CASCADE', diff --git a/src/entities/project.entity.ts b/src/entities/project.entity.ts index f00fa63..7a8137d 100644 --- a/src/entities/project.entity.ts +++ b/src/entities/project.entity.ts @@ -24,27 +24,27 @@ export class Project extends Base { @JoinTable() users: UserEntity[]; - @ApiProperty({ description: '当前项目下的 apiData' }) + @ApiHideProperty() @Exclude() @OneToMany(() => ApiData, (apiData) => apiData.project) apiData: ApiData[]; - @ApiProperty({ description: '当前项目下的 apiGroup' }) + @ApiHideProperty() @Exclude() @OneToMany(() => ApiGroup, (apiGroup) => apiGroup.project) apiGroup: ApiGroup[]; - @ApiProperty({ description: '当前项目下的 apiTestHistory' }) + @ApiHideProperty() @Exclude() @OneToMany(() => ApiTestHistory, (apiTestHistory) => apiTestHistory.project) apiTestHistory: ApiGroup[]; - @ApiProperty({ description: '当前项目下的 environment' }) + @ApiHideProperty() @Exclude() @OneToMany(() => Environment, (environment) => environment.project) environment: Environment[]; - @ApiProperty({ description: '当前项目下的 shared' }) + @ApiHideProperty() @Exclude() @OneToMany(() => SharedEntity, (shared) => shared.project) shared: SharedEntity[]; diff --git a/src/entities/shared.entity.ts b/src/entities/shared.entity.ts index e965e50..994b86a 100644 --- a/src/entities/shared.entity.ts +++ b/src/entities/shared.entity.ts @@ -1,3 +1,4 @@ +import { ApiHideProperty } from '@nestjs/swagger'; import { Exclude } from 'class-transformer'; import { Column, Entity, Generated, ManyToOne } from 'typeorm'; import { Project } from './project.entity'; @@ -12,6 +13,7 @@ export class SharedEntity extends FictitiousBase { @Column() projectID: number; + @ApiHideProperty() @Exclude() @ManyToOne(() => Project, (project) => project.shared, { onDelete: 'CASCADE', diff --git a/src/entities/user.entity.ts b/src/entities/user.entity.ts index 2954350..1f49254 100644 --- a/src/entities/user.entity.ts +++ b/src/entities/user.entity.ts @@ -52,6 +52,8 @@ export class UserEntity extends TimestampBase { @ManyToMany(() => WorkspaceEntity, (workspace) => workspace.users) workspaces: WorkspaceEntity[]; + @ApiHideProperty() + @Exclude() @ManyToMany(() => Project, (project) => project.users) projects: Project[]; } diff --git a/src/entities/workspace.entity.ts b/src/entities/workspace.entity.ts index ca338ea..1714158 100644 --- a/src/entities/workspace.entity.ts +++ b/src/entities/workspace.entity.ts @@ -32,7 +32,7 @@ export class WorkspaceEntity extends TimestampBase { @JoinTable() users: UserEntity[]; - @ApiProperty({ description: '当前空间下的所有项目' }) + @ApiHideProperty() @OneToMany(() => Project, (project) => project.workspace) projects: Project[]; } diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 69888ef..914a88f 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -1,24 +1,24 @@ import { Body, Controller, Post, Put } from '@nestjs/common'; -import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { AuthService } from './auth.service'; import { Public } from '@/common/decorators/public.decorator'; import { LoginInfoDto } from '@/modules/auth/dto/login.dto'; import { LoginToken } from '@/modules/auth/auth.class'; import { JwtLogoutDto, JwtRefreshTokenDto } from '@/modules/auth/dto'; -import { UserService } from '@/modules/user/user.service'; +import { + ApiCreatedResponseData, + ApiOkResponseData, +} from '@/common/class/res.class'; @ApiTags('auth') @Controller('auth') export class AuthController { - constructor( - private readonly authService: AuthService, - private readonly userService: UserService, - ) {} + constructor(private readonly authService: AuthService) {} @ApiOperation({ summary: '用户登录/注册', }) - @ApiOkResponse({ type: LoginToken }) + @ApiCreatedResponseData(LoginToken) @Public() @Post('login') async login(@Body() userLoginDto: LoginInfoDto) { @@ -28,6 +28,7 @@ export class AuthController { @ApiOperation({ summary: '刷新token', }) + @ApiOkResponseData(LoginToken) @Public() @Put('refresh') async refreshToken(@Body() dto: JwtRefreshTokenDto): Promise { @@ -36,19 +37,10 @@ export class AuthController { @ApiOperation({ summary: '用户登出', }) + @ApiCreatedResponseData() @Post('logout') public async logout(@Body() dto: JwtLogoutDto): Promise { await this.authService.delete(dto); return true; } - - // @ApiOperation({ - // summary: '用户注册', - // deprecated: true, - // }) - // @Get('signup') - // public async signup(@Body() dto: LoginInfoDto): Promise { - // const userEntity = await this.userService.getOrCreateUser(dto); - // return this.authService.loginUser(userEntity); - // } } diff --git a/src/modules/shared-docs/dto/index.ts b/src/modules/shared-docs/dto/index.ts index 5888fb8..40a9f00 100644 --- a/src/modules/shared-docs/dto/index.ts +++ b/src/modules/shared-docs/dto/index.ts @@ -1,3 +1,11 @@ -export * from './login.dto'; -export * from './logout.dto'; -export * from './refresh.dto'; +import { ApiProperty } from '@nestjs/swagger'; +import { ApiData } from '@/entities/apiData.entity'; +import { ApiGroup } from '@/entities/apiGroup.entity'; + +export class CollectionsDto { + @ApiProperty({ type: [ApiGroup] }) + groups: ApiGroup[]; + + @ApiProperty({ type: [ApiData] }) + apis: ApiData[]; +} diff --git a/src/modules/shared-docs/dto/login.dto.ts b/src/modules/shared-docs/dto/login.dto.ts deleted file mode 100644 index be1f6db..0000000 --- a/src/modules/shared-docs/dto/login.dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { - IsMobilePhone, - IsNotEmpty, - IsString, - MinLength, -} from 'class-validator'; - -export class LoginInfoDto { - @ApiProperty({ description: '用户名' }) - @IsString() - @IsNotEmpty({ message: '用户名不能为空' }) - username: string; - - @ApiProperty({ description: '密码' }) - @IsNotEmpty({ message: '密码不能为空' }) - @IsString() - @MinLength(6) - password: string; -} diff --git a/src/modules/shared-docs/dto/logout.dto.ts b/src/modules/shared-docs/dto/logout.dto.ts deleted file mode 100644 index bed919c..0000000 --- a/src/modules/shared-docs/dto/logout.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsString } from 'class-validator'; - -export class JwtLogoutDto { - @ApiProperty() - @IsString() - refreshToken: string; -} diff --git a/src/modules/shared-docs/dto/refresh.dto.ts b/src/modules/shared-docs/dto/refresh.dto.ts deleted file mode 100644 index 93919fd..0000000 --- a/src/modules/shared-docs/dto/refresh.dto.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { IsString } from 'class-validator'; - -export class JwtRefreshTokenDto { - @ApiProperty() - @IsString() - refreshToken: string; -} diff --git a/src/modules/shared-docs/shared-docs.controller.ts b/src/modules/shared-docs/shared-docs.controller.ts index ccbef58..c8fb503 100644 --- a/src/modules/shared-docs/shared-docs.controller.ts +++ b/src/modules/shared-docs/shared-docs.controller.ts @@ -1,12 +1,15 @@ import { Controller, Get, Param, UseGuards } from '@nestjs/common'; -import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ApiDataService } from '../workspace/apiData/apiData.service'; -import { LoginToken } from '@/modules/auth/auth.class'; import { XHeader } from '@/common/decorators/xheader.decorator'; import { SharedAuthGuard } from '@/modules/shared-docs/guards/shared-auth.guard'; import { ProjectService } from '@/modules/workspace/project/project.service'; import { Public } from '@/common/decorators/public.decorator'; import { EnvironmentService } from '@/modules/workspace/environment/environment.service'; +import { ApiOkResponseData } from '@/common/class/res.class'; +import { CollectionsDto } from '@/modules/shared-docs/dto'; +import { ApiData } from '@/entities/apiData.entity'; +import { Environment } from '@/entities/environment.entity'; @ApiTags('shared-docs') @UseGuards(SharedAuthGuard) @@ -22,7 +25,7 @@ export class SharedDocsController { summary: '获取所有分组及API', }) @Public() - @ApiOkResponse({ type: LoginToken }) + @ApiOkResponseData(CollectionsDto) @Get(':uniqueID/collections') async collections(@XHeader('projectID') projectID: number) { return this.projectService.getProjectCollections(projectID); @@ -31,6 +34,7 @@ export class SharedDocsController { @ApiOperation({ summary: '获取API详情', }) + @ApiOkResponseData(ApiData) @Public() @Get(':uniqueID/api/:apiDataUUID') async getApidataInfo(@Param('apiDataUUID') apiDataUUID: number) { @@ -40,6 +44,7 @@ export class SharedDocsController { @ApiOperation({ summary: '获取环境列表', }) + @ApiOkResponseData(Environment, 'array') @Public() @Get(':uniqueID/environments') async environments(@XHeader('projectID') projectID: number) { diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts index 7053ac8..c423e38 100644 --- a/src/modules/user/user.controller.ts +++ b/src/modules/user/user.controller.ts @@ -13,6 +13,7 @@ import { UpdateUserInfoDto, UpdateUserPasswordDto } from './user.dto'; import { UserEntity } from '@/entities/user.entity'; import { IUser, User } from '@/common/decorators/user.decorator'; import { LoginToken } from '@/modules/auth/auth.class'; +import { ApiOkResponseData } from '@/common/class/res.class'; @ApiBearerAuth() @ApiTags('user') @@ -21,6 +22,7 @@ export class UserController { constructor(private readonly userService: UserService) {} @ApiOperation({ summary: '查看个人信息' }) + @ApiOkResponseData(UserEntity) @Get('profile') @UseInterceptors(ClassSerializerInterceptor) public getUserProfile(@User() user: IUser): Promise { @@ -28,6 +30,7 @@ export class UserController { } @Put('profile') + @ApiOkResponseData(UserEntity) @ApiOperation({ summary: '更新个人资料' }) async updateUserProfile( @User() user: IUser, @@ -38,6 +41,7 @@ export class UserController { @Put('password') @ApiOperation({ summary: '更改个人密码' }) + @ApiOkResponseData(LoginToken) async updateUserPassword( @User() user: IUser, @Body() updateUserDto: UpdateUserPasswordDto, @@ -47,6 +51,7 @@ export class UserController { @Get(':username') @ApiOperation({ summary: '搜索用户' }) + @ApiOkResponseData(UserEntity, 'array') findOne(@Param('username') username: string) { return this.userService.searchUsers(username); } diff --git a/src/modules/workspace/apiData/apiData.controller.ts b/src/modules/workspace/apiData/apiData.controller.ts index 2a2e839..ff64496 100644 --- a/src/modules/workspace/apiData/apiData.controller.ts +++ b/src/modules/workspace/apiData/apiData.controller.ts @@ -10,11 +10,17 @@ import { } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { ValidateQueryPipe } from 'src/pipe/query.pipe'; +import { InsertResult } from 'typeorm'; import { ApiDataService } from './apiData.service'; import { CreateDto } from './dto/create.dto'; import { UpdateDto } from './dto/update.dto'; import { QueryDto } from './dto/query.dto'; import { WORKSPACE_PROJECT_PREFIX } from '@/common/contants/prefix.contants'; +import { + ApiCreatedResponseData, + ApiOkResponseData, +} from '@/common/class/res.class'; +import { ApiData } from '@/entities/apiData.entity'; @ApiTags('apiData') @Controller(`${WORKSPACE_PROJECT_PREFIX}/api_data`) @@ -36,12 +42,15 @@ export class ApiDataController { }); return item; } + + @ApiCreatedResponseData(ApiData) @Post() async create(@Body() createDto: CreateDto, @Param('projectID') projectID) { createDto = this.filterItem(createDto, projectID); return this.service.create({ ...createDto, projectID }); } + @ApiCreatedResponseData(InsertResult) @Post('batch') async batchCreate( @Body() createDto: Array, @@ -53,15 +62,19 @@ export class ApiDataController { return this.service.batchCreate(createDto); } + @ApiOkResponseData(ApiData, 'array') @Get() async findAll(@Param('projectID') projectID, @Query() query: QueryDto) { return this.service.findAll({ ...query, projectID }); } + @ApiOkResponseData(ApiData) @Get(':uuid') async findOne(@Param('uuid') uuid, @Param('projectID') projectID) { return this.service.findOne({ where: { uuid, projectID } }); } + + @ApiOkResponseData(ApiData, 'array') @Put('batch') async batchUpdate( @Body() updateDtos: Array, @@ -82,6 +95,8 @@ export class ApiDataController { }); return this.service.bulkUpdate(newArr); } + + @ApiOkResponseData(ApiData) @Put(':uuid') async update( @Param('uuid') uuid: string, diff --git a/src/modules/workspace/apiGroup/apiGroup.controller.ts b/src/modules/workspace/apiGroup/apiGroup.controller.ts index 51bfcd2..206fcce 100644 --- a/src/modules/workspace/apiGroup/apiGroup.controller.ts +++ b/src/modules/workspace/apiGroup/apiGroup.controller.ts @@ -18,18 +18,25 @@ import { CreateDto } from './dto/create.dto'; import { UpdateDto } from './dto/update.dto'; import { QueryDto } from './dto/query.dto'; import { WORKSPACE_PROJECT_PREFIX } from '@/common/contants/prefix.contants'; +import { + ApiCreatedResponseData, + ApiOkResponseData, +} from '@/common/class/res.class'; +import { ApiGroup } from '@/entities/apiGroup.entity'; @ApiTags('apiGroup') @Controller(`${WORKSPACE_PROJECT_PREFIX}/group`) export class ApiGroupController { constructor(private readonly service: ApiGroupService) {} + @ApiCreatedResponseData(ApiGroup) @Post() async create(@Body() createDto: CreateDto, @Param('projectID') projectID) { const { uuid } = await this.service.create({ ...createDto, projectID }); return await this.findOne(uuid, projectID); } + @ApiCreatedResponseData() @Post('batch') async batchCreate( @Param('projectID') projectID, @@ -40,11 +47,13 @@ export class ApiGroupController { ); } + @ApiOkResponseData(ApiGroup, 'array') @Get() async findAll(@Query() query: QueryDto, @Param('projectID') projectID) { return this.service.findAll({ ...query, projectID }); } + @ApiOkResponseData(ApiGroup) @Get(':uuid') async findOne( @Param('uuid', ParseIntPipe) uuid: number, @@ -52,6 +61,7 @@ export class ApiGroupController { ) { return this.service.findOne({ where: { uuid, projectID } }); } + @ApiOkResponseData() @Put('batch') async batchUpdate( @Param('projectID') projectID, @@ -74,6 +84,8 @@ export class ApiGroupController { } return new BadRequestException('批量修改失败!'); } + + @ApiOkResponseData(ApiGroup) @Put(':uuid') async update( @Param('uuid', ParseIntPipe) uuid: number, @@ -88,6 +100,7 @@ export class ApiGroupController { return new NotFoundException('修改失败!分组不存在'); } + @ApiOkResponseData() @Delete() async remove(@Param('projectID') projectID, @Query(ValidateQueryPipe) query) { const data = await this.service.remove(query.uuids, projectID); diff --git a/src/modules/workspace/apiTestHistory/apiTestHistory.controller.ts b/src/modules/workspace/apiTestHistory/apiTestHistory.controller.ts index 896c7eb..d89225d 100644 --- a/src/modules/workspace/apiTestHistory/apiTestHistory.controller.ts +++ b/src/modules/workspace/apiTestHistory/apiTestHistory.controller.ts @@ -16,6 +16,11 @@ import { UpdateDto } from './dto/update.dto'; import { QueryDto } from './dto/query.dto'; import { WORKSPACE_PROJECT_PREFIX } from '@/common/contants/prefix.contants'; import { IUser, User } from '@/common/decorators/user.decorator'; +import { + ApiCreatedResponseData, + ApiOkResponseData, +} from '@/common/class/res.class'; +import { ApiTestHistory } from '@/entities/apiTestHistory.entity'; @ApiTags('apiTestHistory') @Controller(`${WORKSPACE_PROJECT_PREFIX}/api_test_history`) @@ -24,6 +29,7 @@ export class ApiTestHistoryController { constructor(private readonly service: ApiTestHistoryService) {} + @ApiCreatedResponseData(ApiTestHistory) @Post() async create( @Body() createDto: CreateDto, @@ -39,6 +45,7 @@ export class ApiTestHistoryController { return await this.findOne(`${data.uuid}`, projectID); } + @ApiCreatedResponseData() @Post('batch') async batchCreate(@Body() createDto: Array, @User() user: IUser) { createDto.map((val) => { @@ -54,6 +61,7 @@ export class ApiTestHistoryController { return this.service.batchCreate(createDto); } + @ApiOkResponseData(ApiTestHistory, 'array') @Get() async findAll( @Query() query: QueryDto, @@ -66,11 +74,13 @@ export class ApiTestHistoryController { ]); } + @ApiOkResponseData(ApiTestHistory) @Get(':uuid') async findOne(@Param('uuid') uuid: string, @Param('projectID') projectID) { return this.service.findOne(+uuid, projectID); } + @ApiOkResponseData(ApiTestHistory) @Put(':uuid') async update( @Param('uuid') uuid: string, @@ -83,9 +93,10 @@ export class ApiTestHistoryController { } }); await this.service.update(+uuid, updateDto); - return await this.findOne(uuid, projectID); + return this.findOne(uuid, projectID); } + @ApiOkResponseData() @Delete() async remove(@Query(ValidateQueryPipe) query) { return await this.service.remove(query.uuids); diff --git a/src/modules/workspace/environment/environment.controller.ts b/src/modules/workspace/environment/environment.controller.ts index ae0d29b..8209849 100644 --- a/src/modules/workspace/environment/environment.controller.ts +++ b/src/modules/workspace/environment/environment.controller.ts @@ -14,6 +14,11 @@ import { CreateDto } from './dto/create.dto'; import { UpdateDto } from './dto/update.dto'; import { QueryDto } from './dto/query.dto'; import { WORKSPACE_PROJECT_PREFIX } from '@/common/contants/prefix.contants'; +import { + ApiCreatedResponseData, + ApiOkResponseData, +} from '@/common/class/res.class'; +import { Environment } from '@/entities/environment.entity'; @ApiTags('Environment') @Controller(`${WORKSPACE_PROJECT_PREFIX}/environment`) @@ -28,6 +33,7 @@ export class EnvironmentController { constructor(private readonly service: EnvironmentService) {} + @ApiCreatedResponseData(Environment) @Post() async create(@Body() createDto: CreateDto, @Param('projectID') projectID) { this.JSON_FIELDS.forEach((field) => { @@ -43,6 +49,7 @@ export class EnvironmentController { return this.NOT_FOUND; } + @ApiCreatedResponseData() @Post('batch') async batchCreate( @Body() createDto: Array, @@ -60,16 +67,19 @@ export class EnvironmentController { return this.service.batchCreate(createDto); } + @ApiOkResponseData(Environment, 'array') @Get() async findAll(@Query() query: QueryDto, @Param('projectID') projectID) { return this.service.findAll({ where: { ...query, projectID } }); } + @ApiOkResponseData(Environment) @Get(':uuid') async findOne(@Param('uuid') uuid, @Param('projectID') projectID) { return this.service.findOne({ where: { uuid, projectID } }); } + @ApiOkResponseData(Environment) @Put(':uuid') async update( @Param('uuid') uuid: string, @@ -89,7 +99,7 @@ export class EnvironmentController { return this.NOT_FOUND; } - + @ApiOkResponseData() @Delete(':uuid') async remove(@Param('uuid') uuid: string, @Param('projectID') projectID) { return this.service.remove(+uuid, projectID); diff --git a/src/modules/workspace/mock/mock.controller.ts b/src/modules/workspace/mock/mock.controller.ts index 3960cad..e3a7496 100644 --- a/src/modules/workspace/mock/mock.controller.ts +++ b/src/modules/workspace/mock/mock.controller.ts @@ -1,62 +1,58 @@ import { Controller, Get, - Req, Post, Body, Put, Param, Delete, Query, - All, BadRequestException, ParseIntPipe, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { Request } from 'express'; import { MockService } from './mock.service'; import { CreateDto } from './dto/create.dto'; import { UpdateDto } from './dto/update.dto'; import { QueryDto } from './dto/query.dto'; import { WORKSPACE_PROJECT_PREFIX } from '@/common/contants/prefix.contants'; -import { Public } from '@/common/decorators/public.decorator'; +import { + ApiCreatedResponseData, + ApiOkResponseData, +} from '@/common/class/res.class'; +import { Mock } from '@/entities/mock.entity'; @ApiTags('Mock') @Controller(`${WORKSPACE_PROJECT_PREFIX}/mock`) export class MockController { constructor(private readonly service: MockService) {} + @ApiCreatedResponseData(Mock) @Post() async create(@Body() createDto: CreateDto, @Param('projectID') projectID) { const data = await this.service.create({ ...createDto, projectID }); return this.findOne(data.uuid, projectID); } + @ApiCreatedResponseData() @Post('batch') async batchCreate(@Body() createDto: Array) { return this.service.batchCreate(createDto); } + @ApiOkResponseData(Mock, 'array') @Get() async findAll(@Query() query: QueryDto, @Param('projectID') projectID) { return this.service.findAll({ ...query, projectID }); } + @ApiOkResponseData(Mock) @Get(':uuid') async findOne(@Param('uuid') uuid, @Param('projectID') projectID) { return this.service.findOne({ where: { uuid, projectID } }); } - @All(':mockID/**') - @Public() - async findMock( - @Param('projectID', ParseIntPipe) projectID: number, - @Param('mockID', ParseIntPipe) mockID: number, - @Req() request: Request, - ) { - return this.service.findMock(projectID, mockID, request); - } - + @ApiOkResponseData(Mock) @Put(':uuid') async update( @Param('uuid') uuid: string, @@ -67,6 +63,7 @@ export class MockController { return await this.findOne(uuid, projectID); } + @ApiOkResponseData() @Delete(':uuid') async remove( @Param('uuid', ParseIntPipe) uuid: number, diff --git a/src/modules/workspace/project/dto/export.dto.ts b/src/modules/workspace/project/dto/export.dto.ts new file mode 100644 index 0000000..ec98b64 --- /dev/null +++ b/src/modules/workspace/project/dto/export.dto.ts @@ -0,0 +1,17 @@ +import { Child, Environment as EnvironmentType } from './import.dto'; +import { ApiData } from '@/entities/apiData.entity'; +import { ApiGroup } from '@/entities/apiGroup.entity'; +import { Environment } from '@/entities/environment.entity'; +import { Project } from '@/entities/project.entity'; + +export class ExportCollectionsResultDto { + collections: Child; + enviroments: EnvironmentType; +} + +export class ExportProjectResultDto { + environment: Environment[]; + group: ApiGroup[]; + project: Project; + apiData: ApiData[]; +} diff --git a/src/modules/workspace/project/dto/query.dto.ts b/src/modules/workspace/project/dto/query.dto.ts index 194665b..13497e9 100644 --- a/src/modules/workspace/project/dto/query.dto.ts +++ b/src/modules/workspace/project/dto/query.dto.ts @@ -1,3 +1,11 @@ +import { ApiData } from '@/entities/apiData.entity'; +import { ApiGroup } from '@/entities/apiGroup.entity'; + export class QueryDto { name: string; } + +export class CollectionsDto { + groups: ApiGroup[]; + apis: ApiData[]; +} diff --git a/src/modules/workspace/project/project.controller.ts b/src/modules/workspace/project/project.controller.ts index 7b5b44a..7d11402 100644 --- a/src/modules/workspace/project/project.controller.ts +++ b/src/modules/workspace/project/project.controller.ts @@ -14,15 +14,25 @@ import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ProjectService } from './project.service'; import { CreateDto } from './dto/create.dto'; import { UpdateDto } from './dto/update.dto'; -import { QueryDto } from './dto/query.dto'; +import { CollectionsDto, QueryDto } from './dto/query.dto'; import { ImportDto } from './dto/import.dto'; import { WORKSPACE_ID_PREFIX } from '@/common/contants/prefix.contants'; +import { + ApiCreatedResponseData, + ApiOkResponseData, +} from '@/common/class/res.class'; +import { Project } from '@/entities/project.entity'; +import { + ExportProjectResultDto, + ExportCollectionsResultDto, +} from '@/modules/workspace/project/dto/export.dto'; @ApiTags('Project') @Controller(`${WORKSPACE_ID_PREFIX}/project`) export class ProjectController { constructor(private readonly service: ProjectService) {} + @ApiCreatedResponseData(Project) @Post() async create( @Param('workspaceID', ParseIntPipe) workspaceID, @@ -32,11 +42,13 @@ export class ProjectController { return this.findOne(workspaceID, `${data.uuid}`); } + @ApiCreatedResponseData() @Post('batch') async batchCreate(@Body() createDto: Array) { return this.service.batchCreate(createDto); } + @ApiOkResponseData(Project, 'array') @Get() async findAll( @Query() query: QueryDto, @@ -45,6 +57,7 @@ export class ProjectController { return this.service.findAll(query, workspaceID); } + @ApiOkResponseData(Project) @Get(':projectID') async findOne( @Param('projectID', ParseIntPipe) projectID, @@ -52,7 +65,7 @@ export class ProjectController { ) { return this.service.findOne(workspaceID, projectID); } - + @ApiOkResponseData(Project) @Put(':projectID') async update( @Param('projectID') projectID: string, @@ -66,7 +79,7 @@ export class ProjectController { return new NotFoundException('更新失败!项目不存在'); } - + @ApiOkResponseData() @Delete(':projectID') async remove(@Param('projectID') projectID: string) { const data = await this.service.remove(+projectID); @@ -77,6 +90,7 @@ export class ProjectController { return new NotFoundException('删除失败!项目不存在'); } + @ApiOkResponseData() @Put(':projectID/import') async import( @Param('projectID') projectID: string, @@ -94,6 +108,7 @@ export class ProjectController { }; } + @ApiOkResponseData(ExportCollectionsResultDto) @Get(':projectID/export/collections') async export( @Param('projectID') projectID: string, @@ -103,15 +118,14 @@ export class ProjectController { return this.service.exportCollections(workspaceID, Number(projectID)); } + @ApiOkResponseData(ExportProjectResultDto) @Get(':projectID/export') - async projectExport( - @Param('projectID') projectID: string, - @Param('workspaceID', ParseIntPipe) workspaceID, - ) { + async projectExport(@Param('projectID') projectID: string) { // console.log('projectID', projectID, importDto); return this.service.projectExport(Number(projectID)); } + @ApiOkResponseData(CollectionsDto) @Get(':projectID/collections') async getProjectCollections( @Param('projectID', ParseIntPipe) projectID: number, diff --git a/src/modules/workspace/shared/shared.controller.ts b/src/modules/workspace/shared/shared.controller.ts index 6b35a75..c2a255b 100644 --- a/src/modules/workspace/shared/shared.controller.ts +++ b/src/modules/workspace/shared/shared.controller.ts @@ -2,6 +2,8 @@ import { Controller, Get, Post, Param, Delete } from '@nestjs/common'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { SharedService } from './shared.service'; import { WORKSPACE_PROJECT_PREFIX } from '@/common/contants/prefix.contants'; +import { ApiCreatedResponseData } from '@/common/class/res.class'; +import { SharedEntity } from '@/entities/shared.entity'; @ApiTags('Shared') @Controller(`${WORKSPACE_PROJECT_PREFIX}/shared`) @@ -9,6 +11,7 @@ export class SharedController { constructor(private readonly sharedService: SharedService) {} @Post() + @ApiCreatedResponseData(SharedEntity) @ApiOperation({ summary: '新增分享' }) async createShared(@Param('projectID') projectID: number) { return this.sharedService.createShared(projectID); diff --git a/src/modules/workspace/workspace.controller.ts b/src/modules/workspace/workspace.controller.ts index 836c1d4..c43336c 100644 --- a/src/modules/workspace/workspace.controller.ts +++ b/src/modules/workspace/workspace.controller.ts @@ -33,6 +33,10 @@ import { Collections } from '@/modules/workspace/project/dto/import.dto'; import { ProjectService } from '@/modules/workspace/project/project.service'; import { sampleApiData } from '@/modules/workspace/apiData/samples/sample.api.data'; import { ApiDataService } from '@/modules/workspace/apiData/apiData.service'; +import { + ApiCreatedResponseData, + ApiOkResponseData, +} from '@/common/class/res.class'; @ApiBearerAuth() @ApiTags('workspace') @@ -46,6 +50,7 @@ export class WorkspaceController { @Post() @ApiOperation({ summary: '创建空间' }) + @ApiCreatedResponseData(WorkspaceEntity) @ApiResponse({ status: 403, description: 'Forbidden.' }) async create( @User() user: IUser, @@ -66,6 +71,7 @@ export class WorkspaceController { return workspace; } + @ApiCreatedResponseData() @Post('upload') @ApiOperation({ summary: '导入本地数据并创建空间' }) async importLocalData(@User() user: IUser, @Body() collections: Collections) { @@ -87,6 +93,7 @@ export class WorkspaceController { } } + @ApiOkResponseData(WorkspaceEntity) @Put(':workspaceID') @ApiOperation({ summary: '修改空间名称' }) async update( @@ -96,6 +103,7 @@ export class WorkspaceController { return this.workspaceService.update(id, updateDto); } + @ApiOkResponseData() @Delete(':workspaceID') @ApiOperation({ summary: '删除空间' }) @ApiResponse({ status: 403, description: 'Forbidden.' }) @@ -113,12 +121,14 @@ export class WorkspaceController { return this.workspaceService.delete(id); } + @ApiOkResponseData(WorkspaceEntity, 'array') @Get('list') @ApiOperation({ summary: '获取空间列表' }) list(@User() user: IUser): Promise { return this.workspaceService.list(user.userId); } + @ApiOkResponseData(WorkspaceEntity) @Get(':workspaceID') @ApiOperation({ summary: '获取空间信息' }) async info(@Param('workspaceID') id): Promise { @@ -130,12 +140,14 @@ export class WorkspaceController { return workspace; } + @ApiOkResponseData(WorkspaceUser, 'array') @Get(':workspaceID/member/list') @ApiOperation({ summary: '获取空间成员列表' }) async getMemberList(@Param('workspaceID') id): Promise { return this.workspaceService.getMemberList(id); } + @ApiOkResponseData(WorkspaceUser, 'array') @Get(':workspaceID/member/list/:username') @ApiOperation({ summary: '搜索空间成员' }) @ApiResponse({ @@ -148,6 +160,7 @@ export class WorkspaceController { return this.workspaceService.getMemberList(id, username); } + @ApiCreatedResponseData(WorkspaceEntity) @Post(':workspaceID/member/add') @ApiOperation({ summary: '添加空间成员' }) async memberAdd( @@ -162,6 +175,7 @@ export class WorkspaceController { return this.workspaceService.addMembers(id, createCatDto.userIDs); } + @ApiOkResponseData(WorkspaceEntity) @Delete(':workspaceID/member/remove') @ApiOperation({ summary: '移除空间成员' }) async memberRemove(