From d58dcc3d41b10d06fc6523318ef003314f624439 Mon Sep 17 00:00:00 2001 From: Scarqin <1054139596@qq.com> Date: Wed, 2 Nov 2022 16:40:02 +0800 Subject: [PATCH] Release/1.9.0-Share API (#18) --- .env | 7 +- .gitignore | 4 +- docker-compose.dev.yaml | 69 +++++++++++++++++++ docker-compose.yaml | 49 +++++++++++-- package.json | 2 +- src/app.controller.ts | 2 +- src/app.module.ts | 2 + src/common/decorators/xheader.decorator.ts | 19 +++++ src/entities/shared.entity.ts | 12 ++++ src/main.ts | 3 + .../1666580390462-update-table_1_9_0.ts | 34 +++++++++ src/modules/auth/guards/jwt-auth.guard.ts | 9 ++- src/modules/shared-docs/dto/index.ts | 3 + src/modules/shared-docs/dto/login.dto.ts | 20 ++++++ src/modules/shared-docs/dto/logout.dto.ts | 8 +++ src/modules/shared-docs/dto/refresh.dto.ts | 8 +++ .../shared-docs/guards/shared-auth.guard.ts | 53 ++++++++++++++ .../shared-docs/shared-docs.controller.ts | 48 +++++++++++++ src/modules/shared-docs/shared-docs.module.ts | 19 +++++ .../shared-docs/shared-docs.service.ts | 28 ++++++++ .../shared-docs/strategies/jwt.strategy.ts | 26 +++++++ .../workspace/project/project.controller.ts | 2 +- .../workspace/shared/dto/create.dto.ts | 9 +++ src/modules/workspace/shared/dto/query.dto.ts | 5 ++ .../workspace/shared/dto/update.dto.ts | 4 ++ .../workspace/shared/shared.controller.ts | 31 +++++++++ .../workspace/shared/shared.service.spec.ts | 18 +++++ .../workspace/shared/shared.service.ts | 28 ++++++++ src/modules/workspace/workspace.module.ts | 6 ++ 29 files changed, 514 insertions(+), 14 deletions(-) create mode 100644 docker-compose.dev.yaml create mode 100644 src/common/decorators/xheader.decorator.ts create mode 100644 src/entities/shared.entity.ts create mode 100644 src/migrations/1666580390462-update-table_1_9_0.ts create mode 100644 src/modules/shared-docs/dto/index.ts create mode 100644 src/modules/shared-docs/dto/login.dto.ts create mode 100644 src/modules/shared-docs/dto/logout.dto.ts create mode 100644 src/modules/shared-docs/dto/refresh.dto.ts create mode 100644 src/modules/shared-docs/guards/shared-auth.guard.ts create mode 100644 src/modules/shared-docs/shared-docs.controller.ts create mode 100644 src/modules/shared-docs/shared-docs.module.ts create mode 100644 src/modules/shared-docs/shared-docs.service.ts create mode 100644 src/modules/shared-docs/strategies/jwt.strategy.ts create mode 100644 src/modules/workspace/shared/dto/create.dto.ts create mode 100644 src/modules/workspace/shared/dto/query.dto.ts create mode 100644 src/modules/workspace/shared/dto/update.dto.ts create mode 100644 src/modules/workspace/shared/shared.controller.ts create mode 100644 src/modules/workspace/shared/shared.service.spec.ts create mode 100644 src/modules/workspace/shared/shared.service.ts diff --git a/.env b/.env index 2cd02e8..d8c4f25 100644 --- a/.env +++ b/.env @@ -1,6 +1,6 @@ # eoapi-server coinfigure EOAPI_SERVER_PORT=3000 -EOAPI_SERVER_PATH=/api +EOAPI_API_PREFIX=/api # mysql configure TZ=Asia/Shanghai @@ -17,4 +17,7 @@ SWAGGER_ENABLE=true SWAGGER_VERSION=1.0 SWAGGER_TITLE=Eoapi-remote-server API文档 SWAGGER_DESC=Eoapi remote server API document。 - \ No newline at end of file + +# eoapi-test-server +TEST_SERVER_PORT=4201 +EOAPI_WEBSOCKET_POST=4202 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 330dc99..f176759 100644 --- a/.gitignore +++ b/.gitignore @@ -39,4 +39,6 @@ lerna-debug.log* # env .env.local -.env.prodction \ No newline at end of file +.env.prodction +docker-compose.dev.yaml +Dockerfile.dev diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml new file mode 100644 index 0000000..77a230f --- /dev/null +++ b/docker-compose.dev.yaml @@ -0,0 +1,69 @@ +version: '3' + +services: + eoapi-remote-server-test: + # build: 从当前路径构建镜像 + # build: . + image: eolinker/eoapi-remote-server:1.9.0 + container_name: eoapi-remote-server-test + deploy: + restart_policy: + condition: on-failure + max_attempts: 10 + environment: + # 服务配置 + EOAPI_SERVER_PORT: 3001 + EOAPI_API_PREFIX: /api + MYSQL_HOST: host.docker.internal # 数据库地址 + TZ: Asia/Shanghai + # 数据库配置 + MYSQL_PORT: 33066 # 数据库端口号 + MYSQL_USERNAME: root + MYSQL_DATABASE: eoapi-test + MYSQL_PASSWORD: 123456a. + MYSQL_ROOT_PASSWORD: 123456a. + # swagger配置 + SWAGGER_PATH: swagger-docs + SWAGGER_ENABLE: 'true' + SWAGGER_VERSION: 1.0 + SWAGGER_TITLE: Eoapi-remote-server API文档 + SWAGGER_DESC: Eoapi remote server API document。 + extra_hosts: + - 'host.docker.internal:host-gateway' + ports: + - '3001:3001' + networks: + - eoapi_net + + eoapi: + image: eolinker/eoapi:1.9.0-dev + container_name: eoapi-dev + deploy: + restart_policy: + condition: on-failure + max_attempts: 3 + extra_hosts: + - 'host.docker.internal:host-gateway' + ports: + - '3002:80' + networks: + - eoapi_net + + eoapi-test-server: + image: eolinker/eoapi-test-server:1.9.0-dev + container_name: eoapi-test-server-dev + deploy: + restart_policy: + condition: on-failure + max_attempts: 3 + extra_hosts: + - 'host.docker.internal:host-gateway' + ports: + - '42011:4201' + - '42022:4202' + networks: + - eoapi_net + +networks: + eoapi_net: + name: eoapi_net diff --git a/docker-compose.yaml b/docker-compose.yaml index 75681f7..d822df9 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,16 +3,19 @@ version: '3' services: eoapi-remote-server: # build: 从当前路径构建镜像 - build: . - image: eoapi/eoapi-remote-server:1.8.2 + # build: . + image: eolinker/eoapi-remote-server:latest container_name: eoapi-remote-server - restart: always + deploy: + restart_policy: + condition: on-failure + max_attempts: 10 env_file: - .env extra_hosts: - 'host.docker.internal:host-gateway' ports: - - '${EOAPI_SERVER_PORT}:3000' + - 3000:3000 # 当前服务启动之前先要把depends_on指定的服务启动起来才行 depends_on: - mysql @@ -22,17 +25,51 @@ services: mysql: image: mysql:latest container_name: eoapi-mysql - restart: always + deploy: + restart_policy: + condition: on-failure + max_attempts: 10 command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci #设置utf8字符集 env_file: - .env ports: - - '${MYSQL_PORT}:3306' + - 33066:3306 volumes: - ./sample/mysql/:/var/lib/mysql/ # ./sample/mysql/路径可以替换成自己的路径 networks: - eoapi_net + eoapi: + image: eolinker/eoapi:latest + container_name: eoapi + deploy: + restart_policy: + condition: on-failure + max_attempts: 3 + extra_hosts: + - 'host.docker.internal:host-gateway' + ports: + - '3002:80' + # volumes: + # - ./nginx.conf:/etc/nginx/conf.d/default.conf + networks: + - eoapi_net + + eoapi-test-server: + image: eolinker/eoapi-test-server:latest + container_name: eoapi-test-server + deploy: + restart_policy: + condition: on-failure + max_attempts: 3 + extra_hosts: + - 'host.docker.internal:host-gateway' + ports: + - '42011:4201' + - '42022:4202' + networks: + - eoapi_net + networks: eoapi_net: name: eoapi_net diff --git a/package.json b/package.json index 659c8e2..4181c0a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eoapi-remote-server", - "version": "1.8.2", + "version": "1.9.0", "description": "Storage api data in remote server", "author": "eoapi", "private": true, diff --git a/src/app.controller.ts b/src/app.controller.ts index 306e395..d3ce1e0 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -16,6 +16,6 @@ export class AppController { @Get('system/status') @Public() status() { - return `v${version}`; + return version; } } diff --git a/src/app.module.ts b/src/app.module.ts index f3d0ae6..ea4c0ad 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -5,6 +5,7 @@ import { AppController } from './app.controller'; import { AppService } from './app.service'; // 引入数据库的及配置文件 import { AuthModule } from './modules/auth/auth.module'; +import { ShareDocsModule } from './modules/shared-docs/shared-docs.module'; import { getConfiguration } from './config/configuration'; import { UserModule } from '@/modules/user/user.module'; import { WorkspaceModule } from '@/modules/workspace/workspace.module'; @@ -37,6 +38,7 @@ console.log('process.env.NODE_ENV', `.env.${process.env.NODE_ENV}`); SharedModule, AuthModule, // 认证 UserModule, + ShareDocsModule, ], controllers: [AppController], providers: [AppService], diff --git a/src/common/decorators/xheader.decorator.ts b/src/common/decorators/xheader.decorator.ts new file mode 100644 index 0000000..eb07f3c --- /dev/null +++ b/src/common/decorators/xheader.decorator.ts @@ -0,0 +1,19 @@ +import { createParamDecorator, ExecutionContext } from '@nestjs/common'; + +export type XHeaderType = { + projectID: number; + workspaceID: number; +}; + +type Keys = keyof XHeaderType; + +export const XHeader = createParamDecorator( + (_data: Keys, ctx: ExecutionContext) => { + const { headers } = ctx.switchToHttp().getRequest(); + const result: XHeaderType = { + projectID: headers['x-project-id'], + workspaceID: headers['x-workspace-id'], + }; + return result[_data] ?? result; + }, +); diff --git a/src/entities/shared.entity.ts b/src/entities/shared.entity.ts new file mode 100644 index 0000000..9ea0f05 --- /dev/null +++ b/src/entities/shared.entity.ts @@ -0,0 +1,12 @@ +import { Column, Entity, Generated } from 'typeorm'; +import { FictitiousBase } from './base.entity'; + +@Entity({ name: 'shared' }) +export class SharedEntity extends FictitiousBase { + @Column() + @Generated('uuid') + uniqueID: string; + + @Column() + projectID: number; +} diff --git a/src/main.ts b/src/main.ts index 080cfb0..f508065 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,6 +10,9 @@ import { ValidationPipe } from '@/pipe/validation.pipe'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.enableCors(); + + app.setGlobalPrefix(process.env.EOAPI_API_PREFIX || '/api'); + app.use(bodyParser.json({ limit: '50mb' })); app.use(bodyParser.urlencoded({ limit: '50mb', extended: true })); diff --git a/src/migrations/1666580390462-update-table_1_9_0.ts b/src/migrations/1666580390462-update-table_1_9_0.ts new file mode 100644 index 0000000..f84da3b --- /dev/null +++ b/src/migrations/1666580390462-update-table_1_9_0.ts @@ -0,0 +1,34 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class updateTable1901666580390462 implements MigrationInterface { + name = 'updateTable1901666580390462' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE \`shared\` (\`createdAt\` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), \`updatedAt\` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`uuid\` int NOT NULL AUTO_INCREMENT, \`uniqueID\` varchar(36) NOT NULL, \`projectID\` int NOT NULL, PRIMARY KEY (\`uuid\`)) ENGINE=InnoDB`); + await queryRunner.query(`ALTER TABLE \`api_data\` DROP COLUMN \`name\``); + await queryRunner.query(`ALTER TABLE \`api_data\` ADD \`name\` varchar(255) NOT NULL`); + await queryRunner.query(`ALTER TABLE \`api_group\` DROP COLUMN \`name\``); + await queryRunner.query(`ALTER TABLE \`api_group\` ADD \`name\` varchar(255) NOT NULL`); + await queryRunner.query(`ALTER TABLE \`project\` DROP COLUMN \`name\``); + await queryRunner.query(`ALTER TABLE \`project\` ADD \`name\` varchar(255) NOT NULL`); + await queryRunner.query(`ALTER TABLE \`mock\` DROP COLUMN \`name\``); + await queryRunner.query(`ALTER TABLE \`mock\` ADD \`name\` varchar(255) NOT NULL`); + await queryRunner.query(`ALTER TABLE \`environment\` DROP COLUMN \`name\``); + await queryRunner.query(`ALTER TABLE \`environment\` ADD \`name\` varchar(255) NOT NULL`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE \`environment\` DROP COLUMN \`name\``); + await queryRunner.query(`ALTER TABLE \`environment\` ADD \`name\` varchar(500) NOT NULL`); + await queryRunner.query(`ALTER TABLE \`mock\` DROP COLUMN \`name\``); + await queryRunner.query(`ALTER TABLE \`mock\` ADD \`name\` varchar(500) NOT NULL`); + await queryRunner.query(`ALTER TABLE \`project\` DROP COLUMN \`name\``); + await queryRunner.query(`ALTER TABLE \`project\` ADD \`name\` varchar(500) NOT NULL`); + await queryRunner.query(`ALTER TABLE \`api_group\` DROP COLUMN \`name\``); + await queryRunner.query(`ALTER TABLE \`api_group\` ADD \`name\` varchar(500) NOT NULL`); + await queryRunner.query(`ALTER TABLE \`api_data\` DROP COLUMN \`name\``); + await queryRunner.query(`ALTER TABLE \`api_data\` ADD \`name\` varchar(500) NOT NULL`); + await queryRunner.query(`DROP TABLE \`shared\``); + } + +} diff --git a/src/modules/auth/guards/jwt-auth.guard.ts b/src/modules/auth/guards/jwt-auth.guard.ts index 34bac50..6251c1b 100644 --- a/src/modules/auth/guards/jwt-auth.guard.ts +++ b/src/modules/auth/guards/jwt-auth.guard.ts @@ -53,6 +53,7 @@ export class JwtAuthGuard implements CanActivate { throw new UnauthorizedException('您的密码已更新,请重新登录'); } } catch (e) { + console.log('e', e); // 无法通过token校验 throw new UnauthorizedException('token已失效,请重新登录'); } @@ -60,8 +61,12 @@ export class JwtAuthGuard implements CanActivate { throw new UnauthorizedException('当前用户不存在'); } - const workspaceID = Number(request?.params?.workspaceID); - const projectID = Number(request?.params?.projectID); + const workspaceID = Number( + request?.params?.workspaceID || request.headers['x-workspace-id'], + ); + const projectID = Number( + request?.params?.projectID || request.headers['x-project-id'], + ); if (!Number.isNaN(workspaceID)) { const hasWorkspaceAuth = await this.userService.findOneBy({ id: request.currentUser.userId, diff --git a/src/modules/shared-docs/dto/index.ts b/src/modules/shared-docs/dto/index.ts new file mode 100644 index 0000000..5888fb8 --- /dev/null +++ b/src/modules/shared-docs/dto/index.ts @@ -0,0 +1,3 @@ +export * from './login.dto'; +export * from './logout.dto'; +export * from './refresh.dto'; diff --git a/src/modules/shared-docs/dto/login.dto.ts b/src/modules/shared-docs/dto/login.dto.ts new file mode 100644 index 0000000..be1f6db --- /dev/null +++ b/src/modules/shared-docs/dto/login.dto.ts @@ -0,0 +1,20 @@ +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 new file mode 100644 index 0000000..bed919c --- /dev/null +++ b/src/modules/shared-docs/dto/logout.dto.ts @@ -0,0 +1,8 @@ +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 new file mode 100644 index 0000000..93919fd --- /dev/null +++ b/src/modules/shared-docs/dto/refresh.dto.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsString } from 'class-validator'; + +export class JwtRefreshTokenDto { + @ApiProperty() + @IsString() + refreshToken: string; +} diff --git a/src/modules/shared-docs/guards/shared-auth.guard.ts b/src/modules/shared-docs/guards/shared-auth.guard.ts new file mode 100644 index 0000000..1af278d --- /dev/null +++ b/src/modules/shared-docs/guards/shared-auth.guard.ts @@ -0,0 +1,53 @@ +import { + CanActivate, + ExecutionContext, + Injectable, + NotFoundException, +} from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { IS_PUBLIC_KEY } from '@/common/decorators/public.decorator'; +import { ProjectService } from '@/modules/workspace/project/project.service'; +import { SharedDocsService } from '@/modules/shared-docs/shared-docs.service'; + +/** + * admin perm check guard + */ +@Injectable() +export class SharedAuthGuard implements CanActivate { + constructor( + private reflector: Reflector, + private sharedService: SharedDocsService, + private projectService: ProjectService, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + + const projectID = request.headers['x-project-id']; + + if (projectID) { + const projectIsExist = await this.projectService.findOneBy(projectID); + + if (!projectIsExist) { + throw new NotFoundException('操作失败!项目不存在'); + } + } + + // 分享的uuid + const uniqueID = request?.params?.uniqueID; + console.log('uniqueID', uniqueID); + if (uniqueID) { + const shared = await this.sharedService.findOneBy({ + uniqueID: uniqueID, + }); + + if (!shared) { + throw new NotFoundException('该分享不存在或已被取消分享'); + } + request.headers['x-project-id'] = shared.projectID; + } + + // pass + return true; + } +} diff --git a/src/modules/shared-docs/shared-docs.controller.ts b/src/modules/shared-docs/shared-docs.controller.ts new file mode 100644 index 0000000..ccbef58 --- /dev/null +++ b/src/modules/shared-docs/shared-docs.controller.ts @@ -0,0 +1,48 @@ +import { Controller, Get, Param, UseGuards } from '@nestjs/common'; +import { ApiOkResponse, 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'; + +@ApiTags('shared-docs') +@UseGuards(SharedAuthGuard) +@Controller('shared-docs') +export class SharedDocsController { + constructor( + private readonly apiDataService: ApiDataService, + private readonly projectService: ProjectService, + private readonly environmentService: EnvironmentService, + ) {} + + @ApiOperation({ + summary: '获取所有分组及API', + }) + @Public() + @ApiOkResponse({ type: LoginToken }) + @Get(':uniqueID/collections') + async collections(@XHeader('projectID') projectID: number) { + return this.projectService.getProjectCollections(projectID); + } + + @ApiOperation({ + summary: '获取API详情', + }) + @Public() + @Get(':uniqueID/api/:apiDataUUID') + async getApidataInfo(@Param('apiDataUUID') apiDataUUID: number) { + return this.apiDataService.findOne({ where: { uuid: apiDataUUID } }); + } + + @ApiOperation({ + summary: '获取环境列表', + }) + @Public() + @Get(':uniqueID/environments') + async environments(@XHeader('projectID') projectID: number) { + return this.environmentService.findAll({ where: { projectID } }); + } +} diff --git a/src/modules/shared-docs/shared-docs.module.ts b/src/modules/shared-docs/shared-docs.module.ts new file mode 100644 index 0000000..924b757 --- /dev/null +++ b/src/modules/shared-docs/shared-docs.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { SharedDocsController } from './shared-docs.controller'; +import { SharedDocsService } from './shared-docs.service'; +import { WorkspaceModule } from '@/modules/workspace/workspace.module'; +import { SharedEntity } from '@/entities/shared.entity'; +import { UserModule } from '@/modules/user/user.module'; + +@Module({ + imports: [ + TypeOrmModule.forFeature([SharedEntity]), + WorkspaceModule, + UserModule, + ], + controllers: [SharedDocsController], + providers: [SharedDocsService], + exports: [SharedDocsService], +}) +export class ShareDocsModule {} diff --git a/src/modules/shared-docs/shared-docs.service.ts b/src/modules/shared-docs/shared-docs.service.ts new file mode 100644 index 0000000..d71011d --- /dev/null +++ b/src/modules/shared-docs/shared-docs.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { FindOptionsWhere, Repository } from 'typeorm'; +import { SharedEntity } from '@/entities/shared.entity'; + +@Injectable() +export class SharedDocsService { + constructor( + @InjectRepository(SharedEntity) + private readonly sharedEntityRepository: Repository, + ) {} + + async createShared(projectID: number) { + const shared = await this.findOneBy({ projectID }); + return shared ?? this.sharedEntityRepository.save({ projectID }); + } + + async deleteShared(uniqueID: string, projectID: number) { + return this.sharedEntityRepository.delete({ projectID, uniqueID }); + } + async getShared(projectID: number) { + return this.sharedEntityRepository.findBy({ projectID }); + } + + async findOneBy(where: FindOptionsWhere) { + return this.sharedEntityRepository.findOneBy(where); + } +} diff --git a/src/modules/shared-docs/strategies/jwt.strategy.ts b/src/modules/shared-docs/strategies/jwt.strategy.ts new file mode 100644 index 0000000..880726f --- /dev/null +++ b/src/modules/shared-docs/strategies/jwt.strategy.ts @@ -0,0 +1,26 @@ +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { PassportStrategy } from '@nestjs/passport'; +import { ExtractJwt, Strategy } from 'passport-jwt'; +import { UserEntity } from '@/entities/user.entity'; + +@Injectable() +export class JwtStrategy extends PassportStrategy(Strategy) { + constructor(configService: ConfigService) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: configService.get('jwt.secret'), + }); + } + + async validate(payload: any): Promise { + const { username } = payload; + const user = await this.userRepository.findOne({ username }); + + if (!user) { + throw new UnauthorizedException('用户不存在'); + } + return user; + } +} diff --git a/src/modules/workspace/project/project.controller.ts b/src/modules/workspace/project/project.controller.ts index 2537501..7b5b44a 100644 --- a/src/modules/workspace/project/project.controller.ts +++ b/src/modules/workspace/project/project.controller.ts @@ -10,7 +10,7 @@ import { ParseIntPipe, NotFoundException, } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { ProjectService } from './project.service'; import { CreateDto } from './dto/create.dto'; import { UpdateDto } from './dto/update.dto'; diff --git a/src/modules/workspace/shared/dto/create.dto.ts b/src/modules/workspace/shared/dto/create.dto.ts new file mode 100644 index 0000000..063e271 --- /dev/null +++ b/src/modules/workspace/shared/dto/create.dto.ts @@ -0,0 +1,9 @@ +export type CreateWay = 'system' | 'custom'; +export class CreateDto { + name: string; + description: string; + apiDataID: number; + projectID: number; + response: string; + createWay: string; +} diff --git a/src/modules/workspace/shared/dto/query.dto.ts b/src/modules/workspace/shared/dto/query.dto.ts new file mode 100644 index 0000000..840025e --- /dev/null +++ b/src/modules/workspace/shared/dto/query.dto.ts @@ -0,0 +1,5 @@ +export class QueryDto { + name: string; + projectID:number; + apiDataID:number; +} diff --git a/src/modules/workspace/shared/dto/update.dto.ts b/src/modules/workspace/shared/dto/update.dto.ts new file mode 100644 index 0000000..20d53c8 --- /dev/null +++ b/src/modules/workspace/shared/dto/update.dto.ts @@ -0,0 +1,4 @@ +import { PartialType } from '@nestjs/mapped-types'; +import { CreateDto } from './create.dto'; + +export class UpdateDto extends PartialType(CreateDto) {} diff --git a/src/modules/workspace/shared/shared.controller.ts b/src/modules/workspace/shared/shared.controller.ts new file mode 100644 index 0000000..6b35a75 --- /dev/null +++ b/src/modules/workspace/shared/shared.controller.ts @@ -0,0 +1,31 @@ +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'; + +@ApiTags('Shared') +@Controller(`${WORKSPACE_PROJECT_PREFIX}/shared`) +export class SharedController { + constructor(private readonly sharedService: SharedService) {} + + @Post() + @ApiOperation({ summary: '新增分享' }) + async createShared(@Param('projectID') projectID: number) { + return this.sharedService.createShared(projectID); + } + + @Delete(':uniqueID') + @ApiOperation({ summary: '删除分享' }) + async deleteShared( + @Param('projectID') projectID: number, + @Param('uniqueID') uniqueID: string, + ) { + return this.sharedService.deleteShared(uniqueID, projectID); + } + + @Get() + @ApiOperation({ summary: '获取已分享列表' }) + async getShared(@Param('projectID') projectID: number) { + return this.sharedService.getShared(projectID); + } +} diff --git a/src/modules/workspace/shared/shared.service.spec.ts b/src/modules/workspace/shared/shared.service.spec.ts new file mode 100644 index 0000000..e8d0514 --- /dev/null +++ b/src/modules/workspace/shared/shared.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { MockService } from './shared.service'; + +describe('MockService', () => { + let service: MockService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [MockService], + }).compile(); + + service = module.get(MockService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/src/modules/workspace/shared/shared.service.ts b/src/modules/workspace/shared/shared.service.ts new file mode 100644 index 0000000..f7ed1e6 --- /dev/null +++ b/src/modules/workspace/shared/shared.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { FindOptionsWhere, Repository } from 'typeorm'; +import { SharedEntity } from '@/entities/shared.entity'; + +@Injectable() +export class SharedService { + constructor( + @InjectRepository(SharedEntity) + private readonly sharedEntityRepository: Repository, + ) {} + + async createShared(projectID: number) { + const shared = await this.findOneBy({ projectID }); + return shared ?? this.sharedEntityRepository.save({ projectID }); + } + + async deleteShared(uniqueID: string, projectID: number) { + return this.sharedEntityRepository.delete({ projectID, uniqueID }); + } + async getShared(projectID: number) { + return this.sharedEntityRepository.findBy({ projectID }); + } + + async findOneBy(where: FindOptionsWhere) { + return this.sharedEntityRepository.findOneBy(where); + } +} diff --git a/src/modules/workspace/workspace.module.ts b/src/modules/workspace/workspace.module.ts index a7543fb..f5b2275 100644 --- a/src/modules/workspace/workspace.module.ts +++ b/src/modules/workspace/workspace.module.ts @@ -23,6 +23,9 @@ import { EnvironmentService } from '@/modules/workspace/environment/environment. import { ApiGroupService } from '@/modules/workspace/apiGroup/apiGroup.service'; import { ApiGroupController } from '@/modules/workspace/apiGroup/apiGroup.controller'; import { UserModule } from '@/modules/user/user.module'; +import { SharedController } from '@/modules/workspace/shared/shared.controller'; +import { SharedService } from '@/modules/workspace/shared/shared.service'; +import { SharedEntity } from '@/entities/shared.entity'; const commonProviders = [ WorkspaceService, @@ -32,6 +35,7 @@ const commonProviders = [ ProjectService, MockService, EnvironmentService, + SharedService, ]; @Module({ imports: [ @@ -44,6 +48,7 @@ const commonProviders = [ ApiTestHistory, ApiGroup, Environment, + SharedEntity, ]), UserModule, ], @@ -55,6 +60,7 @@ const commonProviders = [ ApiTestHistoryController, MockController, ProjectController, + SharedController, ], providers: [...commonProviders], exports: [...commonProviders],