From 5f5fd3e0354a610a1a34d581474580e6a1c505d0 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Mon, 21 Nov 2022 10:46:01 +0800 Subject: [PATCH 01/27] chore: build --- .env | 2 +- .github/workflows/build-dev.yml | 4 ++-- Dockerfile | 5 +++-- docker-compose.yaml | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/.env b/.env index 4d5edfd..36e8d34 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ # eoapi-server coinfigure -EOAPI_SERVER_PORT=3000 +EOAPI_SERVER_PORT=3300 EOAPI_API_PREFIX=/api # mysql configure diff --git a/.github/workflows/build-dev.yml b/.github/workflows/build-dev.yml index 666791e..c6e4dc7 100644 --- a/.github/workflows/build-dev.yml +++ b/.github/workflows/build-dev.yml @@ -3,7 +3,7 @@ name: Build Dev Image on: push: branches: - - 'release/1.9.1' + - 'dev' jobs: docker: @@ -23,6 +23,6 @@ jobs: uses: docker/build-push-action@v3 with: push: true - tags: eolinker/eoapi-remote-server:1.9.1 + tags: eolinker/eoapi-remote-server:dev - name: Image digest run: echo ${{ steps.docker_build.outputs.digest }} diff --git a/Dockerfile b/Dockerfile index 38f21d6..ca9c2a3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,8 @@ FROM node:lts-alpine as builder ENV PROJECT_DIR=/eoapi-remote-server \ MYSQL_HOST=mysql \ - MYSQL_PORT=3306 + MYSQL_PORT=3306 \ + EOAPI_SERVER_PORT=3000 # WORKDIR指令用于设置Dockerfile中的RUN、CMD和ENTRYPOINT指令执行命令的工作目录(默认为/目录),该指令在Dockerfile文件中可以出现多次, # 如果使用相对路径则为相对于WORKDIR上一次的值, @@ -40,7 +41,7 @@ RUN chmod +x ./wait-for-it.sh \ # RUN yarn cache clean # 容器对外暴露的端口号 -EXPOSE 3000 +EXPOSE $EOAPI_SERVER_PORT # 容器启动时执行的命令,类似npm run start # CMD ["yarn", "start:prod"] diff --git a/docker-compose.yaml b/docker-compose.yaml index 7a1c9c6..3e77b3d 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,7 +3,7 @@ version: '3' services: eoapi-remote-server: # build: 从当前路径构建镜像 - # build: . + build: . image: eolinker/eoapi-remote-server:latest container_name: eoapi-remote-server deploy: @@ -15,7 +15,7 @@ services: extra_hosts: - 'host.docker.internal:host-gateway' ports: - - '${EOAPI_SERVER_PORT}:3000' + - '${EOAPI_SERVER_PORT}:${EOAPI_SERVER_PORT}' networks: - eoapi_net From cc8d913719980c49a3e41d75a40d0386c62157ec Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Mon, 21 Nov 2022 11:53:54 +0800 Subject: [PATCH 02/27] chore: update config --- .env | 2 +- docker-compose.yaml | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 36e8d34..4d5edfd 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ # eoapi-server coinfigure -EOAPI_SERVER_PORT=3300 +EOAPI_SERVER_PORT=3000 EOAPI_API_PREFIX=/api # mysql configure diff --git a/docker-compose.yaml b/docker-compose.yaml index 3e77b3d..78db5a9 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,8 +3,8 @@ version: '3' services: eoapi-remote-server: # build: 从当前路径构建镜像 - build: . - image: eolinker/eoapi-remote-server:latest + # build: . + image: eolinker/eoapi-remote-server:dev container_name: eoapi-remote-server deploy: restart_policy: @@ -45,6 +45,8 @@ services: max_attempts: 3 extra_hosts: - 'host.docker.internal:host-gateway' + env_file: + - .env ports: - '${EOAPI_WEB_SERVER_POST}:80' # volumes: @@ -59,6 +61,8 @@ services: restart_policy: condition: on-failure max_attempts: 3 + env_file: + - .env extra_hosts: - 'host.docker.internal:host-gateway' ports: From 0b1642c9179d6bc64a42f39bbd2a42e58de67072 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Thu, 15 Dec 2022 00:32:20 +0800 Subject: [PATCH 03/27] fix: update add project logic --- .../workspace/project/project.controller.ts | 2 +- .../workspace/project/project.service.ts | 19 +++++++++++++++++-- src/setup-swagger.ts | 8 ++++---- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/modules/workspace/project/project.controller.ts b/src/modules/workspace/project/project.controller.ts index a5d064d..f608942 100644 --- a/src/modules/workspace/project/project.controller.ts +++ b/src/modules/workspace/project/project.controller.ts @@ -38,7 +38,7 @@ export class ProjectController { @Param('workspaceID', ParseIntPipe) workspaceID, @Body() createDto: CreateDto, ) { - const data = await this.service.create(createDto); + const data = await this.service.create(createDto, workspaceID); return this.findOne(workspaceID, `${data.uuid}`); } diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index 4a24148..2ba3429 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -10,19 +10,34 @@ import { QueryDto } from './dto/query.dto'; import { Child, Environment, ImportDto, ImportResult } from './dto/import.dto'; import { parseAndCheckApiData, parseAndCheckEnv } from './validate'; import { Project } from '@/entities/project.entity'; +import { WorkspaceEntity } from '@/entities/workspace.entity'; @Injectable() export class ProjectService { constructor( @InjectRepository(Project) private readonly repository: Repository, + @InjectRepository(WorkspaceEntity) + private workspaceRepository: Repository, private readonly apiDataService: ApiDataService, private readonly apiGroupService: ApiGroupService, private readonly environmentService: EnvironmentService, ) {} - async create(createDto: CreateDto) { - return await this.repository.save(createDto); + async create(createDto: CreateDto, workspaceID: number) { + const workspace = await this.workspaceRepository.findOne({ + where: { + id: workspaceID, + }, + relations: { + users: true, + }, + }); + return await this.repository.save({ + ...createDto, + workspace, + users: workspace.users, + }); } async save(project: DeepPartial) { diff --git a/src/setup-swagger.ts b/src/setup-swagger.ts index 2bb4699..e2cd2cc 100644 --- a/src/setup-swagger.ts +++ b/src/setup-swagger.ts @@ -6,12 +6,12 @@ export function setupSwagger(app: INestApplication): void { const configService: ConfigService = app.get(ConfigService); // 默认为启用 - const enable = configService.get('swagger.enable', true); + // const enable = configService.get('swagger.enable', true); // 判断是否需要启用 - if (!enable) { - return; - } + // if (!enable) { + // return; + // } const swaggerPath = configService.get('swagger.path', '/swagger-api'); const swaggerConfig = new DocumentBuilder() From 6f74c9c799e43b80697bb29d37f6f38bd16e4233 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Fri, 23 Dec 2022 09:43:26 +0800 Subject: [PATCH 04/27] feat: add some entity --- .env | 4 +- package.json | 6 +- scripts/auto-create-database.js | 27 + src/app.module.ts | 13 +- src/common/decorators/permission.decorator.ts | 6 + src/config/data-source.ts | 1 + src/entities/base.new.entity.ts | 70 ++ src/entities/permission.entity.ts | 19 + src/entities/project-user-role.entity.ts | 22 + src/entities/role-permission.entity.ts | 15 + src/entities/role.entity.ts | 23 + src/entities/workspace-user-role.entity.ts | 22 + src/enums/permission.enum.ts | 45 + src/enums/role.enum.ts | 9 + src/guards/index.ts | 2 + .../auth => }/guards/jwt-auth.guard.ts | 12 +- .../strategies => guards}/jwt.strategy.ts | 0 src/guards/roles.guard.ts | 24 + .../1671725826115-update-table_1_11_0.ts | 22 + src/modules/auth/auth.module.ts | 23 +- src/modules/auth/auth.service.ts | 10 +- src/modules/user/user.module.ts | 10 +- .../workspace/apiData/apiData.controller.ts | 3 + .../workspace/apiGroup/apiGroup.controller.ts | 3 + .../apiTestHistory.controller.ts | 3 + .../environment/environment.controller.ts | 3 + .../workspace/project/project.controller.ts | 5 +- .../workspace/project/project.service.ts | 1 + .../workspace/shared/shared.controller.ts | 11 +- src/modules/workspace/workspace.controller.ts | 8 +- src/modules/workspace/workspace.module.ts | 13 +- src/modules/workspace/workspace.service.ts | 11 +- src/setup-swagger.ts | 5 +- src/shared/services/base.service.ts | 67 ++ yarn.lock | 814 +++++++++--------- 35 files changed, 887 insertions(+), 445 deletions(-) create mode 100644 scripts/auto-create-database.js create mode 100644 src/common/decorators/permission.decorator.ts create mode 100644 src/entities/base.new.entity.ts create mode 100644 src/entities/permission.entity.ts create mode 100644 src/entities/project-user-role.entity.ts create mode 100644 src/entities/role-permission.entity.ts create mode 100644 src/entities/role.entity.ts create mode 100644 src/entities/workspace-user-role.entity.ts create mode 100644 src/enums/permission.enum.ts create mode 100644 src/enums/role.enum.ts create mode 100644 src/guards/index.ts rename src/{modules/auth => }/guards/jwt-auth.guard.ts (88%) rename src/{modules/auth/strategies => guards}/jwt.strategy.ts (100%) create mode 100644 src/guards/roles.guard.ts create mode 100644 src/migrations/1671725826115-update-table_1_11_0.ts create mode 100644 src/shared/services/base.service.ts diff --git a/.env b/.env index fbff061..d1f7d3a 100644 --- a/.env +++ b/.env @@ -4,8 +4,8 @@ EOAPI_API_PREFIX=/api # mysql configure TZ=Asia/Shanghai -MYSQL_HOST=mysql -MYSQL_PORT=3306 +MYSQL_HOST=localhost +MYSQL_PORT=33066 MYSQL_USERNAME=root MYSQL_DATABASE=eoapi MYSQL_PASSWORD=123456a. diff --git a/package.json b/package.json index 9d6065e..f642184 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,10 @@ "start:dev": "rimraf dist && cross-env NODE_ENV=development nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "cross-env NODE_ENV=production node dist/src/main.js", + "createDatabase": "node ./scripts/auto-create-database.js", "migration:create": "npx typeorm-ts-node-commonjs migration:create ./src/migrations/create-table", "migration:generate": "node ./scripts/migration-generate.js", - "migration:run": "npm run build&&npx typeorm-ts-node-commonjs migration:run -d ./src/config/data-source.ts", + "migration:run": "npm run createDatabase && npm run build&&npx typeorm-ts-node-commonjs migration:run -d ./src/config/data-source.ts", "migration:revert": "npx typeorm-ts-node-commonjs migration:revert -d ./src/config/data-source.ts", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "typeorm": "npx typeorm-ts-node-commonjs", @@ -46,6 +47,8 @@ "class-validator": "^0.13.2", "cross-env": "^7.0.3", "crypto-js": "^4.1.1", + "dayjs": "^1.11.7", + "lodash": "^4.17.21", "mysql2": "^2.3.3", "nanoid": "^3.3.4", "passport": "^0.6.0", @@ -64,6 +67,7 @@ "@types/crypto-js": "^4.1.1", "@types/express": "^4.17.14", "@types/jest": "29.2.2", + "@types/lodash": "^4.14.191", "@types/node": "^18.11.9", "@types/passport": "^1.0.11", "@types/passport-http": "^0.3.9", diff --git a/scripts/auto-create-database.js b/scripts/auto-create-database.js new file mode 100644 index 0000000..b33ecee --- /dev/null +++ b/scripts/auto-create-database.js @@ -0,0 +1,27 @@ +const mysql = require('mysql2'); +const dotenv = require('dotenv'); +dotenv.config(); + +const con = mysql.createConnection({ + host: process.env.MYSQL_HOST, + port: Number.parseInt(process.env.MYSQL_PORT, 10), + user: process.env.MYSQL_USERNAME, + password: process.env.MYSQL_PASSWORD || process.env.MYSQL_ROOT_PASSWORD, +}); + +con.connect((err) => { + if (err) throw err; + console.log('Connected!'); + + const sql = `CREATE DATABASE if not EXISTS ${process.env.MYSQL_DATABASE} CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci`; + + con.query(sql, (err) => { + if (err) { + console.log(err.sqlMessage); + } else { + console.log('Database created'); + } + + process.exit(); + }); +}); diff --git a/src/app.module.ts b/src/app.module.ts index ea4c0ad..46d3162 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,6 +1,7 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { ConfigModule, ConfigService } from '@nestjs/config'; +import { APP_GUARD } from '@nestjs/core'; import { AppController } from './app.controller'; import { AppService } from './app.service'; // 引入数据库的及配置文件 @@ -10,6 +11,7 @@ import { getConfiguration } from './config/configuration'; import { UserModule } from '@/modules/user/user.module'; import { WorkspaceModule } from '@/modules/workspace/workspace.module'; import { SharedModule } from '@/shared/shared.module'; +import { JwtAuthGuard } from '@/guards'; console.log('process.env.NODE_ENV', `.env.${process.env.NODE_ENV}`); @Module({ @@ -33,7 +35,8 @@ console.log('process.env.NODE_ENV', `.env.${process.env.NODE_ENV}`); logging: configService.get('database.logging'), timezone: configService.get('database.timezone'), // 时区 }), - }), // 数据库 + }), + // 数据库 WorkspaceModule, SharedModule, AuthModule, // 认证 @@ -41,6 +44,12 @@ console.log('process.env.NODE_ENV', `.env.${process.env.NODE_ENV}`); ShareDocsModule, ], controllers: [AppController], - providers: [AppService], + providers: [ + AppService, + { + provide: APP_GUARD, + useClass: JwtAuthGuard, + }, + ], }) export class AppModule {} diff --git a/src/common/decorators/permission.decorator.ts b/src/common/decorators/permission.decorator.ts new file mode 100644 index 0000000..ea26196 --- /dev/null +++ b/src/common/decorators/permission.decorator.ts @@ -0,0 +1,6 @@ +import { SetMetadata } from '@nestjs/common'; +import { Permission } from '@/enums/permission.enum'; + +export const PERMISSIONS_KEY = 'permissions'; +export const Permissions = (permissions: Permission) => + SetMetadata(PERMISSIONS_KEY, permissions); diff --git a/src/config/data-source.ts b/src/config/data-source.ts index 9cfd932..8d2ddb3 100644 --- a/src/config/data-source.ts +++ b/src/config/data-source.ts @@ -1,6 +1,7 @@ import 'reflect-metadata'; import * as dotenv from 'dotenv'; import { DataSource } from 'typeorm'; + import { getConfiguration } from './configuration'; dotenv.config(); diff --git a/src/entities/base.new.entity.ts b/src/entities/base.new.entity.ts new file mode 100644 index 0000000..634eb5f --- /dev/null +++ b/src/entities/base.new.entity.ts @@ -0,0 +1,70 @@ +import { ApiHideProperty, ApiProperty } from '@nestjs/swagger'; +import { + Column, + PrimaryGeneratedColumn, + CreateDateColumn, + UpdateDateColumn, + DeleteDateColumn, + Generated, +} from 'typeorm'; +import { Exclude, Transform } from 'class-transformer'; +import dayjs from 'dayjs'; + +export class BaseTimestampEntity { + @ApiProperty({ type: Date, description: '创建时间' }) + @Transform(({ value }) => dayjs(value).format('YYYY-MM-DD HH:mm:ss'), { + toPlainOnly: true, + }) + @CreateDateColumn({ + type: 'datetime', + nullable: false, + name: 'created_at', + comment: '创建时间', + }) + createdAt: Date | null; + + @ApiProperty({ type: Date, description: '更新时间' }) + @UpdateDateColumn({ + type: 'datetime', + name: 'updated_at', + comment: '更新时间', + }) + @Transform(({ value }) => dayjs(value).format('YYYY-MM-DD HH:mm:ss'), { + toPlainOnly: true, + }) + updatedAt: Date | null; + + @Exclude() + @DeleteDateColumn({ + type: 'datetime', + name: 'deleted_at', + select: false, + comment: '删除时间', + }) + @Transform(({ value }) => dayjs(value).format('YYYY-MM-DD HH:mm:ss'), { + toPlainOnly: true, + }) + deletedAt: Date; +} + +export class BaseGeneratedEntity extends BaseTimestampEntity { + @ApiHideProperty() + @Exclude() + @PrimaryGeneratedColumn({ name: 'id', comment: '主键ID' }) + id: number; + + @ApiProperty({ type: String, description: 'uuid' }) + @Column({ comment: '业务 UUID' }) + @Generated('uuid') + uuid: string; +} + +export class BaseEntity extends BaseGeneratedEntity { + @ApiProperty({ type: Number, description: '创建者' }) + @Column('bigint', { name: 'create_by', nullable: true, comment: '创建者' }) + createBy: number | null; + + @ApiProperty({ type: Number, description: '更新者' }) + @Column('bigint', { name: 'update_by', nullable: true, comment: '更新者' }) + updateBy: number | null; +} diff --git a/src/entities/permission.entity.ts b/src/entities/permission.entity.ts new file mode 100644 index 0000000..208efe0 --- /dev/null +++ b/src/entities/permission.entity.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Column, Entity } from 'typeorm'; +import { BaseEntity } from './base.new.entity'; + +@Entity({ name: 'permission' }) +export class Permission extends BaseEntity { + @ApiProperty({ type: String, description: '权限名称' }) + @Column({ type: 'varchar', length: 120, comment: '权限名称' }) + name: string; + + @ApiProperty({ type: Number, description: '权限状态' }) + @Column({ + type: 'tinyint', + width: 1, + default: 1, + comment: '状态:0=禁用 1=启用', + }) + status: number; +} diff --git a/src/entities/project-user-role.entity.ts b/src/entities/project-user-role.entity.ts new file mode 100644 index 0000000..d67f9a7 --- /dev/null +++ b/src/entities/project-user-role.entity.ts @@ -0,0 +1,22 @@ +import { Column, Entity } from 'typeorm'; +import { BaseEntity } from './base.new.entity'; + +@Entity({ name: 'project_user_role' }) +export class ProjectUserRoleEntity extends BaseEntity { + @Column({ + name: 'project_id', + type: 'int', + comment: '项目ID', + }) + projectID: number; + + @Column({ + name: 'user_id', + type: 'int', + comment: '用户ID', + }) + userID: number; + + @Column({ name: 'role_id', type: 'int', comment: '角色ID' }) + roleID: number; +} diff --git a/src/entities/role-permission.entity.ts b/src/entities/role-permission.entity.ts new file mode 100644 index 0000000..4c4b3c5 --- /dev/null +++ b/src/entities/role-permission.entity.ts @@ -0,0 +1,15 @@ +import { Column, Entity } from 'typeorm'; +import { BaseEntity } from './base.new.entity'; + +@Entity({ name: 'role_permission' }) +export class RolePermission extends BaseEntity { + @Column({ type: 'int', name: 'role_id', unsigned: true, comment: '角色ID' }) + roleID: number; + + @Column({ + type: 'int', + name: 'permission_id', + comment: '权限ID', + }) + permissionID: number; +} diff --git a/src/entities/role.entity.ts b/src/entities/role.entity.ts new file mode 100644 index 0000000..160ab3e --- /dev/null +++ b/src/entities/role.entity.ts @@ -0,0 +1,23 @@ +import { Column, Entity } from 'typeorm'; +import { BaseEntity } from './base.new.entity'; + +@Entity({ name: 'role' }) +export class RoleEntity extends BaseEntity { + @Column({ + type: 'varchar', + length: 50, + comment: '角色名称', + }) + name: string; + + @Column({ + type: 'tinyint', + width: 1, + unsigned: true, + comment: '角色类别: 1=空间 2=项目', + }) + module: number; + + @Column({ type: 'varchar', length: 200, default: '', comment: '角色备注' }) + remark: string; +} diff --git a/src/entities/workspace-user-role.entity.ts b/src/entities/workspace-user-role.entity.ts new file mode 100644 index 0000000..d891ce2 --- /dev/null +++ b/src/entities/workspace-user-role.entity.ts @@ -0,0 +1,22 @@ +import { Column, Entity } from 'typeorm'; +import { BaseEntity } from './base.new.entity'; + +@Entity({ name: 'workspace_user_role' }) +export class WorkspaceUserRoleEntity extends BaseEntity { + @Column({ + name: 'workspace_id', + type: 'int', + comment: '空间ID', + }) + workspaceID: number; + + @Column({ + name: 'user_id', + type: 'int', + comment: '用户ID', + }) + userID: number; + + @Column({ name: 'role_id', type: 'int', comment: '角色ID' }) + roleID: number; +} diff --git a/src/enums/permission.enum.ts b/src/enums/permission.enum.ts new file mode 100644 index 0000000..4fed7c4 --- /dev/null +++ b/src/enums/permission.enum.ts @@ -0,0 +1,45 @@ +export enum Permission { + /** workspace */ + UPDATE_WORKSPACE = 'update:workspace', + DELETE_WORKSPACE = 'delete:workspace', + VIEW_WORKSPACE = 'view:workspace', + ADD_WORKSPACE_MEMBER = 'add:workspace:member', + UPDATE_WORKSPACE_MEMBER = 'update:workspace:member', + DELETE_WORKSPACE__MEMBER = 'delete:workspace:member', + + /** project */ + VIEW_PROJECT_LIST = 'view:project:list', + VIEW_PROJECT = 'view:project', + UPDATE_PROJECT = 'update:project', + DELETE_PROJECT = 'delete:project', + IMPORT_PROJECT = 'import:project', + EXPORT_PROJECT = 'export:project', + + /** apiGroup */ + VIEW_API_GROUP = 'view:apiGroup', + CREATE_API_GROUP = 'create:apiGroup', + UPDATE_API_GROUP = 'update:apiGroup', + DELETE_API_GROUP = 'delete:apiGroup', + + /** apiData */ + VIEW_API_DATA = 'view:apiData', + CREATE_API_DATA = 'create:apiData', + UPDATE_API_DATA = 'update:apiData', + DELETE_API_DATA = 'delete:apiData', + + /** environment */ + VIEW_ENVIRONMENT = 'view:environment', + CREATE_ENVIRONMENT = 'create:environment', + UPDATE_ENVIRONMENT = 'update:environment', + DELETE_ENVIRONMENT = 'delete:environment', + + /** apiTestHistory */ + VIEW_API_TEST_HISTORY = 'view:apiTestHistory', + CREATE_API_TEST_HISTORY = 'create:apiTestHistory', + DELETE_API_TEST_HISTORY = 'delete:apiTestHistory', + + /** shared */ + CREATE_SHARED = 'create:shared', + VIEW_SHARED = 'view:shared', + DELETE_SHARED = 'delete:shared', +} diff --git a/src/enums/role.enum.ts b/src/enums/role.enum.ts new file mode 100644 index 0000000..bfcb049 --- /dev/null +++ b/src/enums/role.enum.ts @@ -0,0 +1,9 @@ +export enum WorkspaceRole { + Owner = 'owner', + Editor = 'editor', +} + +export enum ProjectRole { + Owner = 'owner', + Editor = 'editor', +} diff --git a/src/guards/index.ts b/src/guards/index.ts new file mode 100644 index 0000000..e174be2 --- /dev/null +++ b/src/guards/index.ts @@ -0,0 +1,2 @@ +export * from './jwt-auth.guard'; +export * from './roles.guard'; diff --git a/src/modules/auth/guards/jwt-auth.guard.ts b/src/guards/jwt-auth.guard.ts similarity index 88% rename from src/modules/auth/guards/jwt-auth.guard.ts rename to src/guards/jwt-auth.guard.ts index 6251c1b..9bcb4fc 100644 --- a/src/modules/auth/guards/jwt-auth.guard.ts +++ b/src/guards/jwt-auth.guard.ts @@ -7,6 +7,7 @@ import { } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { JwtService } from '@nestjs/jwt'; +import { ConfigService } from '@nestjs/config'; import { isEmpty } from 'lodash'; import { IS_PUBLIC_KEY } from '@/common/decorators/public.decorator'; import { AuthService } from '@/modules/auth/auth.service'; @@ -25,6 +26,7 @@ export class JwtAuthGuard implements CanActivate { private authService: AuthService, private userService: UserService, private projectService: ProjectService, + private readonly config: ConfigService, ) {} async canActivate(context: ExecutionContext): Promise { @@ -43,17 +45,19 @@ export class JwtAuthGuard implements CanActivate { } try { // 挂载对象到当前请求上 - request.currentUser = this.jwtService.verify(token); + request.currentUser = this.jwtService.verify(token, { + secret: this.config.get('jwt.secret'), + }); const isExit = await this.authService.findOne({ accessToken: token }); - const { passwordVersion } = await this.userService.findOne({ + const user = await this.userService.findOne({ where: { id: request.currentUser.userId }, select: ['passwordVersion'], }); - if (!isExit || passwordVersion !== request.currentUser.pv) { + if (!isExit || user?.passwordVersion !== request.currentUser.pv) { throw new UnauthorizedException('您的密码已更新,请重新登录'); } } catch (e) { - console.log('e', e); + console.log('JwtAuthGuard e', e); // 无法通过token校验 throw new UnauthorizedException('token已失效,请重新登录'); } diff --git a/src/modules/auth/strategies/jwt.strategy.ts b/src/guards/jwt.strategy.ts similarity index 100% rename from src/modules/auth/strategies/jwt.strategy.ts rename to src/guards/jwt.strategy.ts diff --git a/src/guards/roles.guard.ts b/src/guards/roles.guard.ts new file mode 100644 index 0000000..0fff32e --- /dev/null +++ b/src/guards/roles.guard.ts @@ -0,0 +1,24 @@ +import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; +import { Reflector } from '@nestjs/core'; +import { PERMISSIONS_KEY } from '@/common/decorators/permission.decorator'; +import { Permission } from '@/enums/permission.enum'; + +@Injectable() +export class RolesGuard implements CanActivate { + constructor(private reflector: Reflector) {} + + canActivate(context: ExecutionContext): boolean { + // 当前请求所需权限 + const currentPerm = this.reflector.getAllAndOverride( + PERMISSIONS_KEY, + [context.getHandler(), context.getClass()], + ); + console.log('currentPerm', currentPerm); + if (!currentPerm) { + return true; + } + return true; + // const { user } = context.switchToHttp().getRequest(); + // return requiredRoles.some((role) => user.roles?.includes(role)); + } +} diff --git a/src/migrations/1671725826115-update-table_1_11_0.ts b/src/migrations/1671725826115-update-table_1_11_0.ts new file mode 100644 index 0000000..39140da --- /dev/null +++ b/src/migrations/1671725826115-update-table_1_11_0.ts @@ -0,0 +1,22 @@ +import { MigrationInterface, QueryRunner } from "typeorm"; + +export class updateTable11101671725826115 implements MigrationInterface { + name = 'updateTable11101671725826115' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`CREATE TABLE \`permission\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`name\` varchar(120) NOT NULL COMMENT '权限名称', \`status\` tinyint(1) NOT NULL COMMENT '状态:0=禁用 1=启用' DEFAULT '1', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); + await queryRunner.query(`CREATE TABLE \`project_user_role\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`project_id\` int NOT NULL COMMENT '项目ID', \`user_id\` int NOT NULL COMMENT '用户ID', \`role_id\` int NOT NULL COMMENT '角色ID', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); + await queryRunner.query(`CREATE TABLE \`role_permission\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`role_id\` int UNSIGNED NOT NULL COMMENT '角色ID', \`permission_id\` int NOT NULL COMMENT '权限ID', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); + await queryRunner.query(`CREATE TABLE \`role\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`name\` varchar(50) NOT NULL COMMENT '角色名称', \`module\` tinyint(1) UNSIGNED NOT NULL COMMENT '角色类别: 1=空间 2=项目', \`remark\` varchar(200) NOT NULL COMMENT '角色备注' DEFAULT '', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); + await queryRunner.query(`CREATE TABLE \`workspace_user_role\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`workspace_id\` int NOT NULL COMMENT '空间ID', \`user_id\` int NOT NULL COMMENT '用户ID', \`role_id\` int NOT NULL COMMENT '角色ID', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE \`workspace_user_role\``); + await queryRunner.query(`DROP TABLE \`role\``); + await queryRunner.query(`DROP TABLE \`role_permission\``); + await queryRunner.query(`DROP TABLE \`project_user_role\``); + await queryRunner.query(`DROP TABLE \`permission\``); + } + +} diff --git a/src/modules/auth/auth.module.ts b/src/modules/auth/auth.module.ts index b9cf618..df3c55e 100644 --- a/src/modules/auth/auth.module.ts +++ b/src/modules/auth/auth.module.ts @@ -1,13 +1,10 @@ import { Module } from '@nestjs/common'; import { PassportModule } from '@nestjs/passport'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { JwtModule } from '@nestjs/jwt'; -import { APP_GUARD } from '@nestjs/core'; +import { ConfigModule } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AuthController } from './auth.controller'; import { UserModule } from '@/modules/user/user.module'; import { AuthService } from '@/modules/auth/auth.service'; -import { JwtAuthGuard } from '@/modules/auth/guards/jwt-auth.guard'; import { AuthEntity } from '@/entities/auth.entity'; import { WorkspaceModule } from '@/modules/workspace/workspace.module'; @@ -18,25 +15,9 @@ import { WorkspaceModule } from '@/modules/workspace/workspace.module'; ConfigModule, UserModule, WorkspaceModule, - PassportModule, - JwtModule.registerAsync({ - imports: [ConfigModule], - useFactory: (configService: ConfigService) => ({ - secret: configService.get('jwt.secret'), - signOptions: { expiresIn: '600s' }, - }), - inject: [ConfigService], - }), ], controllers: [AuthController], - providers: [ - AuthService, - // JwtStrategy, - { - provide: APP_GUARD, - useClass: JwtAuthGuard, - }, - ], + providers: [AuthService], exports: [AuthService], }) export class AuthModule {} diff --git a/src/modules/auth/auth.service.ts b/src/modules/auth/auth.service.ts index 714d394..0fd1df1 100644 --- a/src/modules/auth/auth.service.ts +++ b/src/modules/auth/auth.service.ts @@ -1,6 +1,4 @@ import { - forwardRef, - Inject, Injectable, OnModuleInit, UnauthorizedException, @@ -8,11 +6,11 @@ import { import { JwtService } from '@nestjs/jwt'; import { InjectRepository } from '@nestjs/typeorm'; import { ModuleRef } from '@nestjs/core'; +import { ConfigService } from '@nestjs/config'; import { DeleteResult, FindOptionsWhere, Repository } from 'typeorm'; import { nanoid } from 'nanoid'; import { LoginInfoDto } from '@/modules/auth/dto/login.dto'; import { UserService } from '@/modules/user/user.service'; -import { UserEntity } from '@/entities/user.entity'; import { AuthEntity } from '@/entities/auth.entity'; import { accessTokenExpiresIn, @@ -29,6 +27,7 @@ export class AuthService implements OnModuleInit { private readonly authEntityRepository: Repository, private readonly jwtService: JwtService, private moduleRef: ModuleRef, + private readonly config: ConfigService, ) {} onModuleInit() { this.userService = this.moduleRef.get(UserService, { strict: false }); @@ -105,7 +104,10 @@ export class AuthService implements OnModuleInit { const result = { accessToken: this.jwtService.sign( { userId: userEntity.id, pv: userEntity.passwordVersion }, - { expiresIn: accessTokenExpiresIn / 1000 }, + { + expiresIn: accessTokenExpiresIn / 1000, + secret: this.config.get('jwt.secret'), + }, ), refreshToken: nanoid(), accessTokenExpiresAt: date.getTime() + accessTokenExpiresIn, diff --git a/src/modules/user/user.module.ts b/src/modules/user/user.module.ts index 5dcf692..f2ae35a 100644 --- a/src/modules/user/user.module.ts +++ b/src/modules/user/user.module.ts @@ -3,9 +3,17 @@ import { TypeOrmModule } from '@nestjs/typeorm'; import { UserController } from './user.controller'; import { UserService } from './user.service'; import { UserEntity } from '@/entities/user.entity'; +import { WorkspaceUserRoleEntity } from '@/entities/workspace-user-role.entity'; +import { ProjectUserRoleEntity } from '@/entities/project-user-role.entity'; @Module({ - imports: [TypeOrmModule.forFeature([UserEntity])], + imports: [ + TypeOrmModule.forFeature([ + UserEntity, + WorkspaceUserRoleEntity, + ProjectUserRoleEntity, + ]), + ], controllers: [UserController], providers: [UserService], exports: [UserService], diff --git a/src/modules/workspace/apiData/apiData.controller.ts b/src/modules/workspace/apiData/apiData.controller.ts index ff64496..73e516f 100644 --- a/src/modules/workspace/apiData/apiData.controller.ts +++ b/src/modules/workspace/apiData/apiData.controller.ts @@ -7,6 +7,7 @@ import { Param, Delete, Query, + UseGuards, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { ValidateQueryPipe } from 'src/pipe/query.pipe'; @@ -21,9 +22,11 @@ import { ApiOkResponseData, } from '@/common/class/res.class'; import { ApiData } from '@/entities/apiData.entity'; +import { RolesGuard } from '@/guards'; @ApiTags('apiData') @Controller(`${WORKSPACE_PROJECT_PREFIX}/api_data`) +@UseGuards(RolesGuard) export class ApiDataController { private readonly JSON_FIELDS = [ 'requestHeaders', diff --git a/src/modules/workspace/apiGroup/apiGroup.controller.ts b/src/modules/workspace/apiGroup/apiGroup.controller.ts index 206fcce..d462e58 100644 --- a/src/modules/workspace/apiGroup/apiGroup.controller.ts +++ b/src/modules/workspace/apiGroup/apiGroup.controller.ts @@ -10,6 +10,7 @@ import { ParseIntPipe, NotFoundException, BadRequestException, + UseGuards, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { ValidateQueryPipe } from 'src/pipe/query.pipe'; @@ -23,9 +24,11 @@ import { ApiOkResponseData, } from '@/common/class/res.class'; import { ApiGroup } from '@/entities/apiGroup.entity'; +import { RolesGuard } from '@/guards'; @ApiTags('apiGroup') @Controller(`${WORKSPACE_PROJECT_PREFIX}/group`) +@UseGuards(RolesGuard) export class ApiGroupController { constructor(private readonly service: ApiGroupService) {} diff --git a/src/modules/workspace/apiTestHistory/apiTestHistory.controller.ts b/src/modules/workspace/apiTestHistory/apiTestHistory.controller.ts index d89225d..b6a4f25 100644 --- a/src/modules/workspace/apiTestHistory/apiTestHistory.controller.ts +++ b/src/modules/workspace/apiTestHistory/apiTestHistory.controller.ts @@ -7,6 +7,7 @@ import { Param, Delete, Query, + UseGuards, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { ValidateQueryPipe } from 'src/pipe/query.pipe'; @@ -21,9 +22,11 @@ import { ApiOkResponseData, } from '@/common/class/res.class'; import { ApiTestHistory } from '@/entities/apiTestHistory.entity'; +import { RolesGuard } from '@/guards'; @ApiTags('apiTestHistory') @Controller(`${WORKSPACE_PROJECT_PREFIX}/api_test_history`) +@UseGuards(RolesGuard) export class ApiTestHistoryController { private readonly JSON_FIELDS = ['general', 'request', 'response']; diff --git a/src/modules/workspace/environment/environment.controller.ts b/src/modules/workspace/environment/environment.controller.ts index 8209849..724c63b 100644 --- a/src/modules/workspace/environment/environment.controller.ts +++ b/src/modules/workspace/environment/environment.controller.ts @@ -7,6 +7,7 @@ import { Param, Delete, Query, + UseGuards, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { EnvironmentService } from './environment.service'; @@ -19,9 +20,11 @@ import { ApiOkResponseData, } from '@/common/class/res.class'; import { Environment } from '@/entities/environment.entity'; +import { RolesGuard } from '@/guards'; @ApiTags('Environment') @Controller(`${WORKSPACE_PROJECT_PREFIX}/environment`) +@UseGuards(RolesGuard) export class EnvironmentController { private readonly NOT_FOUND = { statusCode: 201, diff --git a/src/modules/workspace/project/project.controller.ts b/src/modules/workspace/project/project.controller.ts index f608942..2b79385 100644 --- a/src/modules/workspace/project/project.controller.ts +++ b/src/modules/workspace/project/project.controller.ts @@ -9,8 +9,9 @@ import { Query, ParseIntPipe, NotFoundException, + UseGuards, } from '@nestjs/common'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiTags } from '@nestjs/swagger'; import { ProjectService } from './project.service'; import { CreateDto } from './dto/create.dto'; import { UpdateDto } from './dto/update.dto'; @@ -26,9 +27,11 @@ import { ExportProjectResultDto, ExportCollectionsResultDto, } from '@/modules/workspace/project/dto/export.dto'; +import { RolesGuard } from '@/guards'; @ApiTags('Project') @Controller(`${WORKSPACE_ID_PREFIX}/project`) +@UseGuards(RolesGuard) export class ProjectController { constructor(private readonly service: ProjectService) {} diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index 2ba3429..47af3df 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -19,6 +19,7 @@ export class ProjectService { private readonly repository: Repository, @InjectRepository(WorkspaceEntity) private workspaceRepository: Repository, + private readonly apiDataService: ApiDataService, private readonly apiGroupService: ApiGroupService, private readonly environmentService: EnvironmentService, diff --git a/src/modules/workspace/shared/shared.controller.ts b/src/modules/workspace/shared/shared.controller.ts index c2a255b..6d5e156 100644 --- a/src/modules/workspace/shared/shared.controller.ts +++ b/src/modules/workspace/shared/shared.controller.ts @@ -1,12 +1,21 @@ -import { Controller, Get, Post, Param, Delete } from '@nestjs/common'; +import { + Controller, + Get, + Post, + Param, + Delete, + UseGuards, +} 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'; +import { RolesGuard } from '@/guards'; @ApiTags('Shared') @Controller(`${WORKSPACE_PROJECT_PREFIX}/shared`) +@UseGuards(RolesGuard) export class SharedController { constructor(private readonly sharedService: SharedService) {} diff --git a/src/modules/workspace/workspace.controller.ts b/src/modules/workspace/workspace.controller.ts index 41caf89..1e82a7e 100644 --- a/src/modules/workspace/workspace.controller.ts +++ b/src/modules/workspace/workspace.controller.ts @@ -10,6 +10,7 @@ import { Post, Put, UnauthorizedException, + UseGuards, } from '@nestjs/common'; import { ApiBearerAuth, @@ -29,10 +30,7 @@ import { import { WorkspaceEntity } from '@/entities/workspace.entity'; import { IUser, User } from '@/common/decorators/user.decorator'; import { UserEntity } from '@/entities/user.entity'; -import { - Collections, - ImportDto, -} from '@/modules/workspace/project/dto/import.dto'; +import { ImportDto } 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'; @@ -40,10 +38,12 @@ import { ApiCreatedResponseData, ApiOkResponseData, } from '@/common/class/res.class'; +import { RolesGuard } from '@/guards'; @ApiBearerAuth() @ApiTags('workspace') @Controller('workspace') +@UseGuards(RolesGuard) export class WorkspaceController { constructor( private readonly workspaceService: WorkspaceService, diff --git a/src/modules/workspace/workspace.module.ts b/src/modules/workspace/workspace.module.ts index f5b2275..ca85ad8 100644 --- a/src/modules/workspace/workspace.module.ts +++ b/src/modules/workspace/workspace.module.ts @@ -1,5 +1,8 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; +import { JwtService } from '@nestjs/jwt'; +import { ConfigService } from '@nestjs/config'; +import { WorkspaceUserRoleEntity } from './../../entities/workspace-user-role.entity'; import { WorkspaceController } from './workspace.controller'; import { WorkspaceService } from './workspace.service'; import { WorkspaceEntity } from '@/entities/workspace.entity'; @@ -26,6 +29,9 @@ 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'; +import { AuthEntity } from '@/entities/auth.entity'; +import { AuthService } from '@/modules/auth/auth.service'; +import { JwtStrategy } from '@/guards/jwt.strategy'; const commonProviders = [ WorkspaceService, @@ -36,11 +42,15 @@ const commonProviders = [ MockService, EnvironmentService, SharedService, + JwtService, + AuthService, + ConfigService, ]; @Module({ imports: [ TypeOrmModule.forFeature([ WorkspaceEntity, + WorkspaceUserRoleEntity, UserEntity, Project, ApiData, @@ -49,6 +59,7 @@ const commonProviders = [ ApiGroup, Environment, SharedEntity, + AuthEntity, ]), UserModule, ], @@ -62,7 +73,7 @@ const commonProviders = [ ProjectController, SharedController, ], - providers: [...commonProviders], + providers: [...commonProviders, JwtStrategy], exports: [...commonProviders], }) export class WorkspaceModule {} diff --git a/src/modules/workspace/workspace.service.ts b/src/modules/workspace/workspace.service.ts index f78d2e9..b52ab2d 100644 --- a/src/modules/workspace/workspace.service.ts +++ b/src/modules/workspace/workspace.service.ts @@ -1,6 +1,7 @@ import { Injectable, OnModuleInit } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { ModuleRef } from '@nestjs/core'; +import { uniqBy } from 'lodash'; import { DeleteResult, FindOneOptions, In, Like, Repository } from 'typeorm'; import { CreateWorkspaceDto, @@ -118,11 +119,11 @@ export class WorkspaceService implements OnModuleInit { }, }); users.forEach((user) => { - user.workspaces.push({ - ...workspace, - users: [], - }); - user.projects.push(...workspace.projects); + // user.workspaces.push({ + // ...workspace, + // users: [], + // }); + user.projects = uniqBy([...user.projects, ...workspace.projects], 'uuid'); this.userService.updateUser(user); }); workspace.users.push(...users); diff --git a/src/setup-swagger.ts b/src/setup-swagger.ts index e2cd2cc..7c1b6cf 100644 --- a/src/setup-swagger.ts +++ b/src/setup-swagger.ts @@ -13,7 +13,10 @@ export function setupSwagger(app: INestApplication): void { // return; // } - const swaggerPath = configService.get('swagger.path', '/swagger-api'); + const swaggerPath = configService.get( + 'swagger.path', + '/swagger-docs', + ); const swaggerConfig = new DocumentBuilder() .setTitle(configService.get('swagger.title')) .setDescription(configService.get('swagger.desc')) diff --git a/src/shared/services/base.service.ts b/src/shared/services/base.service.ts new file mode 100644 index 0000000..6d1e6b5 --- /dev/null +++ b/src/shared/services/base.service.ts @@ -0,0 +1,67 @@ +// BaseService.ts +import { Injectable } from '@nestjs/common'; +import { + Repository, + DeleteResult, + SaveOptions, + RemoveOptions, + DeepPartial, + FindOneOptions, +} from 'typeorm'; +import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'; + +/** + * 服务基类,实现一些共有的基本方法,这样就不用每个服务类在写一遍了,直接继承该类即可 + */ +@Injectable() +export class BaseService { + protected readonly repository: Repository; + constructor(repository: Repository) { + this.repository = repository; + } + + async saveOne(entity: DeepPartial, options?: SaveOptions): Promise { + return await this.repository.save(entity, options); + } + + async saveMany(entities: DeepPartial[], options?: SaveOptions) { + return await this.repository.save(entities, options); + } + + async findOne(options?: FindOneOptions): Promise { + return await this.repository.findOne(options); + } + + async findMany(options?: FindOneOptions): Promise { + return await this.repository.find(options); + } + + async findAll(): Promise { + return await this.repository.find(); + } + + async removeOne(entity: T, options?: RemoveOptions): Promise { + return await this.repository.remove(entity, options); + } + + async removeMany(entities: T[], options?: RemoveOptions): Promise { + return await this.repository.remove(entities, options); + } + + async delete( + options?: Parameters['delete']>[number], + ): Promise { + return await this.repository.delete(options); + } + + async update( + conditions: Parameters['update']>[0], + newValue: QueryDeepPartialEntity, + ): Promise { + let updateResult = 1; + await this.repository + .update(conditions, newValue) + .catch((e) => (updateResult = 0)); + return updateResult; + } +} diff --git a/yarn.lock b/yarn.lock index 5efa681..3327a90 100644 --- a/yarn.lock +++ b/yarn.lock @@ -365,25 +365,25 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@eslint/eslintrc@^1.3.3": - version "1.3.3" - resolved "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" - integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== +"@eslint/eslintrc@^1.4.0": + version "1.4.0" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.0.tgz#8ec64e0df3e7a1971ee1ff5158da87389f167a63" + integrity sha512-7yfvXy6MWLgWSFsLhz5yH3iQ52St8cdUY6FoGieKkRDVxuxmrNuUetIuu6cmjNWwniUHiWXjxCr5tTXDrbYS5A== dependencies: ajv "^6.12.4" debug "^4.3.2" espree "^9.4.0" - globals "^13.15.0" + globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" js-yaml "^4.1.0" minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@humanwhocodes/config-array@^0.11.6": - version "0.11.7" - resolved "https://registry.npmmirror.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f" - integrity sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw== +"@humanwhocodes/config-array@^0.11.8": + version "0.11.8" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" + integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" @@ -415,28 +415,28 @@ resolved "https://registry.npmmirror.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jest/console@^29.2.1": - version "29.2.1" - resolved "https://registry.npmmirror.com/@jest/console/-/console-29.2.1.tgz#5f2c62dcdd5ce66e94b6d6729e021758bceea090" - integrity sha512-MF8Adcw+WPLZGBiNxn76DOuczG3BhODTcMlDCA4+cFi41OkaY/lyI0XUUhi73F88Y+7IHoGmD80pN5CtxQUdSw== +"@jest/console@^29.3.1": + version "29.3.1" + resolved "https://registry.npmjs.org/@jest/console/-/console-29.3.1.tgz#3e3f876e4e47616ea3b1464b9fbda981872e9583" + integrity sha512-IRE6GD47KwcqA09RIWrabKdHPiKDGgtAL31xDxbi/RjQMsr+lY+ppxmHwY0dUEV3qvvxZzoe5Hl0RXZJOjQNUg== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.3.1" "@types/node" "*" chalk "^4.0.0" - jest-message-util "^29.2.1" - jest-util "^29.2.1" + jest-message-util "^29.3.1" + jest-util "^29.3.1" slash "^3.0.0" -"@jest/core@^29.2.2": - version "29.2.2" - resolved "https://registry.npmmirror.com/@jest/core/-/core-29.2.2.tgz#207aa8973d9de8769f9518732bc5f781efc3ffa7" - integrity sha512-susVl8o2KYLcZhhkvSB+b7xX575CX3TmSvxfeDjpRko7KmT89rHkXj6XkDkNpSeFMBzIENw5qIchO9HC9Sem+A== - dependencies: - "@jest/console" "^29.2.1" - "@jest/reporters" "^29.2.2" - "@jest/test-result" "^29.2.1" - "@jest/transform" "^29.2.2" - "@jest/types" "^29.2.1" +"@jest/core@^29.3.1": + version "29.3.1" + resolved "https://registry.npmjs.org/@jest/core/-/core-29.3.1.tgz#bff00f413ff0128f4debec1099ba7dcd649774a1" + integrity sha512-0ohVjjRex985w5MmO5L3u5GR1O30DexhBSpuwx2P+9ftyqHdJXnk7IUWiP80oHMvt7ubHCJHxV0a0vlKVuZirw== + dependencies: + "@jest/console" "^29.3.1" + "@jest/reporters" "^29.3.1" + "@jest/test-result" "^29.3.1" + "@jest/transform" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" @@ -444,32 +444,32 @@ exit "^0.1.2" graceful-fs "^4.2.9" jest-changed-files "^29.2.0" - jest-config "^29.2.2" - jest-haste-map "^29.2.1" - jest-message-util "^29.2.1" + jest-config "^29.3.1" + jest-haste-map "^29.3.1" + jest-message-util "^29.3.1" jest-regex-util "^29.2.0" - jest-resolve "^29.2.2" - jest-resolve-dependencies "^29.2.2" - jest-runner "^29.2.2" - jest-runtime "^29.2.2" - jest-snapshot "^29.2.2" - jest-util "^29.2.1" - jest-validate "^29.2.2" - jest-watcher "^29.2.2" + jest-resolve "^29.3.1" + jest-resolve-dependencies "^29.3.1" + jest-runner "^29.3.1" + jest-runtime "^29.3.1" + jest-snapshot "^29.3.1" + jest-util "^29.3.1" + jest-validate "^29.3.1" + jest-watcher "^29.3.1" micromatch "^4.0.4" - pretty-format "^29.2.1" + pretty-format "^29.3.1" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^29.2.2": - version "29.2.2" - resolved "https://registry.npmmirror.com/@jest/environment/-/environment-29.2.2.tgz#481e729048d42e87d04842c38aa4d09c507f53b0" - integrity sha512-OWn+Vhu0I1yxuGBJEFFekMYc8aGBGrY4rt47SOh/IFaI+D7ZHCk7pKRiSoZ2/Ml7b0Ony3ydmEHRx/tEOC7H1A== +"@jest/environment@^29.3.1": + version "29.3.1" + resolved "https://registry.npmjs.org/@jest/environment/-/environment-29.3.1.tgz#eb039f726d5fcd14698acd072ac6576d41cfcaa6" + integrity sha512-pMmvfOPmoa1c1QpfFW0nXYtNLpofqo4BrCIk6f2kW4JFeNlHV2t3vd+3iDLf31e2ot2Mec0uqZfmI+U0K2CFag== dependencies: - "@jest/fake-timers" "^29.2.2" - "@jest/types" "^29.2.1" + "@jest/fake-timers" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" - jest-mock "^29.2.2" + jest-mock "^29.3.1" "@jest/expect-utils@^29.0.3": version "29.0.3" @@ -478,53 +478,53 @@ dependencies: jest-get-type "^29.0.0" -"@jest/expect-utils@^29.2.2": - version "29.2.2" - resolved "https://registry.npmmirror.com/@jest/expect-utils/-/expect-utils-29.2.2.tgz#460a5b5a3caf84d4feb2668677393dd66ff98665" - integrity sha512-vwnVmrVhTmGgQzyvcpze08br91OL61t9O0lJMDyb6Y/D8EKQ9V7rGUb/p7PDt0GPzK0zFYqXWFo4EO2legXmkg== +"@jest/expect-utils@^29.3.1": + version "29.3.1" + resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.3.1.tgz#531f737039e9b9e27c42449798acb5bba01935b6" + integrity sha512-wlrznINZI5sMjwvUoLVk617ll/UYfGIZNxmbU+Pa7wmkL4vYzhV9R2pwVqUh4NWWuLQWkI8+8mOkxs//prKQ3g== dependencies: jest-get-type "^29.2.0" -"@jest/expect@^29.2.2": - version "29.2.2" - resolved "https://registry.npmmirror.com/@jest/expect/-/expect-29.2.2.tgz#81edbd33afbde7795ca07ff6b4753d15205032e4" - integrity sha512-zwblIZnrIVt8z/SiEeJ7Q9wKKuB+/GS4yZe9zw7gMqfGf4C5hBLGrVyxu1SzDbVSqyMSlprKl3WL1r80cBNkgg== +"@jest/expect@^29.3.1": + version "29.3.1" + resolved "https://registry.npmjs.org/@jest/expect/-/expect-29.3.1.tgz#456385b62894349c1d196f2d183e3716d4c6a6cd" + integrity sha512-QivM7GlSHSsIAWzgfyP8dgeExPRZ9BIe2LsdPyEhCGkZkoyA+kGsoIzbKAfZCvvRzfZioKwPtCZIt5SaoxYCvg== dependencies: - expect "^29.2.2" - jest-snapshot "^29.2.2" + expect "^29.3.1" + jest-snapshot "^29.3.1" -"@jest/fake-timers@^29.2.2": - version "29.2.2" - resolved "https://registry.npmmirror.com/@jest/fake-timers/-/fake-timers-29.2.2.tgz#d8332e6e3cfa99cde4bc87d04a17d6b699deb340" - integrity sha512-nqaW3y2aSyZDl7zQ7t1XogsxeavNpH6kkdq+EpXncIDvAkjvFD7hmhcIs1nWloengEWUoWqkqSA6MSbf9w6DgA== +"@jest/fake-timers@^29.3.1": + version "29.3.1" + resolved "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.3.1.tgz#b140625095b60a44de820876d4c14da1aa963f67" + integrity sha512-iHTL/XpnDlFki9Tq0Q1GGuVeQ8BHZGIYsvCO5eN/O/oJaRzofG9Xndd9HuSDBI/0ZS79pg0iwn07OMTQ7ngF2A== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.3.1" "@sinonjs/fake-timers" "^9.1.2" "@types/node" "*" - jest-message-util "^29.2.1" - jest-mock "^29.2.2" - jest-util "^29.2.1" + jest-message-util "^29.3.1" + jest-mock "^29.3.1" + jest-util "^29.3.1" -"@jest/globals@^29.2.2": - version "29.2.2" - resolved "https://registry.npmmirror.com/@jest/globals/-/globals-29.2.2.tgz#205ff1e795aa774301c2c0ba0be182558471b845" - integrity sha512-/nt+5YMh65kYcfBhj38B3Hm0Trk4IsuMXNDGKE/swp36yydBWfz3OXkLqkSvoAtPW8IJMSJDFCbTM2oj5SNprw== +"@jest/globals@^29.3.1": + version "29.3.1" + resolved "https://registry.npmjs.org/@jest/globals/-/globals-29.3.1.tgz#92be078228e82d629df40c3656d45328f134a0c6" + integrity sha512-cTicd134vOcwO59OPaB6AmdHQMCtWOe+/DitpTZVxWgMJ+YvXL1HNAmPyiGbSHmF/mXVBkvlm8YYtQhyHPnV6Q== dependencies: - "@jest/environment" "^29.2.2" - "@jest/expect" "^29.2.2" - "@jest/types" "^29.2.1" - jest-mock "^29.2.2" + "@jest/environment" "^29.3.1" + "@jest/expect" "^29.3.1" + "@jest/types" "^29.3.1" + jest-mock "^29.3.1" -"@jest/reporters@^29.2.2": - version "29.2.2" - resolved "https://registry.npmmirror.com/@jest/reporters/-/reporters-29.2.2.tgz#69b395f79c3a97ce969ce05ccf1a482e5d6de290" - integrity sha512-AzjL2rl2zJC0njIzcooBvjA4sJjvdoq98sDuuNs4aNugtLPSQ+91nysGKRF0uY1to5k0MdGMdOBggUsPqvBcpA== +"@jest/reporters@^29.3.1": + version "29.3.1" + resolved "https://registry.npmjs.org/@jest/reporters/-/reporters-29.3.1.tgz#9a6d78c109608e677c25ddb34f907b90e07b4310" + integrity sha512-GhBu3YFuDrcAYW/UESz1JphEAbvUjaY2vShRZRoRY1mxpCMB3yGSJ4j9n0GxVlEOdCf7qjvUfBCrTUUqhVfbRA== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.2.1" - "@jest/test-result" "^29.2.1" - "@jest/transform" "^29.2.2" - "@jest/types" "^29.2.1" + "@jest/console" "^29.3.1" + "@jest/test-result" "^29.3.1" + "@jest/transform" "^29.3.1" + "@jest/types" "^29.3.1" "@jridgewell/trace-mapping" "^0.3.15" "@types/node" "*" chalk "^4.0.0" @@ -537,9 +537,9 @@ istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.1.3" - jest-message-util "^29.2.1" - jest-util "^29.2.1" - jest-worker "^29.2.1" + jest-message-util "^29.3.1" + jest-util "^29.3.1" + jest-worker "^29.3.1" slash "^3.0.0" string-length "^4.0.1" strip-ansi "^6.0.0" @@ -561,42 +561,42 @@ callsites "^3.0.0" graceful-fs "^4.2.9" -"@jest/test-result@^29.2.1": - version "29.2.1" - resolved "https://registry.npmmirror.com/@jest/test-result/-/test-result-29.2.1.tgz#f42dbf7b9ae465d0a93eee6131473b8bb3bd2edb" - integrity sha512-lS4+H+VkhbX6z64tZP7PAUwPqhwj3kbuEHcaLuaBuB+riyaX7oa1txe0tXgrFj5hRWvZKvqO7LZDlNWeJ7VTPA== +"@jest/test-result@^29.3.1": + version "29.3.1" + resolved "https://registry.npmjs.org/@jest/test-result/-/test-result-29.3.1.tgz#92cd5099aa94be947560a24610aa76606de78f50" + integrity sha512-qeLa6qc0ddB0kuOZyZIhfN5q0e2htngokyTWsGriedsDhItisW7SDYZ7ceOe57Ii03sL988/03wAcBh3TChMGw== dependencies: - "@jest/console" "^29.2.1" - "@jest/types" "^29.2.1" + "@jest/console" "^29.3.1" + "@jest/types" "^29.3.1" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^29.2.2": - version "29.2.2" - resolved "https://registry.npmmirror.com/@jest/test-sequencer/-/test-sequencer-29.2.2.tgz#4ac7487b237e517a1f55e7866fb5553f6e0168b9" - integrity sha512-Cuc1znc1pl4v9REgmmLf0jBd3Y65UXJpioGYtMr/JNpQEIGEzkmHhy6W6DLbSsXeUA13TDzymPv0ZGZ9jH3eIw== +"@jest/test-sequencer@^29.3.1": + version "29.3.1" + resolved "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.3.1.tgz#fa24b3b050f7a59d48f7ef9e0b782ab65123090d" + integrity sha512-IqYvLbieTv20ArgKoAMyhLHNrVHJfzO6ARZAbQRlY4UGWfdDnLlZEF0BvKOMd77uIiIjSZRwq3Jb3Fa3I8+2UA== dependencies: - "@jest/test-result" "^29.2.1" + "@jest/test-result" "^29.3.1" graceful-fs "^4.2.9" - jest-haste-map "^29.2.1" + jest-haste-map "^29.3.1" slash "^3.0.0" -"@jest/transform@^29.2.2": - version "29.2.2" - resolved "https://registry.npmmirror.com/@jest/transform/-/transform-29.2.2.tgz#dfc03fc092b31ffea0c55917728e75bfcf8b5de6" - integrity sha512-aPe6rrletyuEIt2axxgdtxljmzH8O/nrov4byy6pDw9S8inIrTV+2PnjyP/oFHMSynzGxJ2s6OHowBNMXp/Jzg== +"@jest/transform@^29.3.1": + version "29.3.1" + resolved "https://registry.npmjs.org/@jest/transform/-/transform-29.3.1.tgz#1e6bd3da4af50b5c82a539b7b1f3770568d6e36d" + integrity sha512-8wmCFBTVGYqFNLWfcOWoVuMuKYPUBTnTMDkdvFtAYELwDOl9RGwOsvQWGPFxDJ8AWY9xM/8xCXdqmPK3+Q5Lug== dependencies: "@babel/core" "^7.11.6" - "@jest/types" "^29.2.1" + "@jest/types" "^29.3.1" "@jridgewell/trace-mapping" "^0.3.15" babel-plugin-istanbul "^6.1.1" chalk "^4.0.0" - convert-source-map "^1.4.0" + convert-source-map "^2.0.0" fast-json-stable-stringify "^2.1.0" graceful-fs "^4.2.9" - jest-haste-map "^29.2.1" + jest-haste-map "^29.3.1" jest-regex-util "^29.2.0" - jest-util "^29.2.1" + jest-util "^29.3.1" micromatch "^4.0.4" pirates "^4.0.4" slash "^3.0.0" @@ -614,10 +614,10 @@ "@types/yargs" "^17.0.8" chalk "^4.0.0" -"@jest/types@^29.2.1": - version "29.2.1" - resolved "https://registry.npmmirror.com/@jest/types/-/types-29.2.1.tgz#ec9c683094d4eb754e41e2119d8bdaef01cf6da0" - integrity sha512-O/QNDQODLnINEPAI0cl9U6zUIDXEWXt6IC1o2N2QENuos7hlGUIthlKyV4p6ki3TvXFX071blj8HUhgLGquPjw== +"@jest/types@^29.3.1": + version "29.3.1" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.3.1.tgz#7c5a80777cb13e703aeec6788d044150341147e3" + integrity sha512-d0S0jmmTpjnhCmNpApgX3jrUZgZ22ivKJRvL2lli5hpCRoNnp1f85r2/wpKfXuYu8E7Jjh1hGfhPyup1NM5AmA== dependencies: "@jest/schemas" "^29.0.0" "@types/istanbul-lib-coverage" "^2.0.0" @@ -725,13 +725,13 @@ webpack "5.74.0" webpack-node-externals "3.0.0" -"@nestjs/common@^9.1.6": - version "9.1.6" - resolved "https://registry.npmmirror.com/@nestjs/common/-/common-9.1.6.tgz#79d71fb702816c07d8479f5aec71815c1762eea7" - integrity sha512-9Ttk9va/BwEab36RSXLZdRoPUX3DZHUpzseKEfqHVhnaUIsIMt7lVd79GQ1FroQ2FZqoCwcLyBowevXhrE1Wnw== +"@nestjs/common@^9.2.0": + version "9.2.1" + resolved "https://registry.npmjs.org/@nestjs/common/-/common-9.2.1.tgz#24de19ee85a8f1747288980fe517b12753cf66ea" + integrity sha512-nZuo3oDsSSlC5mti/M2aCWTEIfHPGDXmBwWgPeCpRbrNz3IWd109rkajll+yxgidVjznAdBS9y00JkAVJblNYw== dependencies: iterare "1.2.1" - tslib "2.4.0" + tslib "2.4.1" uuid "9.0.0" "@nestjs/config@^2.2.0": @@ -744,17 +744,17 @@ lodash "4.17.21" uuid "8.3.2" -"@nestjs/core@^9.1.6": - version "9.1.6" - resolved "https://registry.npmmirror.com/@nestjs/core/-/core-9.1.6.tgz#b73a453a40470a1456fc404384b195e816da0828" - integrity sha512-B52nYYTDSH72f1DU0G14NQSPCviXRE9fCp2/gUHuWIfVfBwcmVBAxVgyB/jAIUAhhj1f5/2odwUiw194xYtRRA== +"@nestjs/core@^9.2.0": + version "9.2.1" + resolved "https://registry.npmjs.org/@nestjs/core/-/core-9.2.1.tgz#598e51a421a0aaafc568c1a02499f7c1f9491caf" + integrity sha512-a9GkXuu8uXgNgCVW+17iI8kLCltO+HwHpU2IhR+32JKnN2WEQ1YEWU4t3GJ2MNq44YkjIw9zrKvFkjJBlYrNbQ== dependencies: "@nuxtjs/opencollective" "0.3.2" fast-safe-stringify "2.1.1" iterare "1.2.1" object-hash "3.0.0" path-to-regexp "3.2.0" - tslib "2.4.0" + tslib "2.4.1" uuid "9.0.0" "@nestjs/jwt@^9.0.0": @@ -780,16 +780,16 @@ resolved "https://registry.yarnpkg.com/@nestjs/passport/-/passport-9.0.0.tgz#0571bb08f8043456bc6df44cd4f59ca5f10c9b9f" integrity sha512-Gnh8n1wzFPOLSS/94X1sUP4IRAoXTgG4odl7/AO5h+uwscEGXxJFercrZfqdAwkWhqkKWbsntM3j5mRy/6ZQDA== -"@nestjs/platform-express@^9.1.6": - version "9.1.6" - resolved "https://registry.npmmirror.com/@nestjs/platform-express/-/platform-express-9.1.6.tgz#01e0dc29bb02d1a3820a5da4f2a0a534b97c75cb" - integrity sha512-zZuB1g6yIPytPIzp0EYKWT+1d02hOQ5aU6VdtO6Qg0Wn1RjdjGh3kScsqdamMEjzt0WAfL8DjE5oqrLgvashsw== +"@nestjs/platform-express@^9.2.0": + version "9.2.1" + resolved "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.2.1.tgz#74b88a531239eaee3fe23af2f2912ebef313866f" + integrity sha512-7PecaXt8lrdS1p6Vb1X/am3GGv+EO1VahyDzaEGOK6C0zwhc0VPfLtwihkjjfhS6BjpRIXXgviwEjONUvxVZnA== dependencies: body-parser "1.20.1" cors "2.8.5" express "4.18.2" multer "1.4.4-lts.1" - tslib "2.4.0" + tslib "2.4.1" "@nestjs/schematics@^9.0.0", "@nestjs/schematics@^9.0.3": version "9.0.3" @@ -813,12 +813,12 @@ path-to-regexp "3.2.0" swagger-ui-dist "4.15.1" -"@nestjs/testing@^9.1.6": - version "9.1.6" - resolved "https://registry.npmmirror.com/@nestjs/testing/-/testing-9.1.6.tgz#e85601d8cb5158bec4a5029b435c352dc440be9c" - integrity sha512-3kdTgiv5DHRHMQ/befLWd/60LZWjOM2V+SwOLqGkITt1yjo+jpkDze8/f0ZXVGEa6pcWYZ5oeedbL2T1nyUKSQ== +"@nestjs/testing@^9.2.0": + version "9.2.1" + resolved "https://registry.npmjs.org/@nestjs/testing/-/testing-9.2.1.tgz#2a3f64214d58ec4ab878862395407947671e4ece" + integrity sha512-lemXZdRSuqoZ87l0orCrS/c7gqwxeduIFOd21g9g2RUeQ4qlWPegbQDKASzbfC28klPyrgJLW4MNq7uv2JwV8w== dependencies: - tslib "2.4.0" + tslib "2.4.1" "@nestjs/typeorm@^9.0.1": version "9.0.1" @@ -1040,10 +1040,10 @@ dependencies: "@types/istanbul-lib-report" "*" -"@types/jest@29.2.1": - version "29.2.1" - resolved "https://registry.npmmirror.com/@types/jest/-/jest-29.2.1.tgz#31fda30bdf2861706abc5f1730be78bed54f83ee" - integrity sha512-nKixEdnGDqFOZkMTF74avFNr3yRqB1ZJ6sRZv5/28D5x2oLN14KApv7F9mfDT/vUic0L3tRCsh3XWpWjtJisUQ== +"@types/jest@29.2.2": + version "29.2.2" + resolved "https://registry.npmjs.org/@types/jest/-/jest-29.2.2.tgz#874e7dc6702fa6a3fe6107792aa98636dcc480b4" + integrity sha512-og1wAmdxKoS71K2ZwSVqWPX6OVn3ihZ6ZT2qvZvZQm90lJVDyXIjYcu4Khx2CNIeaFv12rOU/YObOsI3VOkzog== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -1065,6 +1065,11 @@ dependencies: "@types/node" "*" +"@types/lodash@^4.14.191": + version "4.14.191" + resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" + integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== + "@types/mime@*": version "3.0.1" resolved "https://registry.npmmirror.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" @@ -1167,14 +1172,14 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.42.0": - version "5.42.0" - resolved "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.42.0.tgz#36a8c0c379870127059889a9cc7e05c260d2aaa5" - integrity sha512-5TJh2AgL6+wpL8H/GTSjNb4WrjKoR2rqvFxR/DDTqYNk6uXn8BJMEcncLSpMbf/XV1aS0jAjYwn98uvVCiAywQ== +"@typescript-eslint/eslint-plugin@^5.42.1": + version "5.46.1" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.1.tgz#098abb4c9354e19f460d57ab18bff1f676a6cff0" + integrity sha512-YpzNv3aayRBwjs4J3oz65eVLXc9xx0PDbIRisHj+dYhvBn02MjYOD96P8YGiWEIFBrojaUjxvkaUpakD82phsA== dependencies: - "@typescript-eslint/scope-manager" "5.42.0" - "@typescript-eslint/type-utils" "5.42.0" - "@typescript-eslint/utils" "5.42.0" + "@typescript-eslint/scope-manager" "5.46.1" + "@typescript-eslint/type-utils" "5.46.1" + "@typescript-eslint/utils" "5.46.1" debug "^4.3.4" ignore "^5.2.0" natural-compare-lite "^1.4.0" @@ -1182,72 +1187,72 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/parser@^5.42.0": - version "5.42.0" - resolved "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-5.42.0.tgz#be0ffbe279e1320e3d15e2ef0ad19262f59e9240" - integrity sha512-Ixh9qrOTDRctFg3yIwrLkgf33AHyEIn6lhyf5cCfwwiGtkWhNpVKlEZApi3inGQR/barWnY7qY8FbGKBO7p3JA== +"@typescript-eslint/parser@^5.42.1": + version "5.46.1" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.46.1.tgz#1fc8e7102c1141eb64276c3b89d70da8c0ba5699" + integrity sha512-RelQ5cGypPh4ySAtfIMBzBGyrNerQcmfA1oJvPj5f+H4jI59rl9xxpn4bonC0tQvUKOEN7eGBFWxFLK3Xepneg== dependencies: - "@typescript-eslint/scope-manager" "5.42.0" - "@typescript-eslint/types" "5.42.0" - "@typescript-eslint/typescript-estree" "5.42.0" + "@typescript-eslint/scope-manager" "5.46.1" + "@typescript-eslint/types" "5.46.1" + "@typescript-eslint/typescript-estree" "5.46.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.42.0": - version "5.42.0" - resolved "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-5.42.0.tgz#e1f2bb26d3b2a508421ee2e3ceea5396b192f5ef" - integrity sha512-l5/3IBHLH0Bv04y+H+zlcLiEMEMjWGaCX6WyHE5Uk2YkSGAMlgdUPsT/ywTSKgu9D1dmmKMYgYZijObfA39Wow== +"@typescript-eslint/scope-manager@5.46.1": + version "5.46.1" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.46.1.tgz#70af8425c79bbc1178b5a63fb51102ddf48e104a" + integrity sha512-iOChVivo4jpwUdrJZyXSMrEIM/PvsbbDOX1y3UCKjSgWn+W89skxWaYXACQfxmIGhPVpRWK/VWPYc+bad6smIA== dependencies: - "@typescript-eslint/types" "5.42.0" - "@typescript-eslint/visitor-keys" "5.42.0" + "@typescript-eslint/types" "5.46.1" + "@typescript-eslint/visitor-keys" "5.46.1" -"@typescript-eslint/type-utils@5.42.0": - version "5.42.0" - resolved "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-5.42.0.tgz#4206d7192d4fe903ddf99d09b41d4ac31b0b7dca" - integrity sha512-HW14TXC45dFVZxnVW8rnUGnvYyRC0E/vxXShFCthcC9VhVTmjqOmtqj6H5rm9Zxv+ORxKA/1aLGD7vmlLsdlOg== +"@typescript-eslint/type-utils@5.46.1": + version "5.46.1" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.46.1.tgz#195033e4b30b51b870dfcf2828e88d57b04a11cc" + integrity sha512-V/zMyfI+jDmL1ADxfDxjZ0EMbtiVqj8LUGPAGyBkXXStWmCUErMpW873zEHsyguWCuq2iN4BrlWUkmuVj84yng== dependencies: - "@typescript-eslint/typescript-estree" "5.42.0" - "@typescript-eslint/utils" "5.42.0" + "@typescript-eslint/typescript-estree" "5.46.1" + "@typescript-eslint/utils" "5.46.1" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.42.0": - version "5.42.0" - resolved "https://registry.npmmirror.com/@typescript-eslint/types/-/types-5.42.0.tgz#5aeff9b5eced48f27d5b8139339bf1ef805bad7a" - integrity sha512-t4lzO9ZOAUcHY6bXQYRuu+3SSYdD9TS8ooApZft4WARt4/f2Cj/YpvbTe8A4GuhT4bNW72goDMOy7SW71mZwGw== +"@typescript-eslint/types@5.46.1": + version "5.46.1" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.46.1.tgz#4e9db2107b9a88441c4d5ecacde3bb7a5ebbd47e" + integrity sha512-Z5pvlCaZgU+93ryiYUwGwLl9AQVB/PQ1TsJ9NZ/gHzZjN7g9IAn6RSDkpCV8hqTwAiaj6fmCcKSQeBPlIpW28w== -"@typescript-eslint/typescript-estree@5.42.0": - version "5.42.0" - resolved "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.42.0.tgz#2592d24bb5f89bf54a63384ff3494870f95b3fd8" - integrity sha512-2O3vSq794x3kZGtV7i4SCWZWCwjEtkWfVqX4m5fbUBomOsEOyd6OAD1qU2lbvV5S8tgy/luJnOYluNyYVeOTTg== +"@typescript-eslint/typescript-estree@5.46.1": + version "5.46.1" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.1.tgz#5358088f98a8f9939355e0996f9c8f41c25eced2" + integrity sha512-j9W4t67QiNp90kh5Nbr1w92wzt+toiIsaVPnEblB2Ih2U9fqBTyqV9T3pYWZBRt6QoMh/zVWP59EpuCjc4VRBg== dependencies: - "@typescript-eslint/types" "5.42.0" - "@typescript-eslint/visitor-keys" "5.42.0" + "@typescript-eslint/types" "5.46.1" + "@typescript-eslint/visitor-keys" "5.46.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.42.0": - version "5.42.0" - resolved "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-5.42.0.tgz#f06bd43b9a9a06ed8f29600273240e84a53f2f15" - integrity sha512-JZ++3+h1vbeG1NUECXQZE3hg0kias9kOtcQr3+JVQ3whnjvKuMyktJAAIj6743OeNPnGBmjj7KEmiDL7qsdnCQ== +"@typescript-eslint/utils@5.46.1": + version "5.46.1" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.46.1.tgz#7da3c934d9fd0eb4002a6bb3429f33298b469b4a" + integrity sha512-RBdBAGv3oEpFojaCYT4Ghn4775pdjvwfDOfQ2P6qzNVgQOVrnSPe5/Pb88kv7xzYQjoio0eKHKB9GJ16ieSxvA== dependencies: "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.42.0" - "@typescript-eslint/types" "5.42.0" - "@typescript-eslint/typescript-estree" "5.42.0" + "@typescript-eslint/scope-manager" "5.46.1" + "@typescript-eslint/types" "5.46.1" + "@typescript-eslint/typescript-estree" "5.46.1" eslint-scope "^5.1.1" eslint-utils "^3.0.0" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.42.0": - version "5.42.0" - resolved "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.42.0.tgz#ee8d62d486f41cfe646632fab790fbf0c1db5bb0" - integrity sha512-QHbu5Hf/2lOEOwy+IUw0GoSCuAzByTAWWrOTKzTzsotiUnWFpuKnXcAhC9YztAf2EElQ0VvIK+pHJUPkM0q7jg== +"@typescript-eslint/visitor-keys@5.46.1": + version "5.46.1" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.1.tgz#126cc6fe3c0f83608b2b125c5d9daced61394242" + integrity sha512-jczZ9noovXwy59KjRTk1OftT78pwygdcmCuBf8yMoWt/8O8l+6x2LSEze0E4TeepXK4MezW3zGSyoDRZK7Y9cg== dependencies: - "@typescript-eslint/types" "5.42.0" + "@typescript-eslint/types" "5.46.1" eslint-visitor-keys "^3.3.0" "@webassemblyjs/ast@1.11.1": @@ -1567,12 +1572,12 @@ axios@1.1.3: form-data "^4.0.0" proxy-from-env "^1.1.0" -babel-jest@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/babel-jest/-/babel-jest-29.2.2.tgz#2c15abd8c2081293c9c3f4f80a4ed1d51542fee5" - integrity sha512-kkq2QSDIuvpgfoac3WZ1OOcHsQQDU5xYk2Ql7tLdJ8BVAYbefEXal+NfS45Y5LVZA7cxC8KYcQMObpCt1J025w== +babel-jest@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/babel-jest/-/babel-jest-29.3.1.tgz#05c83e0d128cd48c453eea851482a38782249f44" + integrity sha512-aard+xnMoxgjwV70t0L6wkW/3HQQtV+O0PEimxKgzNqCJnbYmroPojdP2tqKSOAt8QAKV/uSZU8851M7B5+fcA== dependencies: - "@jest/transform" "^29.2.2" + "@jest/transform" "^29.3.1" "@types/babel__core" "^7.1.14" babel-plugin-istanbul "^6.1.1" babel-preset-jest "^29.2.0" @@ -1992,13 +1997,18 @@ content-type@~1.0.4: resolved "https://registry.npmmirror.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== -convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: +convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.8.0" resolved "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== dependencies: safe-buffer "~5.1.1" +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.npmmirror.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -2069,6 +2079,11 @@ date-fns@^2.28.0: resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA== +dayjs@^1.11.7: + version "1.11.7" + resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2" + integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ== + debug@2.6.9, debug@^2.6.9: version "2.6.9" resolved "https://registry.npmmirror.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2158,10 +2173,10 @@ diff-sequences@^29.0.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.0.0.tgz#bae49972ef3933556bcb0800b72e8579d19d9e4f" integrity sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA== -diff-sequences@^29.2.0: - version "29.2.0" - resolved "https://registry.npmmirror.com/diff-sequences/-/diff-sequences-29.2.0.tgz#4c55b5b40706c7b5d2c5c75999a50c56d214e8f6" - integrity sha512-413SY5JpYeSBZxmenGEmCVQ8mCgtFJF0w9PROdaS6z987XC2Pd2GOKqOITLtMftmyFZqgtCOb/QA7/Z3ZXfzIw== +diff-sequences@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.3.1.tgz#104b5b95fe725932421a9c6e5b4bef84c3f2249e" + integrity sha512-hlM3QR272NXCi4pq+N4Kok4kOp6EsgOM3ZSpJI7Da3UAs+Ttsi8MRmB6trM/lhyzUxGfOgnpkHtgqm5Q/CTcfQ== diff@^4.0.1: version "4.0.2" @@ -2418,13 +2433,13 @@ eslint-visitor-keys@^3.3.0: resolved "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@^8.26.0: - version "8.26.0" - resolved "https://registry.npmmirror.com/eslint/-/eslint-8.26.0.tgz#2bcc8836e6c424c4ac26a5674a70d44d84f2181d" - integrity sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg== +eslint@^8.27.0: + version "8.30.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.30.0.tgz#83a506125d089eef7c5b5910eeea824273a33f50" + integrity sha512-MGADB39QqYuzEGov+F/qb18r4i7DohCDOfatHaxI2iGlPuC65bwG2gxgO+7DkyL38dRFaRH7RaRAgU6JKL9rMQ== dependencies: - "@eslint/eslintrc" "^1.3.3" - "@humanwhocodes/config-array" "^0.11.6" + "@eslint/eslintrc" "^1.4.0" + "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" ajv "^6.10.0" @@ -2443,7 +2458,7 @@ eslint@^8.26.0: file-entry-cache "^6.0.1" find-up "^5.0.0" glob-parent "^6.0.2" - globals "^13.15.0" + globals "^13.19.0" grapheme-splitter "^1.0.4" ignore "^5.2.0" import-fresh "^3.0.0" @@ -2562,16 +2577,16 @@ expect@^29.0.0: jest-message-util "^29.0.3" jest-util "^29.0.3" -expect@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/expect/-/expect-29.2.2.tgz#ba2dd0d7e818727710324a6e7f13dd0e6d086106" - integrity sha512-hE09QerxZ5wXiOhqkXy5d2G9ar+EqOyifnCXCpMNu+vZ6DG9TJ6CO2c2kPDSLqERTTWrO7OZj8EkYHQqSd78Yw== +expect@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/expect/-/expect-29.3.1.tgz#92877aad3f7deefc2e3f6430dd195b92295554a6" + integrity sha512-gGb1yTgU30Q0O/tQq+z30KBWv24ApkMgFUpvKBkyLUBL68Wv8dHdJxTBZFl/iT8K/bqDHvUYRH6IIN3rToopPA== dependencies: - "@jest/expect-utils" "^29.2.2" + "@jest/expect-utils" "^29.3.1" jest-get-type "^29.2.0" - jest-matcher-utils "^29.2.2" - jest-message-util "^29.2.1" - jest-util "^29.2.1" + jest-matcher-utils "^29.3.1" + jest-message-util "^29.3.1" + jest-util "^29.3.1" express@4.18.2: version "4.18.2" @@ -2924,10 +2939,10 @@ globals@^11.1.0: resolved "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== -globals@^13.15.0: - version "13.17.0" - resolved "https://registry.npmmirror.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" - integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== +globals@^13.19.0: + version "13.19.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.19.0.tgz#7a42de8e6ad4f7242fbcca27ea5b23aca367b5c8" + integrity sha512-dkQ957uSRWHw7CFXLUtUHQI3g3aWApYhfNR2O6jn/907riyTYKVBmxYVROkBcY614FSSeSJh7Xm7SrUWCxvJMQ== dependencies: type-fest "^0.20.2" @@ -3358,74 +3373,74 @@ jest-changed-files@^29.2.0: execa "^5.0.0" p-limit "^3.1.0" -jest-circus@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-circus/-/jest-circus-29.2.2.tgz#1dc4d35fd49bf5e64d3cc505fb2db396237a6dfa" - integrity sha512-upSdWxx+Mh4DV7oueuZndJ1NVdgtTsqM4YgywHEx05UMH5nxxA2Qu9T9T9XVuR021XxqSoaKvSmmpAbjwwwxMw== +jest-circus@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-circus/-/jest-circus-29.3.1.tgz#177d07c5c0beae8ef2937a67de68f1e17bbf1b4a" + integrity sha512-wpr26sEvwb3qQQbdlmei+gzp6yoSSoSL6GsLPxnuayZSMrSd5Ka7IjAvatpIernBvT2+Ic6RLTg+jSebScmasg== dependencies: - "@jest/environment" "^29.2.2" - "@jest/expect" "^29.2.2" - "@jest/test-result" "^29.2.1" - "@jest/types" "^29.2.1" + "@jest/environment" "^29.3.1" + "@jest/expect" "^29.3.1" + "@jest/test-result" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" chalk "^4.0.0" co "^4.6.0" dedent "^0.7.0" is-generator-fn "^2.0.0" - jest-each "^29.2.1" - jest-matcher-utils "^29.2.2" - jest-message-util "^29.2.1" - jest-runtime "^29.2.2" - jest-snapshot "^29.2.2" - jest-util "^29.2.1" + jest-each "^29.3.1" + jest-matcher-utils "^29.3.1" + jest-message-util "^29.3.1" + jest-runtime "^29.3.1" + jest-snapshot "^29.3.1" + jest-util "^29.3.1" p-limit "^3.1.0" - pretty-format "^29.2.1" + pretty-format "^29.3.1" slash "^3.0.0" stack-utils "^2.0.3" -jest-cli@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-cli/-/jest-cli-29.2.2.tgz#feaf0aa57d327e80d4f2f18d5f8cd2e77cac5371" - integrity sha512-R45ygnnb2CQOfd8rTPFR+/fls0d+1zXS6JPYTBBrnLPrhr58SSuPTiA5Tplv8/PXpz4zXR/AYNxmwIj6J6nrvg== +jest-cli@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-cli/-/jest-cli-29.3.1.tgz#e89dff427db3b1df50cea9a393ebd8640790416d" + integrity sha512-TO/ewvwyvPOiBBuWZ0gm04z3WWP8TIK8acgPzE4IxgsLKQgb377NYGrQLc3Wl/7ndWzIH2CDNNsUjGxwLL43VQ== dependencies: - "@jest/core" "^29.2.2" - "@jest/test-result" "^29.2.1" - "@jest/types" "^29.2.1" + "@jest/core" "^29.3.1" + "@jest/test-result" "^29.3.1" + "@jest/types" "^29.3.1" chalk "^4.0.0" exit "^0.1.2" graceful-fs "^4.2.9" import-local "^3.0.2" - jest-config "^29.2.2" - jest-util "^29.2.1" - jest-validate "^29.2.2" + jest-config "^29.3.1" + jest-util "^29.3.1" + jest-validate "^29.3.1" prompts "^2.0.1" yargs "^17.3.1" -jest-config@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-config/-/jest-config-29.2.2.tgz#bf98623a46454d644630c1f0de8bba3f495c2d59" - integrity sha512-Q0JX54a5g1lP63keRfKR8EuC7n7wwny2HoTRDb8cx78IwQOiaYUVZAdjViY3WcTxpR02rPUpvNVmZ1fkIlZPcw== +jest-config@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-config/-/jest-config-29.3.1.tgz#0bc3dcb0959ff8662957f1259947aedaefb7f3c6" + integrity sha512-y0tFHdj2WnTEhxmGUK1T7fgLen7YK4RtfvpLFBXfQkh2eMJAQq24Vx9472lvn5wg0MAO6B+iPfJfzdR9hJYalg== dependencies: "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.2.2" - "@jest/types" "^29.2.1" - babel-jest "^29.2.2" + "@jest/test-sequencer" "^29.3.1" + "@jest/types" "^29.3.1" + babel-jest "^29.3.1" chalk "^4.0.0" ci-info "^3.2.0" deepmerge "^4.2.2" glob "^7.1.3" graceful-fs "^4.2.9" - jest-circus "^29.2.2" - jest-environment-node "^29.2.2" + jest-circus "^29.3.1" + jest-environment-node "^29.3.1" jest-get-type "^29.2.0" jest-regex-util "^29.2.0" - jest-resolve "^29.2.2" - jest-runner "^29.2.2" - jest-util "^29.2.1" - jest-validate "^29.2.2" + jest-resolve "^29.3.1" + jest-runner "^29.3.1" + jest-util "^29.3.1" + jest-validate "^29.3.1" micromatch "^4.0.4" parse-json "^5.2.0" - pretty-format "^29.2.1" + pretty-format "^29.3.1" slash "^3.0.0" strip-json-comments "^3.1.1" @@ -3439,15 +3454,15 @@ jest-diff@^29.0.3: jest-get-type "^29.0.0" pretty-format "^29.0.3" -jest-diff@^29.2.1: - version "29.2.1" - resolved "https://registry.npmmirror.com/jest-diff/-/jest-diff-29.2.1.tgz#027e42f5a18b693fb2e88f81b0ccab533c08faee" - integrity sha512-gfh/SMNlQmP3MOUgdzxPOd4XETDJifADpT937fN1iUGz+9DgOu2eUPHH25JDkLVcLwwqxv3GzVyK4VBUr9fjfA== +jest-diff@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.3.1.tgz#d8215b72fed8f1e647aed2cae6c752a89e757527" + integrity sha512-vU8vyiO7568tmin2lA3r2DP8oRvzhvRcD4DjpXc6uGveQodyk7CKLhQlCSiwgx3g0pFaE88/KLZ0yaTWMc4Uiw== dependencies: chalk "^4.0.0" - diff-sequences "^29.2.0" + diff-sequences "^29.3.1" jest-get-type "^29.2.0" - pretty-format "^29.2.1" + pretty-format "^29.3.1" jest-docblock@^29.2.0: version "29.2.0" @@ -3456,28 +3471,28 @@ jest-docblock@^29.2.0: dependencies: detect-newline "^3.0.0" -jest-each@^29.2.1: - version "29.2.1" - resolved "https://registry.npmmirror.com/jest-each/-/jest-each-29.2.1.tgz#6b0a88ee85c2ba27b571a6010c2e0c674f5c9b29" - integrity sha512-sGP86H/CpWHMyK3qGIGFCgP6mt+o5tu9qG4+tobl0LNdgny0aitLXs9/EBacLy3Bwqy+v4uXClqJgASJWcruYw== +jest-each@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-each/-/jest-each-29.3.1.tgz#bc375c8734f1bb96625d83d1ca03ef508379e132" + integrity sha512-qrZH7PmFB9rEzCSl00BWjZYuS1BSOH8lLuC0azQE9lQrAx3PWGKHTDudQiOSwIy5dGAJh7KA0ScYlCP7JxvFYA== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.3.1" chalk "^4.0.0" jest-get-type "^29.2.0" - jest-util "^29.2.1" - pretty-format "^29.2.1" + jest-util "^29.3.1" + pretty-format "^29.3.1" -jest-environment-node@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-environment-node/-/jest-environment-node-29.2.2.tgz#a64b272773870c3a947cd338c25fd34938390bc2" - integrity sha512-B7qDxQjkIakQf+YyrqV5dICNs7tlCO55WJ4OMSXsqz1lpI/0PmeuXdx2F7eU8rnPbRkUR/fItSSUh0jvE2y/tw== +jest-environment-node@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.3.1.tgz#5023b32472b3fba91db5c799a0d5624ad4803e74" + integrity sha512-xm2THL18Xf5sIHoU7OThBPtuH6Lerd+Y1NLYiZJlkE3hbE+7N7r8uvHIl/FkZ5ymKXJe/11SQuf3fv4v6rUMag== dependencies: - "@jest/environment" "^29.2.2" - "@jest/fake-timers" "^29.2.2" - "@jest/types" "^29.2.1" + "@jest/environment" "^29.3.1" + "@jest/fake-timers" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" - jest-mock "^29.2.2" - jest-util "^29.2.1" + jest-mock "^29.3.1" + jest-util "^29.3.1" jest-get-type@^29.0.0: version "29.0.0" @@ -3489,32 +3504,32 @@ jest-get-type@^29.2.0: resolved "https://registry.npmmirror.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408" integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA== -jest-haste-map@^29.2.1: - version "29.2.1" - resolved "https://registry.npmmirror.com/jest-haste-map/-/jest-haste-map-29.2.1.tgz#f803fec57f8075e6c55fb5cd551f99a72471c699" - integrity sha512-wF460rAFmYc6ARcCFNw4MbGYQjYkvjovb9GBT+W10Um8q5nHq98jD6fHZMDMO3tA56S8XnmNkM8GcA8diSZfnA== +jest-haste-map@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.3.1.tgz#af83b4347f1dae5ee8c2fb57368dc0bb3e5af843" + integrity sha512-/FFtvoG1xjbbPXQLFef+WSU4yrc0fc0Dds6aRPBojUid7qlPqZvxdUBA03HW0fnVHXVCnCdkuoghYItKNzc/0A== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.3.1" "@types/graceful-fs" "^4.1.3" "@types/node" "*" anymatch "^3.0.3" fb-watchman "^2.0.0" graceful-fs "^4.2.9" jest-regex-util "^29.2.0" - jest-util "^29.2.1" - jest-worker "^29.2.1" + jest-util "^29.3.1" + jest-worker "^29.3.1" micromatch "^4.0.4" walker "^1.0.8" optionalDependencies: fsevents "^2.3.2" -jest-leak-detector@^29.2.1: - version "29.2.1" - resolved "https://registry.npmmirror.com/jest-leak-detector/-/jest-leak-detector-29.2.1.tgz#ec551686b7d512ec875616c2c3534298b1ffe2fc" - integrity sha512-1YvSqYoiurxKOJtySc+CGVmw/e1v4yNY27BjWTVzp0aTduQeA7pdieLiW05wTYG/twlKOp2xS/pWuikQEmklug== +jest-leak-detector@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.3.1.tgz#95336d020170671db0ee166b75cd8ef647265518" + integrity sha512-3DA/VVXj4zFOPagGkuqHnSQf1GZBmmlagpguxEERO6Pla2g84Q1MaVIB3YMxgUaFIaYag8ZnTyQgiZ35YEqAQA== dependencies: jest-get-type "^29.2.0" - pretty-format "^29.2.1" + pretty-format "^29.3.1" jest-matcher-utils@^29.0.3: version "29.0.3" @@ -3526,15 +3541,15 @@ jest-matcher-utils@^29.0.3: jest-get-type "^29.0.0" pretty-format "^29.0.3" -jest-matcher-utils@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-matcher-utils/-/jest-matcher-utils-29.2.2.tgz#9202f8e8d3a54733266784ce7763e9a08688269c" - integrity sha512-4DkJ1sDPT+UX2MR7Y3od6KtvRi9Im1ZGLGgdLFLm4lPexbTaCgJW5NN3IOXlQHF7NSHY/VHhflQ+WoKtD/vyCw== +jest-matcher-utils@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.3.1.tgz#6e7f53512f80e817dfa148672bd2d5d04914a572" + integrity sha512-fkRMZUAScup3txIKfMe3AIZZmPEjWEdsPJFK3AIy5qRohWqQFg1qrmKfYXR9qEkNc7OdAu2N4KPHibEmy4HPeQ== dependencies: chalk "^4.0.0" - jest-diff "^29.2.1" + jest-diff "^29.3.1" jest-get-type "^29.2.0" - pretty-format "^29.2.1" + pretty-format "^29.3.1" jest-message-util@^29.0.3: version "29.0.3" @@ -3551,29 +3566,29 @@ jest-message-util@^29.0.3: slash "^3.0.0" stack-utils "^2.0.3" -jest-message-util@^29.2.1: - version "29.2.1" - resolved "https://registry.npmmirror.com/jest-message-util/-/jest-message-util-29.2.1.tgz#3a51357fbbe0cc34236f17a90d772746cf8d9193" - integrity sha512-Dx5nEjw9V8C1/Yj10S/8ivA8F439VS8vTq1L7hEgwHFn9ovSKNpYW/kwNh7UglaEgXO42XxzKJB+2x0nSglFVw== +jest-message-util@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.3.1.tgz#37bc5c468dfe5120712053dd03faf0f053bd6adb" + integrity sha512-lMJTbgNcDm5z+6KDxWtqOFWlGQxD6XaYwBqHR8kmpkP+WWWG90I35kdtQHY67Ay5CSuydkTBbJG+tH9JShFCyA== dependencies: "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.2.1" + "@jest/types" "^29.3.1" "@types/stack-utils" "^2.0.0" chalk "^4.0.0" graceful-fs "^4.2.9" micromatch "^4.0.4" - pretty-format "^29.2.1" + pretty-format "^29.3.1" slash "^3.0.0" stack-utils "^2.0.3" -jest-mock@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-mock/-/jest-mock-29.2.2.tgz#9045618b3f9d27074bbcf2d55bdca6a5e2e8bca7" - integrity sha512-1leySQxNAnivvbcx0sCB37itu8f4OX2S/+gxLAV4Z62shT4r4dTG9tACDywUAEZoLSr36aYUTsVp3WKwWt4PMQ== +jest-mock@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-mock/-/jest-mock-29.3.1.tgz#60287d92e5010979d01f218c6b215b688e0f313e" + integrity sha512-H8/qFDtDVMFvFP4X8NuOT3XRDzOUTz+FeACjufHzsOIBAxivLqkB1PoLCaJx9iPPQ8dZThHPp/G3WRWyMgA3JA== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.3.1" "@types/node" "*" - jest-util "^29.2.1" + jest-util "^29.3.1" jest-pnp-resolver@^1.2.2: version "1.2.2" @@ -3585,88 +3600,88 @@ jest-regex-util@^29.2.0: resolved "https://registry.npmmirror.com/jest-regex-util/-/jest-regex-util-29.2.0.tgz#82ef3b587e8c303357728d0322d48bbfd2971f7b" integrity sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA== -jest-resolve-dependencies@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.2.2.tgz#1f444766f37a25f1490b5137408b6ff746a05d64" - integrity sha512-wWOmgbkbIC2NmFsq8Lb+3EkHuW5oZfctffTGvwsA4JcJ1IRk8b2tg+hz44f0lngvRTeHvp3Kyix9ACgudHH9aQ== +jest-resolve-dependencies@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.1.tgz#a6a329708a128e68d67c49f38678a4a4a914c3bf" + integrity sha512-Vk0cYq0byRw2WluNmNWGqPeRnZ3p3hHmjJMp2dyyZeYIfiBskwq4rpiuGFR6QGAdbj58WC7HN4hQHjf2mpvrLA== dependencies: jest-regex-util "^29.2.0" - jest-snapshot "^29.2.2" + jest-snapshot "^29.3.1" -jest-resolve@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-resolve/-/jest-resolve-29.2.2.tgz#ad6436053b0638b41e12bbddde2b66e1397b35b5" - integrity sha512-3gaLpiC3kr14rJR3w7vWh0CBX2QAhfpfiQTwrFPvVrcHe5VUBtIXaR004aWE/X9B2CFrITOQAp5gxLONGrk6GA== +jest-resolve@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.3.1.tgz#9a4b6b65387a3141e4a40815535c7f196f1a68a7" + integrity sha512-amXJgH/Ng712w3Uz5gqzFBBjxV8WFLSmNjoreBGMqxgCz5cH7swmBZzgBaCIOsvb0NbpJ0vgaSFdJqMdT+rADw== dependencies: chalk "^4.0.0" graceful-fs "^4.2.9" - jest-haste-map "^29.2.1" + jest-haste-map "^29.3.1" jest-pnp-resolver "^1.2.2" - jest-util "^29.2.1" - jest-validate "^29.2.2" + jest-util "^29.3.1" + jest-validate "^29.3.1" resolve "^1.20.0" resolve.exports "^1.1.0" slash "^3.0.0" -jest-runner@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-runner/-/jest-runner-29.2.2.tgz#6b5302ed15eba8bf05e6b14d40f1e8d469564da3" - integrity sha512-1CpUxXDrbsfy9Hr9/1zCUUhT813kGGK//58HeIw/t8fa/DmkecEwZSWlb1N/xDKXg3uCFHQp1GCvlSClfImMxg== - dependencies: - "@jest/console" "^29.2.1" - "@jest/environment" "^29.2.2" - "@jest/test-result" "^29.2.1" - "@jest/transform" "^29.2.2" - "@jest/types" "^29.2.1" +jest-runner@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-runner/-/jest-runner-29.3.1.tgz#a92a879a47dd096fea46bb1517b0a99418ee9e2d" + integrity sha512-oFvcwRNrKMtE6u9+AQPMATxFcTySyKfLhvso7Sdk/rNpbhg4g2GAGCopiInk1OP4q6gz3n6MajW4+fnHWlU3bA== + dependencies: + "@jest/console" "^29.3.1" + "@jest/environment" "^29.3.1" + "@jest/test-result" "^29.3.1" + "@jest/transform" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" chalk "^4.0.0" emittery "^0.13.1" graceful-fs "^4.2.9" jest-docblock "^29.2.0" - jest-environment-node "^29.2.2" - jest-haste-map "^29.2.1" - jest-leak-detector "^29.2.1" - jest-message-util "^29.2.1" - jest-resolve "^29.2.2" - jest-runtime "^29.2.2" - jest-util "^29.2.1" - jest-watcher "^29.2.2" - jest-worker "^29.2.1" + jest-environment-node "^29.3.1" + jest-haste-map "^29.3.1" + jest-leak-detector "^29.3.1" + jest-message-util "^29.3.1" + jest-resolve "^29.3.1" + jest-runtime "^29.3.1" + jest-util "^29.3.1" + jest-watcher "^29.3.1" + jest-worker "^29.3.1" p-limit "^3.1.0" source-map-support "0.5.13" -jest-runtime@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-runtime/-/jest-runtime-29.2.2.tgz#4068ee82423769a481460efd21d45a8efaa5c179" - integrity sha512-TpR1V6zRdLynckKDIQaY41od4o0xWL+KOPUCZvJK2bu5P1UXhjobt5nJ2ICNeIxgyj9NGkO0aWgDqYPVhDNKjA== +jest-runtime@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.3.1.tgz#21efccb1a66911d6d8591276a6182f520b86737a" + integrity sha512-jLzkIxIqXwBEOZx7wx9OO9sxoZmgT2NhmQKzHQm1xwR1kNW/dn0OjxR424VwHHf1SPN6Qwlb5pp1oGCeFTQ62A== dependencies: - "@jest/environment" "^29.2.2" - "@jest/fake-timers" "^29.2.2" - "@jest/globals" "^29.2.2" + "@jest/environment" "^29.3.1" + "@jest/fake-timers" "^29.3.1" + "@jest/globals" "^29.3.1" "@jest/source-map" "^29.2.0" - "@jest/test-result" "^29.2.1" - "@jest/transform" "^29.2.2" - "@jest/types" "^29.2.1" + "@jest/test-result" "^29.3.1" + "@jest/transform" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" chalk "^4.0.0" cjs-module-lexer "^1.0.0" collect-v8-coverage "^1.0.0" glob "^7.1.3" graceful-fs "^4.2.9" - jest-haste-map "^29.2.1" - jest-message-util "^29.2.1" - jest-mock "^29.2.2" + jest-haste-map "^29.3.1" + jest-message-util "^29.3.1" + jest-mock "^29.3.1" jest-regex-util "^29.2.0" - jest-resolve "^29.2.2" - jest-snapshot "^29.2.2" - jest-util "^29.2.1" + jest-resolve "^29.3.1" + jest-snapshot "^29.3.1" + jest-util "^29.3.1" slash "^3.0.0" strip-bom "^4.0.0" -jest-snapshot@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-snapshot/-/jest-snapshot-29.2.2.tgz#1016ce60297b77382386bad561107174604690c2" - integrity sha512-GfKJrpZ5SMqhli3NJ+mOspDqtZfJBryGA8RIBxF+G+WbDoC7HCqKaeAss4Z/Sab6bAW11ffasx8/vGsj83jyjA== +jest-snapshot@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.3.1.tgz#17bcef71a453adc059a18a32ccbd594b8cc4e45e" + integrity sha512-+3JOc+s28upYLI2OJM4PWRGK9AgpsMs/ekNryUV0yMBClT9B1DF2u2qay8YxcQd338PPYSFNb0lsar1B49sLDA== dependencies: "@babel/core" "^7.11.6" "@babel/generator" "^7.7.2" @@ -3674,23 +3689,23 @@ jest-snapshot@^29.2.2: "@babel/plugin-syntax-typescript" "^7.7.2" "@babel/traverse" "^7.7.2" "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.2.2" - "@jest/transform" "^29.2.2" - "@jest/types" "^29.2.1" + "@jest/expect-utils" "^29.3.1" + "@jest/transform" "^29.3.1" + "@jest/types" "^29.3.1" "@types/babel__traverse" "^7.0.6" "@types/prettier" "^2.1.5" babel-preset-current-node-syntax "^1.0.0" chalk "^4.0.0" - expect "^29.2.2" + expect "^29.3.1" graceful-fs "^4.2.9" - jest-diff "^29.2.1" + jest-diff "^29.3.1" jest-get-type "^29.2.0" - jest-haste-map "^29.2.1" - jest-matcher-utils "^29.2.2" - jest-message-util "^29.2.1" - jest-util "^29.2.1" + jest-haste-map "^29.3.1" + jest-matcher-utils "^29.3.1" + jest-message-util "^29.3.1" + jest-util "^29.3.1" natural-compare "^1.4.0" - pretty-format "^29.2.1" + pretty-format "^29.3.1" semver "^7.3.5" jest-util@^29.0.0, jest-util@^29.0.3: @@ -3705,42 +3720,42 @@ jest-util@^29.0.0, jest-util@^29.0.3: graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-util@^29.2.1: - version "29.2.1" - resolved "https://registry.npmmirror.com/jest-util/-/jest-util-29.2.1.tgz#f26872ba0dc8cbefaba32c34f98935f6cf5fc747" - integrity sha512-P5VWDj25r7kj7kl4pN2rG/RN2c1TLfYYYZYULnS/35nFDjBai+hBeo3MDrYZS7p6IoY3YHZnt2vq4L6mKnLk0g== +jest-util@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz#1dda51e378bbcb7e3bc9d8ab651445591ed373e1" + integrity sha512-7YOVZaiX7RJLv76ZfHt4nbNEzzTRiMW/IiOG7ZOKmTXmoGBxUDefgMAxQubu6WPVqP5zSzAdZG0FfLcC7HOIFQ== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.3.1" "@types/node" "*" chalk "^4.0.0" ci-info "^3.2.0" graceful-fs "^4.2.9" picomatch "^2.2.3" -jest-validate@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-validate/-/jest-validate-29.2.2.tgz#e43ce1931292dfc052562a11bc681af3805eadce" - integrity sha512-eJXATaKaSnOuxNfs8CLHgdABFgUrd0TtWS8QckiJ4L/QVDF4KVbZFBBOwCBZHOS0Rc5fOxqngXeGXE3nGQkpQA== +jest-validate@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-validate/-/jest-validate-29.3.1.tgz#d56fefaa2e7d1fde3ecdc973c7f7f8f25eea704a" + integrity sha512-N9Lr3oYR2Mpzuelp1F8negJR3YE+L1ebk1rYA5qYo9TTY3f9OWdptLoNSPP9itOCBIRBqjt/S5XHlzYglLN67g== dependencies: - "@jest/types" "^29.2.1" + "@jest/types" "^29.3.1" camelcase "^6.2.0" chalk "^4.0.0" jest-get-type "^29.2.0" leven "^3.1.0" - pretty-format "^29.2.1" + pretty-format "^29.3.1" -jest-watcher@^29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest-watcher/-/jest-watcher-29.2.2.tgz#7093d4ea8177e0a0da87681a9e7b09a258b9daf7" - integrity sha512-j2otfqh7mOvMgN2WlJ0n7gIx9XCMWntheYGlBK7+5g3b1Su13/UAK7pdKGyd4kDlrLwtH2QPvRv5oNIxWvsJ1w== +jest-watcher@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.3.1.tgz#3341547e14fe3c0f79f9c3a4c62dbc3fc977fd4a" + integrity sha512-RspXG2BQFDsZSRKGCT/NiNa8RkQ1iKAjrO0//soTMWx/QUt+OcxMqMSBxz23PYGqUuWm2+m2mNNsmj0eIoOaFg== dependencies: - "@jest/test-result" "^29.2.1" - "@jest/types" "^29.2.1" + "@jest/test-result" "^29.3.1" + "@jest/types" "^29.3.1" "@types/node" "*" ansi-escapes "^4.2.1" chalk "^4.0.0" emittery "^0.13.1" - jest-util "^29.2.1" + jest-util "^29.3.1" string-length "^4.0.1" jest-worker@^27.4.5: @@ -3752,25 +3767,25 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" -jest-worker@^29.2.1: - version "29.2.1" - resolved "https://registry.npmmirror.com/jest-worker/-/jest-worker-29.2.1.tgz#8ba68255438252e1674f990f0180c54dfa26a3b1" - integrity sha512-ROHTZ+oj7sBrgtv46zZ84uWky71AoYi0vEV9CdEtc1FQunsoAGe5HbQmW76nI5QWdvECVPrSi1MCVUmizSavMg== +jest-worker@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-29.3.1.tgz#e9462161017a9bb176380d721cab022661da3d6b" + integrity sha512-lY4AnnmsEWeiXirAIA0c9SDPbuCBq8IYuDVL8PMm0MZ2PEs2yPvRA/J64QBXuZp7CYKrDM/rmNrc9/i3KJQncw== dependencies: "@types/node" "*" - jest-util "^29.2.1" + jest-util "^29.3.1" merge-stream "^2.0.0" supports-color "^8.0.0" -jest@29.2.2: - version "29.2.2" - resolved "https://registry.npmmirror.com/jest/-/jest-29.2.2.tgz#24da83cbbce514718acd698926b7679109630476" - integrity sha512-r+0zCN9kUqoON6IjDdjbrsWobXM/09Nd45kIPRD8kloaRh1z5ZCMdVsgLXGxmlL7UpAJsvCYOQNO+NjvG/gqiQ== +jest@29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/jest/-/jest-29.3.1.tgz#c130c0d551ae6b5459b8963747fed392ddbde122" + integrity sha512-6iWfL5DTT0Np6UYs/y5Niu7WIfNv/wRTtN5RSXt2DIEft3dx3zPuw/3WJQBCJfmEzvDiEKwoqMbGD9n49+qLSA== dependencies: - "@jest/core" "^29.2.2" - "@jest/types" "^29.2.1" + "@jest/core" "^29.3.1" + "@jest/types" "^29.3.1" import-local "^3.0.2" - jest-cli "^29.2.2" + jest-cli "^29.3.1" js-sdsl@^4.1.4: version "4.1.4" @@ -4564,10 +4579,10 @@ pretty-format@^29.0.0, pretty-format@^29.0.3: ansi-styles "^5.0.0" react-is "^18.0.0" -pretty-format@^29.2.1: - version "29.2.1" - resolved "https://registry.npmmirror.com/pretty-format/-/pretty-format-29.2.1.tgz#86e7748fe8bbc96a6a4e04fa99172630907a9611" - integrity sha512-Y41Sa4aLCtKAXvwuIpTvcFBkyeYp2gdFWzXGA+ZNES3VwURIB165XO/z7CjETwzCCS53MjW/rLMyyqEnTtaOfA== +pretty-format@^29.3.1: + version "29.3.1" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz#1841cac822b02b4da8971dacb03e8a871b4722da" + integrity sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg== dependencies: "@jest/schemas" "^29.0.0" ansi-styles "^5.0.0" @@ -5334,16 +5349,21 @@ tsconfig-paths@^3.14.1: minimist "^1.2.6" strip-bom "^3.0.0" -tslib@2.4.0, tslib@^2.1.0, tslib@^2.3.1: - version "2.4.0" - resolved "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== +tslib@2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.1.0, tslib@^2.3.1: + version "2.4.0" + resolved "https://registry.npmmirror.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" From 90873991c8d0d2d00b9b2adc7b7a775db8d5c5b8 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Fri, 23 Dec 2022 16:35:15 +0800 Subject: [PATCH 05/27] chore: submit code to review --- src/common/decorators/permission.decorator.ts | 4 +- src/entities/permission.entity.ts | 4 +- src/entities/role-permission.entity.ts | 2 +- src/enums/permission.enum.ts | 2 +- src/enums/role.enum.ts | 13 +- src/guards/roles.guard.ts | 4 +- .../1671725826115-update-table_1_11_0.ts | 22 --- .../1671766182451-update-table_1_11_0.ts | 139 ++++++++++++++++++ .../workspace/project/dto/update.dto.ts | 4 + .../workspace/project/project.controller.ts | 58 +++++++- .../workspace/project/project.service.ts | 11 +- src/modules/workspace/workspace.controller.ts | 40 +++++ src/modules/workspace/workspace.dto.ts | 25 +++- src/modules/workspace/workspace.module.ts | 8 + src/modules/workspace/workspace.service.ts | 100 +++++++++++-- 15 files changed, 383 insertions(+), 53 deletions(-) delete mode 100644 src/migrations/1671725826115-update-table_1_11_0.ts create mode 100644 src/migrations/1671766182451-update-table_1_11_0.ts diff --git a/src/common/decorators/permission.decorator.ts b/src/common/decorators/permission.decorator.ts index ea26196..7855c34 100644 --- a/src/common/decorators/permission.decorator.ts +++ b/src/common/decorators/permission.decorator.ts @@ -1,6 +1,6 @@ import { SetMetadata } from '@nestjs/common'; -import { Permission } from '@/enums/permission.enum'; +import { PermissionEnum } from '@/enums/permission.enum'; export const PERMISSIONS_KEY = 'permissions'; -export const Permissions = (permissions: Permission) => +export const Permissions = (permissions: PermissionEnum) => SetMetadata(PERMISSIONS_KEY, permissions); diff --git a/src/entities/permission.entity.ts b/src/entities/permission.entity.ts index 208efe0..2b338f5 100644 --- a/src/entities/permission.entity.ts +++ b/src/entities/permission.entity.ts @@ -3,9 +3,9 @@ import { Column, Entity } from 'typeorm'; import { BaseEntity } from './base.new.entity'; @Entity({ name: 'permission' }) -export class Permission extends BaseEntity { +export class PermissionEntity extends BaseEntity { @ApiProperty({ type: String, description: '权限名称' }) - @Column({ type: 'varchar', length: 120, comment: '权限名称' }) + @Column({ type: 'varchar', unique: true, length: 120, comment: '权限名称' }) name: string; @ApiProperty({ type: Number, description: '权限状态' }) diff --git a/src/entities/role-permission.entity.ts b/src/entities/role-permission.entity.ts index 4c4b3c5..185fe59 100644 --- a/src/entities/role-permission.entity.ts +++ b/src/entities/role-permission.entity.ts @@ -2,7 +2,7 @@ import { Column, Entity } from 'typeorm'; import { BaseEntity } from './base.new.entity'; @Entity({ name: 'role_permission' }) -export class RolePermission extends BaseEntity { +export class RolePermissionEntity extends BaseEntity { @Column({ type: 'int', name: 'role_id', unsigned: true, comment: '角色ID' }) roleID: number; diff --git a/src/enums/permission.enum.ts b/src/enums/permission.enum.ts index 4fed7c4..ee4718d 100644 --- a/src/enums/permission.enum.ts +++ b/src/enums/permission.enum.ts @@ -1,4 +1,4 @@ -export enum Permission { +export enum PermissionEnum { /** workspace */ UPDATE_WORKSPACE = 'update:workspace', DELETE_WORKSPACE = 'delete:workspace', diff --git a/src/enums/role.enum.ts b/src/enums/role.enum.ts index bfcb049..5621da3 100644 --- a/src/enums/role.enum.ts +++ b/src/enums/role.enum.ts @@ -1,9 +1,6 @@ -export enum WorkspaceRole { - Owner = 'owner', - Editor = 'editor', -} - -export enum ProjectRole { - Owner = 'owner', - Editor = 'editor', +export enum RoleEnum { + WorkspaceOwnerRoleID = 1, + WorkspaceEditorRoleID = 2, + ProjectOwnerRoleID = 3, + ProjectEditorRoleID = 4, } diff --git a/src/guards/roles.guard.ts b/src/guards/roles.guard.ts index 0fff32e..6d55d8a 100644 --- a/src/guards/roles.guard.ts +++ b/src/guards/roles.guard.ts @@ -1,7 +1,7 @@ import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { PERMISSIONS_KEY } from '@/common/decorators/permission.decorator'; -import { Permission } from '@/enums/permission.enum'; +import { PermissionEnum } from '@/enums/permission.enum'; @Injectable() export class RolesGuard implements CanActivate { @@ -9,7 +9,7 @@ export class RolesGuard implements CanActivate { canActivate(context: ExecutionContext): boolean { // 当前请求所需权限 - const currentPerm = this.reflector.getAllAndOverride( + const currentPerm = this.reflector.getAllAndOverride( PERMISSIONS_KEY, [context.getHandler(), context.getClass()], ); diff --git a/src/migrations/1671725826115-update-table_1_11_0.ts b/src/migrations/1671725826115-update-table_1_11_0.ts deleted file mode 100644 index 39140da..0000000 --- a/src/migrations/1671725826115-update-table_1_11_0.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { MigrationInterface, QueryRunner } from "typeorm"; - -export class updateTable11101671725826115 implements MigrationInterface { - name = 'updateTable11101671725826115' - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.query(`CREATE TABLE \`permission\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`name\` varchar(120) NOT NULL COMMENT '权限名称', \`status\` tinyint(1) NOT NULL COMMENT '状态:0=禁用 1=启用' DEFAULT '1', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); - await queryRunner.query(`CREATE TABLE \`project_user_role\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`project_id\` int NOT NULL COMMENT '项目ID', \`user_id\` int NOT NULL COMMENT '用户ID', \`role_id\` int NOT NULL COMMENT '角色ID', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); - await queryRunner.query(`CREATE TABLE \`role_permission\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`role_id\` int UNSIGNED NOT NULL COMMENT '角色ID', \`permission_id\` int NOT NULL COMMENT '权限ID', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); - await queryRunner.query(`CREATE TABLE \`role\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`name\` varchar(50) NOT NULL COMMENT '角色名称', \`module\` tinyint(1) UNSIGNED NOT NULL COMMENT '角色类别: 1=空间 2=项目', \`remark\` varchar(200) NOT NULL COMMENT '角色备注' DEFAULT '', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); - await queryRunner.query(`CREATE TABLE \`workspace_user_role\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`workspace_id\` int NOT NULL COMMENT '空间ID', \`user_id\` int NOT NULL COMMENT '用户ID', \`role_id\` int NOT NULL COMMENT '角色ID', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`); - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.query(`DROP TABLE \`workspace_user_role\``); - await queryRunner.query(`DROP TABLE \`role\``); - await queryRunner.query(`DROP TABLE \`role_permission\``); - await queryRunner.query(`DROP TABLE \`project_user_role\``); - await queryRunner.query(`DROP TABLE \`permission\``); - } - -} diff --git a/src/migrations/1671766182451-update-table_1_11_0.ts b/src/migrations/1671766182451-update-table_1_11_0.ts new file mode 100644 index 0000000..79444b1 --- /dev/null +++ b/src/migrations/1671766182451-update-table_1_11_0.ts @@ -0,0 +1,139 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; +import { PermissionEntity } from '@/entities/permission.entity'; +import { RoleEntity } from '@/entities/role.entity'; +import { PermissionEnum } from '@/enums/permission.enum'; +import { RolePermissionEntity } from '@/entities/role-permission.entity'; + +const perms = Object.values(PermissionEnum); + +const workspaceOwnerPerms = [ + 'update:workspace', + 'delete:workspace', + 'view:workspace', + 'add:workspace:member', + 'update:workspace:member', + 'delete:workspace:member', + 'view:project:list', + 'view:project', + 'update:project', + 'delete:project', +]; + +export class updateTable11101671725826115 implements MigrationInterface { + name = 'updateTable11101671725826115'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE \`permission\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`name\` varchar(120) NOT NULL COMMENT '权限名称', \`status\` tinyint(1) NOT NULL COMMENT '状态:0=禁用 1=启用' DEFAULT '1', UNIQUE INDEX \`IDX_240853a0c3353c25fb12434ad3\` (\`name\`), PRIMARY KEY (\`id\`)) ENGINE=InnoDB`, + ); + await queryRunner.query( + `CREATE TABLE \`role_permission\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`role_id\` int UNSIGNED NOT NULL COMMENT '角色ID', \`permission_id\` int NOT NULL COMMENT '权限ID', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`, + ); + await queryRunner.query( + `CREATE TABLE \`project_user_role\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`project_id\` int NOT NULL COMMENT '项目ID', \`user_id\` int NOT NULL COMMENT '用户ID', \`role_id\` int NOT NULL COMMENT '角色ID', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`, + ); + await queryRunner.query( + `CREATE TABLE \`workspace_user_role\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`workspace_id\` int NOT NULL COMMENT '空间ID', \`user_id\` int NOT NULL COMMENT '用户ID', \`role_id\` int NOT NULL COMMENT '角色ID', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`, + ); + await queryRunner.query( + `CREATE TABLE \`role\` (\`created_at\` datetime(6) NOT NULL COMMENT '创建时间' DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL COMMENT '更新时间' DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), \`deleted_at\` datetime(6) NULL COMMENT '删除时间', \`id\` int NOT NULL AUTO_INCREMENT COMMENT '主键ID', \`uuid\` varchar(36) NOT NULL COMMENT '业务 UUID', \`create_by\` bigint NULL COMMENT '创建者', \`update_by\` bigint NULL COMMENT '更新者', \`name\` varchar(50) NOT NULL COMMENT '角色名称', \`module\` tinyint(1) UNSIGNED NOT NULL COMMENT '角色类别: 1=空间 2=项目', \`remark\` varchar(200) NOT NULL COMMENT '角色备注' DEFAULT '', PRIMARY KEY (\`id\`)) ENGINE=InnoDB`, + ); + + // 初始化权限 + await queryRunner.manager.insert( + 'permission', + perms.map((name) => ({ name })), + ); + + // 初始化默认角色 + const roleInsertResult = await queryRunner.manager.insert( + 'role', + [ + { + name: 'Owner', + module: 1, + remark: 'workspace owner', + }, + { + name: 'Editor', + module: 1, + remark: 'workspace editor', + }, + { + name: 'Owner', + module: 2, + remark: 'project owner', + }, + { + name: 'Editor', + module: 2, + remark: 'project editor', + }, + ], + ); + + const [wOwner, wEditor, pOwner, pEditor] = roleInsertResult.identifiers; + + for (const wPerm of workspaceOwnerPerms) { + const perm = await queryRunner.manager.findOneBy( + 'permission', + { name: wPerm }, + ); + await queryRunner.manager.insert( + 'role_permission', + { + roleID: wOwner.id, + permissionID: perm.id, + }, + ); + // workspace editor 没有删除或移除操作 + if (!['delete', 'remove'].some((n) => perm.name.includes(n))) { + await queryRunner.manager.insert( + 'role_permission', + { + roleID: wEditor.id, + permissionID: perm.id, + }, + ); + } + } + + for (const name of perms) { + if (name.includes('workspace')) { + continue; + } + const perm = await queryRunner.manager.findOneBy( + 'permission', + { name }, + ); + await queryRunner.manager.insert( + 'role_permission', + { + roleID: pOwner.id, + permissionID: perm.id, + }, + ); + // project editor 没有删除或更新项目操作 + if (!['update:project', 'delete:project'].some((n) => n === name)) { + await queryRunner.manager.insert( + 'role_permission', + { + roleID: pEditor.id, + permissionID: perm.id, + }, + ); + } + } + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE \`role\``); + await queryRunner.query(`DROP TABLE \`workspace_user_role\``); + await queryRunner.query(`DROP TABLE \`project_user_role\``); + await queryRunner.query(`DROP TABLE \`role_permission\``); + await queryRunner.query( + `DROP INDEX \`IDX_240853a0c3353c25fb12434ad3\` ON \`permission\``, + ); + await queryRunner.query(`DROP TABLE \`permission\``); + } +} diff --git a/src/modules/workspace/project/dto/update.dto.ts b/src/modules/workspace/project/dto/update.dto.ts index 20d53c8..a1471ee 100644 --- a/src/modules/workspace/project/dto/update.dto.ts +++ b/src/modules/workspace/project/dto/update.dto.ts @@ -2,3 +2,7 @@ import { PartialType } from '@nestjs/mapped-types'; import { CreateDto } from './create.dto'; export class UpdateDto extends PartialType(CreateDto) {} + +export class SetRoleDto { + roleID: number; +} diff --git a/src/modules/workspace/project/project.controller.ts b/src/modules/workspace/project/project.controller.ts index 2b79385..05561d9 100644 --- a/src/modules/workspace/project/project.controller.ts +++ b/src/modules/workspace/project/project.controller.ts @@ -10,11 +10,12 @@ import { ParseIntPipe, NotFoundException, UseGuards, + UnauthorizedException, } 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'; +import { SetRoleDto, UpdateDto } from './dto/update.dto'; import { CollectionsDto, QueryDto } from './dto/query.dto'; import { ImportDto } from './dto/import.dto'; import { WORKSPACE_ID_PREFIX } from '@/common/contants/prefix.contants'; @@ -28,6 +29,12 @@ import { ExportCollectionsResultDto, } from '@/modules/workspace/project/dto/export.dto'; import { RolesGuard } from '@/guards'; +import { IUser, User } from '@/common/decorators/user.decorator'; +import { + WorkspaceMemberAddDto, + WorkspaceMemberRemoveDto, +} from '@/modules/workspace/workspace.dto'; +import { RoleEntity } from '@/entities/role.entity'; @ApiTags('Project') @Controller(`${WORKSPACE_ID_PREFIX}/project`) @@ -128,4 +135,51 @@ export class ProjectController { ) { return this.service.getProjectCollections(projectID); } + + @ApiCreatedResponseData(Project) + @Post(':projectID/member/add') + @ApiOperation({ summary: '添加项目成员' }) + async memberAdd( + @User() user: IUser, + @Param('projectID') projectID, + @Body() addMemberDto: WorkspaceMemberAddDto, + ) { + return this.service.memberAdd(projectID, addMemberDto); + } + + @ApiOkResponseData(Project) + @Delete(':projectID/member/remove') + @ApiOperation({ summary: '移除项目成员' }) + async memberRemove( + @User() user: IUser, + @Param('projectID') id, + @Body() createCatDto: WorkspaceMemberRemoveDto, + ) { + return '' as any; + } + + @ApiOkResponseData(Project) + @Post(':projectID/member/leave') + @ApiOperation({ summary: '项目成员主动退出' }) + async memberLeave(@User() user: IUser, @Param('projectID') id) { + return '' as any; + } + + @Post(':projectID/member/setRole') + @ApiOperation({ summary: '设置项目成员角色' }) + async setMemberRole(@User() user: IUser, @Body() dto: SetRoleDto) { + return '' as any; + } + + @Get(':workspaceID/roles') + @ApiOperation({ summary: '获取当前项目角色列表' }) + async getRoles(): Promise { + return []; + } + + @Get(':projectID/permissions') + @ApiOperation({ summary: '获取当前项目权限' }) + async getPermission(): Promise { + return []; + } } diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index 47af3df..a5bcd4f 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -11,6 +11,8 @@ import { Child, Environment, ImportDto, ImportResult } from './dto/import.dto'; import { parseAndCheckApiData, parseAndCheckEnv } from './validate'; import { Project } from '@/entities/project.entity'; import { WorkspaceEntity } from '@/entities/workspace.entity'; +import { WorkspaceMemberAddDto } from '@/modules/workspace/workspace.dto'; +import { ProjectUserRoleEntity } from '@/entities/project-user-role.entity'; @Injectable() export class ProjectService { @@ -19,12 +21,19 @@ export class ProjectService { private readonly repository: Repository, @InjectRepository(WorkspaceEntity) private workspaceRepository: Repository, - + @InjectRepository(ProjectUserRoleEntity) + private readonly projectUserRoleRepo: Repository, private readonly apiDataService: ApiDataService, private readonly apiGroupService: ApiGroupService, private readonly environmentService: EnvironmentService, ) {} + async memberAdd(projectID: number, addMemberDto: WorkspaceMemberAddDto) { + return addMemberDto.userIDs.map((userID) => { + return this.projectUserRoleRepo.save({ projectID, userID }); + }); + } + async create(createDto: CreateDto, workspaceID: number) { const workspace = await this.workspaceRepository.findOne({ where: { diff --git a/src/modules/workspace/workspace.controller.ts b/src/modules/workspace/workspace.controller.ts index 1e82a7e..d0ebb62 100644 --- a/src/modules/workspace/workspace.controller.ts +++ b/src/modules/workspace/workspace.controller.ts @@ -22,6 +22,8 @@ import { DeleteResult } from 'typeorm'; import { WorkspaceService } from './workspace.service'; import { CreateWorkspaceDto, + RolePermissionDto, + SetRoleDto, UpdateWorkspaceDto, WorkspaceMemberAddDto, WorkspaceMemberRemoveDto, @@ -39,6 +41,8 @@ import { ApiOkResponseData, } from '@/common/class/res.class'; import { RolesGuard } from '@/guards'; +import { RoleEntity } from '@/entities/role.entity'; +import { PermissionEntity } from '@/entities/permission.entity'; @ApiBearerAuth() @ApiTags('workspace') @@ -198,4 +202,40 @@ export class WorkspaceController { } return this.workspaceService.removeMembers(id, createCatDto.userIDs); } + + @ApiOkResponseData(WorkspaceEntity) + @Post(':workspaceID/member/leave') + @ApiOperation({ summary: '空间成员主动退出' }) + async memberLeave( + @User() user: IUser, + @Param('workspaceID') id, + ): Promise { + const workspace = await this.workspaceService.findOne({ where: { id } }); + if (!workspace) { + throw new UnauthorizedException('空间不存在'); + } + return this.workspaceService.removeMembers(id, [user.userId]); + } + + @Post(':workspaceID/member/setRole') + @ApiOperation({ summary: '设置空间成员角色' }) + async setMemberRole(@Body() dto: SetRoleDto) { + return this.workspaceService.setMemberRole(dto); + } + + @Get(':workspaceID/roles') + @ApiOperation({ summary: '获取当前空间角色列表' }) + async getRoles(): Promise { + return []; + } + + @ApiOkResponseData(RolePermissionDto) + @Get(':workspaceID/rolePermission') + @ApiOperation({ summary: '获取当前用户在空间的角色和权限' }) + async getRolePermission( + @User() user: IUser, + @Param('workspaceID') workspaceID, + ): Promise { + return this.workspaceService.getRolePermission(user.userId, workspaceID); + } } diff --git a/src/modules/workspace/workspace.dto.ts b/src/modules/workspace/workspace.dto.ts index 497e09f..ea56773 100644 --- a/src/modules/workspace/workspace.dto.ts +++ b/src/modules/workspace/workspace.dto.ts @@ -1,6 +1,16 @@ import { ApiProperty } from '@nestjs/swagger'; -import { ArrayNotEmpty, IsArray, IsString, MinLength } from 'class-validator'; +import { + ArrayNotEmpty, + IsArray, + IsInt, + IsString, + Max, + Min, + MinLength, +} from 'class-validator'; +import { RoleEnum } from '@/enums/role.enum'; import { UserEntity } from '@/entities/user.entity'; +import { RoleEntity } from '@/entities/role.entity'; export class CreateWorkspaceDto { @ApiProperty({ description: '空间名称' }) @@ -32,3 +42,16 @@ export class WorkspaceUser extends UserEntity { @IsString() readonly roleName: string = 'member'; } +export class SetRoleDto { + @IsInt() + @Min(RoleEnum.WorkspaceOwnerRoleID) + @Max(RoleEnum.WorkspaceEditorRoleID) + roleID: number; + + memberID: number; +} + +export class RolePermissionDto { + permissions: string[]; + role: RoleEntity; +} diff --git a/src/modules/workspace/workspace.module.ts b/src/modules/workspace/workspace.module.ts index ca85ad8..11a0fb4 100644 --- a/src/modules/workspace/workspace.module.ts +++ b/src/modules/workspace/workspace.module.ts @@ -32,6 +32,10 @@ import { SharedEntity } from '@/entities/shared.entity'; import { AuthEntity } from '@/entities/auth.entity'; import { AuthService } from '@/modules/auth/auth.service'; import { JwtStrategy } from '@/guards/jwt.strategy'; +import { RolePermissionEntity } from '@/entities/role-permission.entity'; +import { PermissionEntity } from '@/entities/permission.entity'; +import { RoleEntity } from '@/entities/role.entity'; +import { ProjectUserRoleEntity } from '@/entities/project-user-role.entity'; const commonProviders = [ WorkspaceService, @@ -51,6 +55,10 @@ const commonProviders = [ TypeOrmModule.forFeature([ WorkspaceEntity, WorkspaceUserRoleEntity, + ProjectUserRoleEntity, + RoleEntity, + RolePermissionEntity, + PermissionEntity, UserEntity, Project, ApiData, diff --git a/src/modules/workspace/workspace.service.ts b/src/modules/workspace/workspace.service.ts index b52ab2d..aee8eda 100644 --- a/src/modules/workspace/workspace.service.ts +++ b/src/modules/workspace/workspace.service.ts @@ -1,18 +1,29 @@ -import { Injectable, OnModuleInit } from '@nestjs/common'; +import { + BadRequestException, + Injectable, + OnModuleInit, + NotFoundException, +} from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { ModuleRef } from '@nestjs/core'; import { uniqBy } from 'lodash'; import { DeleteResult, FindOneOptions, In, Like, Repository } from 'typeorm'; import { CreateWorkspaceDto, + SetRoleDto, UpdateWorkspaceDto, WorkspaceUser, } from './workspace.dto'; +import { PermissionEntity } from '@/entities/permission.entity'; import { WorkspaceEntity } from '@/entities/workspace.entity'; import { UserService } from '@/modules/user/user.service'; import { ProjectService } from '@/modules/workspace/project/project.service'; import { Project } from '@/entities/project.entity'; import { CreateDto as ProjectCreateDto } from '@/modules/workspace/project/dto/create.dto'; +import { WorkspaceUserRoleEntity } from '@/entities/workspace-user-role.entity'; +import { RoleEnum } from '@/enums/role.enum'; +import { RoleEntity } from '@/entities/role.entity'; +import { RolePermissionEntity } from '@/entities/role-permission.entity'; @Injectable() export class WorkspaceService implements OnModuleInit { @@ -21,6 +32,14 @@ export class WorkspaceService implements OnModuleInit { private moduleRef: ModuleRef, @InjectRepository(WorkspaceEntity) private workspaceRepository: Repository, + @InjectRepository(WorkspaceUserRoleEntity) + private workspaceUserRoleRepo: Repository, + @InjectRepository(RoleEntity) + private roleRepo: Repository, + @InjectRepository(PermissionEntity) + private permissionRepo: Repository, + @InjectRepository(RolePermissionEntity) + private rolePermissionRepo: Repository, private projectService: ProjectService, ) {} @@ -45,19 +64,26 @@ export class WorkspaceService implements OnModuleInit { projects: true, }, }); + project ??= await this.projectService.save({ - name: '默认项目', - description: createWorkspaceDto.title + '默认项目', + name: 'My project', + description: createWorkspaceDto.title + 'My project', }); creator.projects = (creator?.projects || []).concat(project); - return this.workspaceRepository.save({ + const workspace = await this.workspaceRepository.save({ ...createWorkspaceDto, creatorID, users: [await this.userService.updateUser(creator)], projects: [project], }); + this.workspaceUserRoleRepo.save({ + workspaceID: workspace.id, + userID: creator.id, + roleID: RoleEnum.WorkspaceOwnerRoleID, + }); + return workspace; } async update( @@ -94,13 +120,17 @@ export class WorkspaceService implements OnModuleInit { username: Like(`%${username}%`), }, }); - const workspace = await this.workspaceRepository.findOneBy({ - id: workspaceId, - }); - return result.map((item) => ({ - ...item, - roleName: item.id === workspace.creatorID ? 'Owner' : 'Member', - })); + + for (const item of result) { + const userRole = await this.workspaceUserRoleRepo.findOneBy({ + userID: item.id, + workspaceID: workspaceId, + }); + const roleName = await this.roleRepo.findOneBy({ id: userRole.roleID }); + Reflect.set(item, 'roleName', roleName); + } + + return result as WorkspaceUser[]; } async addMembers(workspaceId: number, userIDs: number[]) { @@ -123,6 +153,11 @@ export class WorkspaceService implements OnModuleInit { // ...workspace, // users: [], // }); + this.workspaceUserRoleRepo.save({ + userID: user.id, + roleID: RoleEnum.WorkspaceEditorRoleID, + workspaceID: workspaceId, + }); user.projects = uniqBy([...user.projects, ...workspace.projects], 'uuid'); this.userService.updateUser(user); }); @@ -131,6 +166,18 @@ export class WorkspaceService implements OnModuleInit { } async removeMembers(workspaceId: number, userIDs: number[]) { + const workspaceUserRoles = await this.workspaceUserRoleRepo.findBy({ + userID: In(userIDs), + }); + const workspaceUserRoleOwners = await this.workspaceUserRoleRepo.findBy({ + roleID: RoleEnum.WorkspaceOwnerRoleID, + }); + if ( + workspaceUserRoleOwners.length === 1 && + workspaceUserRoles.some((n) => n.roleID === RoleEnum.WorkspaceOwnerRoleID) + ) { + throw new BadRequestException('操作失败!至少需要一名空间 owner'); + } const workspace = await this.workspaceRepository.findOne({ where: { id: workspaceId }, relations: { @@ -140,6 +187,37 @@ export class WorkspaceService implements OnModuleInit { workspace.users = workspace.users.filter( (user) => !userIDs.includes(user.id), ); + this.workspaceUserRoleRepo.delete({ userID: In(userIDs) }); return this.workspaceRepository.save(workspace); } + + async setMemberRole(dto: SetRoleDto) { + const userRole = await this.workspaceUserRoleRepo.findOneBy({ + userID: dto.memberID, + }); + userRole.roleID = dto.roleID; + return this.workspaceUserRoleRepo.save(userRole); + } + + async getRolePermission(userID: number, workspaceID: number) { + const userRole = await this.workspaceUserRoleRepo.findOneBy({ + userID, + workspaceID, + }); + if (!userRole) { + throw new NotFoundException('获取失败!你不在该空间里'); + } + const role = await this.roleRepo.findOneBy({ id: userRole.roleID }); + const rolePerm = await this.rolePermissionRepo.findBy({ + roleID: userRole.roleID, + }); + const permissions = await this.permissionRepo.findBy({ + id: In(rolePerm.map((n) => n.permissionID)), + }); + + return { + permissions: permissions.map((n) => n.name), + role, + }; + } } From f99b58d5761f13da872f6c9fa67f671a56e9dc7c Mon Sep 17 00:00:00 2001 From: scarqin <1054139596@qq.com> Date: Fri, 23 Dec 2022 16:53:20 +0800 Subject: [PATCH 06/27] feat: add project member permission --- src/enums/permission.enum.ts | 3 +++ src/migrations/1671766182451-update-table_1_11_0.ts | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/enums/permission.enum.ts b/src/enums/permission.enum.ts index ee4718d..839713e 100644 --- a/src/enums/permission.enum.ts +++ b/src/enums/permission.enum.ts @@ -14,6 +14,9 @@ export enum PermissionEnum { DELETE_PROJECT = 'delete:project', IMPORT_PROJECT = 'import:project', EXPORT_PROJECT = 'export:project', + ADD_PROJECT_MEMBER = 'add:project:member', + UPDATE_PROJECT_MEMBER = 'update:project:member', + DELETE_PROJECT__MEMBER = 'delete:project:member', /** apiGroup */ VIEW_API_GROUP = 'view:apiGroup', diff --git a/src/migrations/1671766182451-update-table_1_11_0.ts b/src/migrations/1671766182451-update-table_1_11_0.ts index 79444b1..aa748d1 100644 --- a/src/migrations/1671766182451-update-table_1_11_0.ts +++ b/src/migrations/1671766182451-update-table_1_11_0.ts @@ -17,6 +17,9 @@ const workspaceOwnerPerms = [ 'view:project', 'update:project', 'delete:project', + 'add:project:member', + 'update:project:member', + 'delete:project:member', ]; export class updateTable11101671725826115 implements MigrationInterface { From 88ab5f7576214a42b59be1726ae48ad2ed87e3ac Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Fri, 23 Dec 2022 16:56:54 +0800 Subject: [PATCH 07/27] chore: submit code to scar review --- src/migrations/1671766182451-update-table_1_11_0.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/migrations/1671766182451-update-table_1_11_0.ts b/src/migrations/1671766182451-update-table_1_11_0.ts index aa748d1..9130bac 100644 --- a/src/migrations/1671766182451-update-table_1_11_0.ts +++ b/src/migrations/1671766182451-update-table_1_11_0.ts @@ -90,7 +90,14 @@ export class updateTable11101671725826115 implements MigrationInterface { }, ); // workspace editor 没有删除或移除操作 - if (!['delete', 'remove'].some((n) => perm.name.includes(n))) { + if ( + ![ + 'delete', + 'remove', + 'add:workspace:member', + 'update:workspace:member', + ].some((n) => perm.name.includes(n)) + ) { await queryRunner.manager.insert( 'role_permission', { From f0b1f3b431c979787f52ecc784b41897f6e30c5b Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Fri, 23 Dec 2022 16:58:41 +0800 Subject: [PATCH 08/27] chore: submit code to scar review --- src/migrations/1671766182451-update-table_1_11_0.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/migrations/1671766182451-update-table_1_11_0.ts b/src/migrations/1671766182451-update-table_1_11_0.ts index 9130bac..2c3c717 100644 --- a/src/migrations/1671766182451-update-table_1_11_0.ts +++ b/src/migrations/1671766182451-update-table_1_11_0.ts @@ -124,7 +124,14 @@ export class updateTable11101671725826115 implements MigrationInterface { }, ); // project editor 没有删除或更新项目操作 - if (!['update:project', 'delete:project'].some((n) => n === name)) { + if ( + ![ + 'update:project', + 'delete:project', + 'update:project:member', + 'delete:project:member', + ].some((n) => n === name) + ) { await queryRunner.manager.insert( 'role_permission', { From 1f78e3af31a6cb4361556c1d4d0f48c38fa1c4a1 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Fri, 23 Dec 2022 20:53:30 +0800 Subject: [PATCH 09/27] chore: publish --- src/guards/jwt-auth.guard.ts | 28 ++- .../workspace/project/dto/update.dto.ts | 7 + .../workspace/project/project.controller.ts | 48 +++-- .../workspace/project/project.service.ts | 168 +++++++++++++++++- src/modules/workspace/workspace.controller.ts | 4 +- src/modules/workspace/workspace.service.ts | 14 +- 6 files changed, 230 insertions(+), 39 deletions(-) diff --git a/src/guards/jwt-auth.guard.ts b/src/guards/jwt-auth.guard.ts index 9bcb4fc..49d4d25 100644 --- a/src/guards/jwt-auth.guard.ts +++ b/src/guards/jwt-auth.guard.ts @@ -14,6 +14,7 @@ import { AuthService } from '@/modules/auth/auth.service'; import { IUser } from '@/common/decorators/user.decorator'; import { UserService } from '@/modules/user/user.service'; import { ProjectService } from '@/modules/workspace/project/project.service'; +import { WorkspaceService } from '@/modules/workspace/workspace.service'; /** * admin perm check guard @@ -26,6 +27,7 @@ export class JwtAuthGuard implements CanActivate { private authService: AuthService, private userService: UserService, private projectService: ProjectService, + private workspaceService: WorkspaceService, private readonly config: ConfigService, ) {} @@ -65,34 +67,28 @@ export class JwtAuthGuard implements CanActivate { throw new UnauthorizedException('当前用户不存在'); } + const userID = request.currentUser.userId; + 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, - workspaces: { - id: workspaceID, - }, - ...(Number.isNaN(projectID) - ? {} - : { - projects: { - uuid: projectID, - }, - }), - }); + if (Number(workspaceID) > 0) { + const hasWorkspaceAuth = await this.workspaceService.hasWorkspaceAuth( + workspaceID, + userID, + ); if (!hasWorkspaceAuth) { throw new ForbiddenException('没有该空间访问权限'); } } - if (!Number.isNaN(projectID)) { - const hasProjectAuth = await this.projectService.findOne( + if (Number(projectID) > 0) { + const hasProjectAuth = await this.projectService.hasProjectAuth( workspaceID, projectID, + userID, ); if (!hasProjectAuth) { throw new ForbiddenException('没有该项目访问权限'); diff --git a/src/modules/workspace/project/dto/update.dto.ts b/src/modules/workspace/project/dto/update.dto.ts index a1471ee..07fb6a3 100644 --- a/src/modules/workspace/project/dto/update.dto.ts +++ b/src/modules/workspace/project/dto/update.dto.ts @@ -1,8 +1,15 @@ import { PartialType } from '@nestjs/mapped-types'; +import { IsInt, Max, Min } from 'class-validator'; import { CreateDto } from './create.dto'; +import { RoleEnum } from '@/enums/role.enum'; export class UpdateDto extends PartialType(CreateDto) {} export class SetRoleDto { + @IsInt() + @Min(RoleEnum.ProjectOwnerRoleID) + @Max(RoleEnum.ProjectEditorRoleID) roleID: number; + + memberID: number; } diff --git a/src/modules/workspace/project/project.controller.ts b/src/modules/workspace/project/project.controller.ts index 05561d9..900644d 100644 --- a/src/modules/workspace/project/project.controller.ts +++ b/src/modules/workspace/project/project.controller.ts @@ -10,9 +10,8 @@ import { ParseIntPipe, NotFoundException, UseGuards, - UnauthorizedException, } from '@nestjs/common'; -import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ProjectService } from './project.service'; import { CreateDto } from './dto/create.dto'; import { SetRoleDto, UpdateDto } from './dto/update.dto'; @@ -31,10 +30,13 @@ import { import { RolesGuard } from '@/guards'; import { IUser, User } from '@/common/decorators/user.decorator'; import { + RolePermissionDto, WorkspaceMemberAddDto, WorkspaceMemberRemoveDto, + WorkspaceUser, } from '@/modules/workspace/workspace.dto'; import { RoleEntity } from '@/entities/role.entity'; +import { UserEntity } from '@/entities/user.entity'; @ApiTags('Project') @Controller(`${WORKSPACE_ID_PREFIX}/project`) @@ -45,10 +47,11 @@ export class ProjectController { @ApiCreatedResponseData(Project) @Post() async create( + @User() user: IUser, @Param('workspaceID', ParseIntPipe) workspaceID, @Body() createDto: CreateDto, ) { - const data = await this.service.create(createDto, workspaceID); + const data = await this.service.create(createDto, workspaceID, user.userId); return this.findOne(workspaceID, `${data.uuid}`); } @@ -136,6 +139,26 @@ export class ProjectController { return this.service.getProjectCollections(projectID); } + @ApiOkResponseData(WorkspaceUser, 'array') + @Get(':projectID/member/list') + @ApiOperation({ summary: '获取项目成员列表' }) + async getMemberList(@Param('projectID') projectID): Promise { + return this.service.getMemberList(projectID); + } + + @ApiOkResponseData(WorkspaceUser, 'array') + @Get(':projectID/member/list/:username') + @ApiOperation({ summary: '搜索项目成员' }) + @ApiResponse({ + type: [UserEntity], + }) + async searchMemberByName( + @Param('workspaceID') id, + @Param('username') username, + ): Promise { + return this.service.getMemberList(id, username); + } + @ApiCreatedResponseData(Project) @Post(':projectID/member/add') @ApiOperation({ summary: '添加项目成员' }) @@ -155,31 +178,36 @@ export class ProjectController { @Param('projectID') id, @Body() createCatDto: WorkspaceMemberRemoveDto, ) { - return '' as any; + return this.service.memberRemove(id, createCatDto); } @ApiOkResponseData(Project) @Post(':projectID/member/leave') @ApiOperation({ summary: '项目成员主动退出' }) async memberLeave(@User() user: IUser, @Param('projectID') id) { - return '' as any; + return this.service.memberLeave(user.userId, id); } @Post(':projectID/member/setRole') @ApiOperation({ summary: '设置项目成员角色' }) - async setMemberRole(@User() user: IUser, @Body() dto: SetRoleDto) { - return '' as any; + async setMemberRole(@Param('projectID') projectID, @Body() dto: SetRoleDto) { + return this.service.setProjectRole(projectID, dto); } - @Get(':workspaceID/roles') + @Get(':projectID/roles') @ApiOperation({ summary: '获取当前项目角色列表' }) async getRoles(): Promise { return []; } + @ApiOkResponseData(RolePermissionDto) @Get(':projectID/permissions') @ApiOperation({ summary: '获取当前项目权限' }) - async getPermission(): Promise { - return []; + async getPermission( + @User() user: IUser, + @Param('projectID') projectID, + @Param('workspaceID', ParseIntPipe) workspaceID, + ): Promise { + return this.service.getRolePermission(user.userId, projectID, workspaceID); } } diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index a5bcd4f..a0caa2c 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -1,40 +1,184 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, NotFoundException, OnModuleInit } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { DeepPartial, Repository } from 'typeorm'; +import { ModuleRef } from '@nestjs/core'; +import { DeepPartial, In, Like, Repository } from 'typeorm'; import { ApiGroupService } from '../apiGroup/apiGroup.service'; import { ApiDataService } from '../apiData/apiData.service'; import { EnvironmentService } from '../environment/environment.service'; import { CreateDto } from './dto/create.dto'; -import { UpdateDto } from './dto/update.dto'; +import { SetRoleDto, UpdateDto } from './dto/update.dto'; import { QueryDto } from './dto/query.dto'; import { Child, Environment, ImportDto, ImportResult } from './dto/import.dto'; import { parseAndCheckApiData, parseAndCheckEnv } from './validate'; import { Project } from '@/entities/project.entity'; import { WorkspaceEntity } from '@/entities/workspace.entity'; -import { WorkspaceMemberAddDto } from '@/modules/workspace/workspace.dto'; +import { + WorkspaceMemberAddDto, + WorkspaceMemberRemoveDto, + WorkspaceUser, +} from '@/modules/workspace/workspace.dto'; import { ProjectUserRoleEntity } from '@/entities/project-user-role.entity'; +import { RoleEnum } from '@/enums/role.enum'; +import { RoleEntity } from '@/entities/role.entity'; +import { PermissionEntity } from '@/entities/permission.entity'; +import { RolePermissionEntity } from '@/entities/role-permission.entity'; +import { WorkspaceUserRoleEntity } from '@/entities/workspace-user-role.entity'; +import { UserService } from '@/modules/user/user.service'; @Injectable() -export class ProjectService { +export class ProjectService implements OnModuleInit { + private userService: UserService; constructor( + private moduleRef: ModuleRef, @InjectRepository(Project) private readonly repository: Repository, @InjectRepository(WorkspaceEntity) private workspaceRepository: Repository, @InjectRepository(ProjectUserRoleEntity) private readonly projectUserRoleRepo: Repository, + @InjectRepository(RoleEntity) + private roleRepo: Repository, + @InjectRepository(PermissionEntity) + private permissionRepo: Repository, + @InjectRepository(RolePermissionEntity) + private rolePermissionRepo: Repository, + @InjectRepository(WorkspaceUserRoleEntity) + private workspaceUserRoleRepo: Repository, private readonly apiDataService: ApiDataService, private readonly apiGroupService: ApiGroupService, private readonly environmentService: EnvironmentService, ) {} + onModuleInit() { + this.userService = this.moduleRef.get(UserService, { strict: false }); + } + async memberAdd(projectID: number, addMemberDto: WorkspaceMemberAddDto) { return addMemberDto.userIDs.map((userID) => { - return this.projectUserRoleRepo.save({ projectID, userID }); + return this.projectUserRoleRepo.save({ + projectID, + userID, + roleID: RoleEnum.ProjectEditorRoleID, + }); + }); + } + + async memberRemove( + projectID: number, + createCatDto: WorkspaceMemberRemoveDto, + ) { + return this.projectUserRoleRepo.delete({ + projectID, + userID: In(createCatDto.userIDs), + }); + } + + async memberLeave(userID: number, projectID: number) { + return this.projectUserRoleRepo.delete({ + projectID, + userID, + }); + } + + async getMemberList( + projectID: number, + username = '', + ): Promise { + const userRoles = await this.projectUserRoleRepo.find({ + where: { projectID }, + }); + const users = await this.userService.find({ + where: { + id: In(userRoles.map((n) => n.userID)), + username: Like(`%${username}%`), + }, + }); + + for (const item of users) { + const userRole = await this.projectUserRoleRepo.findOneBy({ + userID: item.id, + projectID, + }); + const role = await this.roleRepo.findOneBy({ id: userRole.roleID }); + Reflect.set(item, 'roleName', role.name); + } + + return users as WorkspaceUser[]; + } + + async setProjectRole(projectID: number, dto: SetRoleDto) { + const projectUserRole = await this.projectUserRoleRepo.findOneBy({ + projectID, + userID: dto.memberID, + }); + projectUserRole.roleID = dto.roleID; + + return this.projectUserRoleRepo.save(projectUserRole); + } + + async getRolePermission( + userID: number, + projectID: number, + workspaceID: number, + ) { + const isWorkspaceOwner = await this.workspaceUserRoleRepo.findOneBy({ + userID, + workspaceID, + }); + + // 如果是空间 owner,那么直接将项目 owner 的权限返回 + if (isWorkspaceOwner) { + const rolePerm = await this.rolePermissionRepo.findBy({ + roleID: RoleEnum.ProjectOwnerRoleID, + }); + const permissions = await this.permissionRepo.findBy({ + id: In(rolePerm.map((n) => n.permissionID)), + }); + + return { + permissions: permissions.map((n) => n.name), + role: await this.roleRepo.findOneBy({ + id: RoleEnum.ProjectOwnerRoleID, + }), + }; + } + + const userRole = await this.projectUserRoleRepo.findOneBy({ + userID, + projectID, }); + if (!userRole) { + throw new NotFoundException('获取失败!你不在该项目里'); + } + const role = await this.roleRepo.findOneBy({ id: userRole.roleID }); + const rolePerm = await this.rolePermissionRepo.findBy({ + roleID: userRole.roleID, + }); + const permissions = await this.permissionRepo.findBy({ + id: In(rolePerm.map((n) => n.permissionID)), + }); + + return { + permissions: permissions.map((n) => n.name), + role, + }; } - async create(createDto: CreateDto, workspaceID: number) { + async hasProjectAuth(workspaceID: number, projectID: number, userID: number) { + const isWorkspaceOwner = await this.workspaceUserRoleRepo.findOneBy({ + userID, + workspaceID, + }); + return ( + isWorkspaceOwner || + this.projectUserRoleRepo.findOneBy({ + projectID, + userID, + }) + ); + } + + async create(createDto: CreateDto, workspaceID: number, userID: number) { const workspace = await this.workspaceRepository.findOne({ where: { id: workspaceID, @@ -43,11 +187,19 @@ export class ProjectService { users: true, }, }); - return await this.repository.save({ + const project = await this.repository.save({ ...createDto, workspace, users: workspace.users, }); + + this.projectUserRoleRepo.save({ + userID, + projectID: project.uuid, + roleID: RoleEnum.ProjectOwnerRoleID, + }); + + return project; } async save(project: DeepPartial) { diff --git a/src/modules/workspace/workspace.controller.ts b/src/modules/workspace/workspace.controller.ts index d0ebb62..b157bc2 100644 --- a/src/modules/workspace/workspace.controller.ts +++ b/src/modules/workspace/workspace.controller.ts @@ -219,8 +219,8 @@ export class WorkspaceController { @Post(':workspaceID/member/setRole') @ApiOperation({ summary: '设置空间成员角色' }) - async setMemberRole(@Body() dto: SetRoleDto) { - return this.workspaceService.setMemberRole(dto); + async setMemberRole(@Param('workspaceID') id, @Body() dto: SetRoleDto) { + return this.workspaceService.setMemberRole(id, dto); } @Get(':workspaceID/roles') diff --git a/src/modules/workspace/workspace.service.ts b/src/modules/workspace/workspace.service.ts index aee8eda..b56f834 100644 --- a/src/modules/workspace/workspace.service.ts +++ b/src/modules/workspace/workspace.service.ts @@ -126,8 +126,8 @@ export class WorkspaceService implements OnModuleInit { userID: item.id, workspaceID: workspaceId, }); - const roleName = await this.roleRepo.findOneBy({ id: userRole.roleID }); - Reflect.set(item, 'roleName', roleName); + const role = await this.roleRepo.findOneBy({ id: userRole.roleID }); + Reflect.set(item, 'roleName', role.name); } return result as WorkspaceUser[]; @@ -191,8 +191,9 @@ export class WorkspaceService implements OnModuleInit { return this.workspaceRepository.save(workspace); } - async setMemberRole(dto: SetRoleDto) { + async setMemberRole(workspaceID: number, dto: SetRoleDto) { const userRole = await this.workspaceUserRoleRepo.findOneBy({ + workspaceID, userID: dto.memberID, }); userRole.roleID = dto.roleID; @@ -220,4 +221,11 @@ export class WorkspaceService implements OnModuleInit { role, }; } + + async hasWorkspaceAuth(workspaceID: number, userID: number) { + return this.workspaceUserRoleRepo.findOneBy({ + workspaceID, + userID, + }); + } } From dfadabc41636c3db1d66847575aca93c206a8322 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Fri, 23 Dec 2022 21:06:40 +0800 Subject: [PATCH 10/27] refactor: rename roleName to role --- src/modules/workspace/project/project.service.ts | 2 +- src/modules/workspace/workspace.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index a0caa2c..3bccd59 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -100,7 +100,7 @@ export class ProjectService implements OnModuleInit { projectID, }); const role = await this.roleRepo.findOneBy({ id: userRole.roleID }); - Reflect.set(item, 'roleName', role.name); + Reflect.set(item, 'role', role); } return users as WorkspaceUser[]; diff --git a/src/modules/workspace/workspace.service.ts b/src/modules/workspace/workspace.service.ts index b56f834..a385070 100644 --- a/src/modules/workspace/workspace.service.ts +++ b/src/modules/workspace/workspace.service.ts @@ -127,7 +127,7 @@ export class WorkspaceService implements OnModuleInit { workspaceID: workspaceId, }); const role = await this.roleRepo.findOneBy({ id: userRole.roleID }); - Reflect.set(item, 'roleName', role.name); + Reflect.set(item, 'role', role); } return result as WorkspaceUser[]; From 0a7025c376525dc345c5fa06d752f746970d1986 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Fri, 23 Dec 2022 21:09:42 +0800 Subject: [PATCH 11/27] add userID --- .github/workflows/build-latest.yml | 6 +++--- src/modules/workspace/project/project.service.ts | 1 + src/modules/workspace/workspace.service.ts | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-latest.yml b/.github/workflows/build-latest.yml index ec59f63..4c6aa75 100644 --- a/.github/workflows/build-latest.yml +++ b/.github/workflows/build-latest.yml @@ -1,9 +1,9 @@ name: Build Latest Image on: - release: - types: - - "published" + push: + tags: + - 'v*.*.*' env: VERSION: ${{ github.ref_name }} diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index 3bccd59..e2a5dbb 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -101,6 +101,7 @@ export class ProjectService implements OnModuleInit { }); const role = await this.roleRepo.findOneBy({ id: userRole.roleID }); Reflect.set(item, 'role', role); + Reflect.set(item, 'userID', item.id); } return users as WorkspaceUser[]; diff --git a/src/modules/workspace/workspace.service.ts b/src/modules/workspace/workspace.service.ts index a385070..0ed1be3 100644 --- a/src/modules/workspace/workspace.service.ts +++ b/src/modules/workspace/workspace.service.ts @@ -128,6 +128,7 @@ export class WorkspaceService implements OnModuleInit { }); const role = await this.roleRepo.findOneBy({ id: userRole.roleID }); Reflect.set(item, 'role', role); + Reflect.set(item, 'userID', item.id); } return result as WorkspaceUser[]; From 89bf28b41bde91219aa8f1ddf3ce78812b08ae1a Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Fri, 23 Dec 2022 21:27:39 +0800 Subject: [PATCH 12/27] chore: update code --- package.json | 2 +- src/modules/workspace/project/project.controller.ts | 13 +++++++++++-- src/modules/workspace/workspace.controller.ts | 7 ++++++- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f642184..91843fb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eoapi-remote-server", - "version": "1.11.0", + "version": "2.0.0", "description": "Storage api data in remote server", "author": "eoapi", "private": true, diff --git a/src/modules/workspace/project/project.controller.ts b/src/modules/workspace/project/project.controller.ts index 900644d..e846d3a 100644 --- a/src/modules/workspace/project/project.controller.ts +++ b/src/modules/workspace/project/project.controller.ts @@ -37,6 +37,8 @@ import { } from '@/modules/workspace/workspace.dto'; import { RoleEntity } from '@/entities/role.entity'; import { UserEntity } from '@/entities/user.entity'; +import { Permissions } from '@/common/decorators/permission.decorator'; +import { PermissionEnum } from '@/enums/permission.enum'; @ApiTags('Project') @Controller(`${WORKSPACE_ID_PREFIX}/project`) @@ -70,6 +72,7 @@ export class ProjectController { return this.service.findAll(query, workspaceID); } + @Permissions(PermissionEnum.VIEW_PROJECT) @ApiOkResponseData(Project) @Get(':projectID') async findOne( @@ -78,6 +81,8 @@ export class ProjectController { ) { return this.service.findOne(workspaceID, projectID); } + + @Permissions(PermissionEnum.UPDATE_PROJECT) @ApiOkResponseData(Project) @Put(':projectID') async update( @@ -92,6 +97,8 @@ export class ProjectController { return new NotFoundException('更新失败!项目不存在'); } + + @Permissions(PermissionEnum.DELETE_PROJECT) @ApiOkResponseData() @Delete(':projectID') async remove(@Param('projectID') projectID: string) { @@ -159,6 +166,7 @@ export class ProjectController { return this.service.getMemberList(id, username); } + @Permissions(PermissionEnum.ADD_PROJECT_MEMBER) @ApiCreatedResponseData(Project) @Post(':projectID/member/add') @ApiOperation({ summary: '添加项目成员' }) @@ -170,6 +178,7 @@ export class ProjectController { return this.service.memberAdd(projectID, addMemberDto); } + @Permissions(PermissionEnum.DELETE_PROJECT__MEMBER) @ApiOkResponseData(Project) @Delete(':projectID/member/remove') @ApiOperation({ summary: '移除项目成员' }) @@ -201,8 +210,8 @@ export class ProjectController { } @ApiOkResponseData(RolePermissionDto) - @Get(':projectID/permissions') - @ApiOperation({ summary: '获取当前项目权限' }) + @Get(':projectID/rolePermission') + @ApiOperation({ summary: '获取当前用户在项目的角色和权限' }) async getPermission( @User() user: IUser, @Param('projectID') projectID, diff --git a/src/modules/workspace/workspace.controller.ts b/src/modules/workspace/workspace.controller.ts index b157bc2..395d311 100644 --- a/src/modules/workspace/workspace.controller.ts +++ b/src/modules/workspace/workspace.controller.ts @@ -42,7 +42,8 @@ import { } from '@/common/class/res.class'; import { RolesGuard } from '@/guards'; import { RoleEntity } from '@/entities/role.entity'; -import { PermissionEntity } from '@/entities/permission.entity'; +import { Permissions } from '@/common/decorators/permission.decorator'; +import { PermissionEnum } from '@/enums/permission.enum'; @ApiBearerAuth() @ApiTags('workspace') @@ -100,6 +101,7 @@ export class WorkspaceController { } } + @Permissions(PermissionEnum.UPDATE_WORKSPACE) @ApiOkResponseData(WorkspaceEntity) @Put(':workspaceID') @ApiOperation({ summary: '修改空间名称' }) @@ -110,6 +112,7 @@ export class WorkspaceController { return this.workspaceService.update(id, updateDto); } + @Permissions(PermissionEnum.DELETE_WORKSPACE) @ApiOkResponseData() @Delete(':workspaceID') @ApiOperation({ summary: '删除空间' }) @@ -167,6 +170,7 @@ export class WorkspaceController { return this.workspaceService.getMemberList(id, username); } + @Permissions(PermissionEnum.ADD_WORKSPACE_MEMBER) @ApiCreatedResponseData(WorkspaceEntity) @Post(':workspaceID/member/add') @ApiOperation({ summary: '添加空间成员' }) @@ -182,6 +186,7 @@ export class WorkspaceController { return this.workspaceService.addMembers(id, createCatDto.userIDs); } + @Permissions(PermissionEnum.DELETE_WORKSPACE__MEMBER) @ApiOkResponseData(WorkspaceEntity) @Delete(':workspaceID/member/remove') @ApiOperation({ summary: '移除空间成员' }) From 4f7a921378594914361c4cc8ccc535a7cd348cc7 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Fri, 23 Dec 2022 22:23:31 +0800 Subject: [PATCH 13/27] feat: sort memberlist --- src/guards/roles.guard.ts | 17 ++++++++++++++--- .../workspace/project/project.service.ts | 3 +-- src/modules/workspace/workspace.dto.ts | 2 +- src/modules/workspace/workspace.service.ts | 3 +-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/guards/roles.guard.ts b/src/guards/roles.guard.ts index 6d55d8a..de1a7b9 100644 --- a/src/guards/roles.guard.ts +++ b/src/guards/roles.guard.ts @@ -2,10 +2,16 @@ import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { PERMISSIONS_KEY } from '@/common/decorators/permission.decorator'; import { PermissionEnum } from '@/enums/permission.enum'; +import { ProjectService } from '@/modules/workspace/project/project.service'; +import { WorkspaceService } from '@/modules/workspace/workspace.service'; @Injectable() export class RolesGuard implements CanActivate { - constructor(private reflector: Reflector) {} + constructor( + private reflector: Reflector, + private projectService: ProjectService, + private workspaceService: WorkspaceService, + ) {} canActivate(context: ExecutionContext): boolean { // 当前请求所需权限 @@ -13,12 +19,17 @@ export class RolesGuard implements CanActivate { PERMISSIONS_KEY, [context.getHandler(), context.getClass()], ); - console.log('currentPerm', currentPerm); + // 当前用户 + const { currentUser, params } = context.switchToHttp().getRequest(); + console.log('currentPerm', currentPerm, currentUser, params); + if (!currentPerm) { return true; } + + // const rolePermission = this.workspaceService.getRolePermission(currentUser); + return true; - // const { user } = context.switchToHttp().getRequest(); // return requiredRoles.some((role) => user.roles?.includes(role)); } } diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index e2a5dbb..2a70871 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -101,10 +101,9 @@ export class ProjectService implements OnModuleInit { }); const role = await this.roleRepo.findOneBy({ id: userRole.roleID }); Reflect.set(item, 'role', role); - Reflect.set(item, 'userID', item.id); } - return users as WorkspaceUser[]; + return (users as WorkspaceUser[]).sort((a, b) => a.role.id - b.role.id); } async setProjectRole(projectID: number, dto: SetRoleDto) { diff --git a/src/modules/workspace/workspace.dto.ts b/src/modules/workspace/workspace.dto.ts index ea56773..3463a65 100644 --- a/src/modules/workspace/workspace.dto.ts +++ b/src/modules/workspace/workspace.dto.ts @@ -40,7 +40,7 @@ export class WorkspaceUser extends UserEntity { @ApiProperty({ description: '成员身份' }) @MinLength(1) @IsString() - readonly roleName: string = 'member'; + readonly role: RoleEntity; } export class SetRoleDto { @IsInt() diff --git a/src/modules/workspace/workspace.service.ts b/src/modules/workspace/workspace.service.ts index 0ed1be3..c0ec37b 100644 --- a/src/modules/workspace/workspace.service.ts +++ b/src/modules/workspace/workspace.service.ts @@ -128,10 +128,9 @@ export class WorkspaceService implements OnModuleInit { }); const role = await this.roleRepo.findOneBy({ id: userRole.roleID }); Reflect.set(item, 'role', role); - Reflect.set(item, 'userID', item.id); } - return result as WorkspaceUser[]; + return (result as WorkspaceUser[]).sort((a, b) => a.role.id - b.role.id); } async addMembers(workspaceId: number, userIDs: number[]) { From 23fe9d8a96188c34a10af1a333c59672505e519e Mon Sep 17 00:00:00 2001 From: scarqin <1054139596@qq.com> Date: Fri, 23 Dec 2022 17:15:49 +0800 Subject: [PATCH 14/27] fix: project editor without add member --- src/migrations/1671766182451-update-table_1_11_0.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/migrations/1671766182451-update-table_1_11_0.ts b/src/migrations/1671766182451-update-table_1_11_0.ts index 2c3c717..1e524dc 100644 --- a/src/migrations/1671766182451-update-table_1_11_0.ts +++ b/src/migrations/1671766182451-update-table_1_11_0.ts @@ -128,6 +128,7 @@ export class updateTable11101671725826115 implements MigrationInterface { ![ 'update:project', 'delete:project', + 'add:project:member', 'update:project:member', 'delete:project:member', ].some((n) => n === name) From da04337f49732d2fa4d485011d7dd758c8994d8a Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Fri, 23 Dec 2022 23:13:13 +0800 Subject: [PATCH 15/27] feat: sort memberlist --- src/guards/roles.guard.ts | 7 ++++--- src/modules/workspace/project/project.controller.ts | 6 ++++-- src/modules/workspace/project/project.service.ts | 1 + src/modules/workspace/workspace.service.ts | 9 +++++++++ 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/guards/roles.guard.ts b/src/guards/roles.guard.ts index de1a7b9..a1e6516 100644 --- a/src/guards/roles.guard.ts +++ b/src/guards/roles.guard.ts @@ -20,14 +20,15 @@ export class RolesGuard implements CanActivate { [context.getHandler(), context.getClass()], ); // 当前用户 - const { currentUser, params } = context.switchToHttp().getRequest(); - console.log('currentPerm', currentPerm, currentUser, params); + const { currentUser, params = {} } = context.switchToHttp().getRequest(); + const userID = currentUser.userId; + // console.log('currentPerm', currentPerm, currentUser, params); if (!currentPerm) { return true; } - // const rolePermission = this.workspaceService.getRolePermission(currentUser); + // const rolePermission = this.workspaceService.getRolePermission(userID); return true; // return requiredRoles.some((role) => user.roles?.includes(role)); diff --git a/src/modules/workspace/project/project.controller.ts b/src/modules/workspace/project/project.controller.ts index e846d3a..1dc8087 100644 --- a/src/modules/workspace/project/project.controller.ts +++ b/src/modules/workspace/project/project.controller.ts @@ -149,7 +149,9 @@ export class ProjectController { @ApiOkResponseData(WorkspaceUser, 'array') @Get(':projectID/member/list') @ApiOperation({ summary: '获取项目成员列表' }) - async getMemberList(@Param('projectID') projectID): Promise { + async getMemberList( + @Param('projectID', ParseIntPipe) projectID, + ): Promise { return this.service.getMemberList(projectID); } @@ -160,7 +162,7 @@ export class ProjectController { type: [UserEntity], }) async searchMemberByName( - @Param('workspaceID') id, + @Param('projectID', ParseIntPipe) id, @Param('username') username, ): Promise { return this.service.getMemberList(id, username); diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index 2a70871..bb0b991 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -87,6 +87,7 @@ export class ProjectService implements OnModuleInit { const userRoles = await this.projectUserRoleRepo.find({ where: { projectID }, }); + console.log('userRoles', userRoles, projectID); const users = await this.userService.find({ where: { id: In(userRoles.map((n) => n.userID)), diff --git a/src/modules/workspace/workspace.service.ts b/src/modules/workspace/workspace.service.ts index c0ec37b..8f00bd9 100644 --- a/src/modules/workspace/workspace.service.ts +++ b/src/modules/workspace/workspace.service.ts @@ -24,6 +24,7 @@ import { WorkspaceUserRoleEntity } from '@/entities/workspace-user-role.entity'; import { RoleEnum } from '@/enums/role.enum'; import { RoleEntity } from '@/entities/role.entity'; import { RolePermissionEntity } from '@/entities/role-permission.entity'; +import { ProjectUserRoleEntity } from '@/entities/project-user-role.entity'; @Injectable() export class WorkspaceService implements OnModuleInit { @@ -34,6 +35,8 @@ export class WorkspaceService implements OnModuleInit { private workspaceRepository: Repository, @InjectRepository(WorkspaceUserRoleEntity) private workspaceUserRoleRepo: Repository, + @InjectRepository(ProjectUserRoleEntity) + private projectUserRoleRepo: Repository, @InjectRepository(RoleEntity) private roleRepo: Repository, @InjectRepository(PermissionEntity) @@ -70,6 +73,12 @@ export class WorkspaceService implements OnModuleInit { description: createWorkspaceDto.title + 'My project', }); + this.projectUserRoleRepo.save({ + projectID: project.uuid, + userID: creator.id, + roleID: RoleEnum.ProjectOwnerRoleID, + }); + creator.projects = (creator?.projects || []).concat(project); const workspace = await this.workspaceRepository.save({ From 163fe7acea3ad787133640e16f81e73deb937e89 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Fri, 23 Dec 2022 23:43:37 +0800 Subject: [PATCH 16/27] fix: request body type --- src/modules/workspace/apiData/samples/sample.api.data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/workspace/apiData/samples/sample.api.data.ts b/src/modules/workspace/apiData/samples/sample.api.data.ts index c6b1d09..35c5784 100644 --- a/src/modules/workspace/apiData/samples/sample.api.data.ts +++ b/src/modules/workspace/apiData/samples/sample.api.data.ts @@ -10,7 +10,7 @@ export const sampleApiData = [ method: 'GET', requestBodyType: 'raw', requestBodyJsonType: 'object', - requestBody: {}, + requestBody: [], queryParams: [], restParams: [ { From 86516e77866e4e1e9d3030240b26e2f623512452 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Fri, 23 Dec 2022 23:44:31 +0800 Subject: [PATCH 17/27] fix: request body type --- src/modules/workspace/apiData/samples/sample.api.data.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/workspace/apiData/samples/sample.api.data.ts b/src/modules/workspace/apiData/samples/sample.api.data.ts index 35c5784..a812c2d 100644 --- a/src/modules/workspace/apiData/samples/sample.api.data.ts +++ b/src/modules/workspace/apiData/samples/sample.api.data.ts @@ -110,7 +110,7 @@ export const sampleApiData = [ method: 'GET', requestBodyType: 'raw', requestBodyJsonType: 'object', - requestBody: {}, + requestBody: [], queryParams: [{ name: 'name', required: true, example: 'disease_h5' }], restParams: [], requestHeaders: [], From 07ceefeae21d6f3b4a364103af1cc388c7ed52d9 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Sat, 24 Dec 2022 00:00:14 +0800 Subject: [PATCH 18/27] fix: request body type --- src/modules/workspace/project/project.service.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index bb0b991..483b4e7 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -122,15 +122,18 @@ export class ProjectService implements OnModuleInit { projectID: number, workspaceID: number, ) { - const isWorkspaceOwner = await this.workspaceUserRoleRepo.findOneBy({ + const workspaceOwner = await this.workspaceUserRoleRepo.findOneBy({ userID, workspaceID, }); // 如果是空间 owner,那么直接将项目 owner 的权限返回 - if (isWorkspaceOwner) { + if (workspaceOwner) { const rolePerm = await this.rolePermissionRepo.findBy({ - roleID: RoleEnum.ProjectOwnerRoleID, + roleID: In([ + RoleEnum.WorkspaceOwnerRoleID, + RoleEnum.ProjectOwnerRoleID, + ]), }); const permissions = await this.permissionRepo.findBy({ id: In(rolePerm.map((n) => n.permissionID)), @@ -139,7 +142,7 @@ export class ProjectService implements OnModuleInit { return { permissions: permissions.map((n) => n.name), role: await this.roleRepo.findOneBy({ - id: RoleEnum.ProjectOwnerRoleID, + id: RoleEnum.WorkspaceOwnerRoleID, }), }; } From 613a264edca06e9540d1da26804ac7bc1f5724d7 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Sat, 24 Dec 2022 00:07:00 +0800 Subject: [PATCH 19/27] fix: request body type --- src/modules/workspace/project/project.controller.ts | 6 ++++-- src/modules/workspace/project/project.service.ts | 11 ++++++++--- src/modules/workspace/workspace.service.ts | 10 +++++----- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/modules/workspace/project/project.controller.ts b/src/modules/workspace/project/project.controller.ts index 1dc8087..31df178 100644 --- a/src/modules/workspace/project/project.controller.ts +++ b/src/modules/workspace/project/project.controller.ts @@ -151,8 +151,9 @@ export class ProjectController { @ApiOperation({ summary: '获取项目成员列表' }) async getMemberList( @Param('projectID', ParseIntPipe) projectID, + @Param('workspaceID', ParseIntPipe) workspaceID, ): Promise { - return this.service.getMemberList(projectID); + return this.service.getMemberList(projectID, workspaceID); } @ApiOkResponseData(WorkspaceUser, 'array') @@ -163,9 +164,10 @@ export class ProjectController { }) async searchMemberByName( @Param('projectID', ParseIntPipe) id, + @Param('workspaceID', ParseIntPipe) workspaceID, @Param('username') username, ): Promise { - return this.service.getMemberList(id, username); + return this.service.getMemberList(id, workspaceID, username); } @Permissions(PermissionEnum.ADD_PROJECT_MEMBER) diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index 483b4e7..722e79a 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -82,15 +82,20 @@ export class ProjectService implements OnModuleInit { async getMemberList( projectID: number, + workspaceID: number, username = '', ): Promise { - const userRoles = await this.projectUserRoleRepo.find({ + const pUserRoles = await this.projectUserRoleRepo.find({ where: { projectID }, }); - console.log('userRoles', userRoles, projectID); + + const wUserRoles = await this.workspaceUserRoleRepo.find({ + where: { workspaceID, roleID: RoleEnum.WorkspaceOwnerRoleID }, + }); + const users = await this.userService.find({ where: { - id: In(userRoles.map((n) => n.userID)), + id: In([...pUserRoles, ...wUserRoles].map((n) => n.userID)), username: Like(`%${username}%`), }, }); diff --git a/src/modules/workspace/workspace.service.ts b/src/modules/workspace/workspace.service.ts index 8f00bd9..b67999f 100644 --- a/src/modules/workspace/workspace.service.ts +++ b/src/modules/workspace/workspace.service.ts @@ -73,11 +73,11 @@ export class WorkspaceService implements OnModuleInit { description: createWorkspaceDto.title + 'My project', }); - this.projectUserRoleRepo.save({ - projectID: project.uuid, - userID: creator.id, - roleID: RoleEnum.ProjectOwnerRoleID, - }); + // this.projectUserRoleRepo.save({ + // projectID: project.uuid, + // userID: creator.id, + // roleID: RoleEnum.ProjectOwnerRoleID, + // }) creator.projects = (creator?.projects || []).concat(project); From 8e316d7f46a61c687d2482dbe5a40ad5fb2e2474 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Sat, 24 Dec 2022 00:18:37 +0800 Subject: [PATCH 20/27] fix: request body type --- src/modules/workspace/project/project.service.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index 722e79a..bbc095d 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -93,19 +93,18 @@ export class ProjectService implements OnModuleInit { where: { workspaceID, roleID: RoleEnum.WorkspaceOwnerRoleID }, }); + const userRoles = [...pUserRoles, ...wUserRoles]; + const users = await this.userService.find({ where: { - id: In([...pUserRoles, ...wUserRoles].map((n) => n.userID)), + id: In(userRoles.map((n) => n.userID)), username: Like(`%${username}%`), }, }); for (const item of users) { - const userRole = await this.projectUserRoleRepo.findOneBy({ - userID: item.id, - projectID, - }); - const role = await this.roleRepo.findOneBy({ id: userRole.roleID }); + const target = userRoles.find((n) => n.userID === item.id); + const role = await this.roleRepo.findOneBy({ id: target.roleID }); Reflect.set(item, 'role', role); } From 301c0b89f45f9e31f7eeb1539078bb7a591f1e49 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Sat, 24 Dec 2022 00:42:37 +0800 Subject: [PATCH 21/27] fix: remove default logic --- src/modules/user/user.service.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 39c767f..78880f5 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -130,18 +130,6 @@ export class UserService implements OnModuleInit { password: this.utils.md5(userDto.password), }); this.userRepository.update(user.id, { username: `user_${nanoid(10)}` }); - if (user.id === 1) { - const defaultProject = await this.projectService.findOneBy(1); - if (defaultProject) { - this.workspaceService.create( - user.id, - { - title: '默认空间', - }, - defaultProject, - ); - } - } return { ...user, From c31ea58aefe1a0dd1b911b489d930338521a3284 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Mon, 26 Dec 2022 10:40:17 +0800 Subject: [PATCH 22/27] chore: update .env --- .env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env b/.env index d1f7d3a..7902025 100644 --- a/.env +++ b/.env @@ -4,7 +4,7 @@ EOAPI_API_PREFIX=/api # mysql configure TZ=Asia/Shanghai -MYSQL_HOST=localhost +MYSQL_HOST=mysql MYSQL_PORT=33066 MYSQL_USERNAME=root MYSQL_DATABASE=eoapi From fbd4c10f70d5e2e20655dfd616c2afc4ca4e2242 Mon Sep 17 00:00:00 2001 From: buqiyuan <1743369777@qq.com> Date: Mon, 26 Dec 2022 15:28:13 +0800 Subject: [PATCH 23/27] chore: update .env --- docker-compose.yaml | 4 ++-- src/modules/user/user.dto.ts | 10 ---------- src/modules/user/user.service.ts | 18 +++++------------- 3 files changed, 7 insertions(+), 25 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index dd931dd..c5985e8 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -35,7 +35,7 @@ services: - eoapi_net eoapi: - image: eolinker/eoapi:latest + image: eolinker/eoapi:dev container_name: eoapi deploy: restart_policy: @@ -51,7 +51,7 @@ services: - eoapi_net eoapi-test-server: - image: eolinker/eoapi-test-server:latest + image: eolinker/eoapi-test-server:dev container_name: eoapi-test-server deploy: restart_policy: diff --git a/src/modules/user/user.dto.ts b/src/modules/user/user.dto.ts index 000a089..2288015 100644 --- a/src/modules/user/user.dto.ts +++ b/src/modules/user/user.dto.ts @@ -26,11 +26,6 @@ export class UpdateUserInfoDto extends PartialType( ) {} export class UpdateUserPasswordDto { - @ApiProperty({ description: '旧密码', required: false }) - @MinLength(6) - @IsString() - readonly oldPassword: string; - @ApiProperty({ description: '新密码', required: false }) @MinLength(6) @IsString() @@ -38,11 +33,6 @@ export class UpdateUserPasswordDto { } export class UserInfoValidator { - @ApiProperty({ description: '旧密码', required: false }) - @MinLength(6) - @IsString() - readonly oldPassword: string; - @ApiProperty({ description: '新密码', required: false }) @MinLength(6) @IsString() diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index 78880f5..322dfbe 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -86,7 +86,6 @@ export class UserService implements OnModuleInit { validateUser(userDto: Partial) { const userValidator = new UserEntity(); userValidator.email = userDto.username; - userValidator.mobilePhone = userDto.username; return validate(userValidator).then((errors) => { const result = {} as UserEntity; @@ -94,9 +93,6 @@ export class UserService implements OnModuleInit { if (!validateFields.includes('email')) { result.email = userDto.username; } - if (!validateFields.includes('mobilePhone')) { - result.mobilePhone = userDto.username; - } return result; }); } @@ -122,7 +118,7 @@ export class UserService implements OnModuleInit { } else { const other = await this.validateUser(userDto); if (Object.keys(other).length === 0) { - throw new Error('用户名必须是手机号码或邮箱'); + throw new Error('用户名必须是邮箱'); } const user = await this.userRepository.save({ ...other, @@ -153,10 +149,6 @@ export class UserService implements OnModuleInit { userId, userInfoDto: UpdateUserInfoDto, ): Promise { - // const other = await this.validateUser(userInfoDto); - // if (userInfoDto.username && Object.keys(other).length === 0) { - // throw new Error('用户名必须是手机号码或邮箱'); - // } const isConflict = await this.userRepository.findOne({ where: { username: Equal(userInfoDto.username), id: Not(userId) }, }); @@ -184,10 +176,10 @@ export class UserService implements OnModuleInit { if (!userPasswordDto.newPassword) { throw new ConflictException('新密码不能为空'); } - const oldPassword = this.utils.md5(userPasswordDto.oldPassword); - if (oldPassword !== user.password) { - throw new ForbiddenException('旧密码验证失败'); - } + // const oldPassword = this.utils.md5(userPasswordDto.oldPassword); + // if (oldPassword !== user.password) { + // throw new ForbiddenException('旧密码验证失败'); + // } await this.userRepository.update(userId, { password: this.utils.md5(userPasswordDto.newPassword), passwordVersion: user.passwordVersion + 1, From 3a674bb5891c790c597b428ccd38ef70d5b9d6de Mon Sep 17 00:00:00 2001 From: scarqin <1054139596@qq.com> Date: Tue, 27 Dec 2022 11:03:20 +0800 Subject: [PATCH 24/27] feat: projectExport parse to tree --- .env | 2 +- .env.development | 2 +- .../workspace/project/dto/export.dto.ts | 17 ++------- .../workspace/project/dto/import.dto.ts | 8 ++-- .../workspace/project/project.controller.ts | 17 +-------- .../workspace/project/project.service.ts | 38 ++++++++----------- 6 files changed, 26 insertions(+), 58 deletions(-) diff --git a/.env b/.env index 7902025..fbff061 100644 --- a/.env +++ b/.env @@ -5,7 +5,7 @@ EOAPI_API_PREFIX=/api # mysql configure TZ=Asia/Shanghai MYSQL_HOST=mysql -MYSQL_PORT=33066 +MYSQL_PORT=3306 MYSQL_USERNAME=root MYSQL_DATABASE=eoapi MYSQL_PASSWORD=123456a. diff --git a/.env.development b/.env.development index 4f2fa07..2d0e27c 100644 --- a/.env.development +++ b/.env.development @@ -1,2 +1,2 @@ MYSQL_HOST=localhost -MYSQL_PORT=33066 \ No newline at end of file +MYSQL_PORT=3306 \ No newline at end of file diff --git a/src/modules/workspace/project/dto/export.dto.ts b/src/modules/workspace/project/dto/export.dto.ts index ec98b64..6d0075b 100644 --- a/src/modules/workspace/project/dto/export.dto.ts +++ b/src/modules/workspace/project/dto/export.dto.ts @@ -1,17 +1,6 @@ -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; -} +import { Child, Environment } from './import.dto'; export class ExportProjectResultDto { - environment: Environment[]; - group: ApiGroup[]; - project: Project; - apiData: ApiData[]; + collections: Child[]; + environments: Environment[]; } diff --git a/src/modules/workspace/project/dto/import.dto.ts b/src/modules/workspace/project/dto/import.dto.ts index 181bf96..99abf80 100644 --- a/src/modules/workspace/project/dto/import.dto.ts +++ b/src/modules/workspace/project/dto/import.dto.ts @@ -1,7 +1,7 @@ export class ImportDto { groupID: number; collections: Child[]; - enviroments: Environment[]; + environments: Environment[]; } export type Child = @@ -30,7 +30,7 @@ export type Environment = { export type Collections = { collections: Child[]; - enviroments: Environment[]; + environments: Environment[]; }; export interface ParamsEnum { @@ -290,11 +290,11 @@ export type ImportResult = { errors: { apiData: any[]; group: any[]; - enviroments: any[]; + environments: any[]; }; successes: { apiData: any[]; group: any[]; - enviroments: any[]; + environments: any[]; }; }; diff --git a/src/modules/workspace/project/project.controller.ts b/src/modules/workspace/project/project.controller.ts index 31df178..120e13f 100644 --- a/src/modules/workspace/project/project.controller.ts +++ b/src/modules/workspace/project/project.controller.ts @@ -23,10 +23,7 @@ import { ApiOkResponseData, } from '@/common/class/res.class'; import { Project } from '@/entities/project.entity'; -import { - ExportProjectResultDto, - ExportCollectionsResultDto, -} from '@/modules/workspace/project/dto/export.dto'; +import { ExportProjectResultDto } from '@/modules/workspace/project/dto/export.dto'; import { RolesGuard } from '@/guards'; import { IUser, User } from '@/common/decorators/user.decorator'; import { @@ -121,21 +118,11 @@ export class ProjectController { return this.service.import(workspaceID, Number(projectID), importDto); } - @ApiOkResponseData(ExportCollectionsResultDto) - @Get(':projectID/export/collections') - async export( - @Param('projectID') projectID: string, - @Param('workspaceID', ParseIntPipe) workspaceID, - ) { - // console.log('projectID', projectID, importDto); - return this.service.exportCollections(workspaceID, Number(projectID)); - } - @ApiOkResponseData(ExportProjectResultDto) @Get(':projectID/export') async projectExport(@Param('projectID') projectID: string) { // console.log('projectID', projectID, importDto); - return this.service.projectExport(Number(projectID)); + return this.service.exportCollections(Number(projectID)); } @ApiOkResponseData(CollectionsDto) diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index bbc095d..a62e620 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -12,6 +12,7 @@ import { Child, Environment, ImportDto, ImportResult } from './dto/import.dto'; import { parseAndCheckApiData, parseAndCheckEnv } from './validate'; import { Project } from '@/entities/project.entity'; import { WorkspaceEntity } from '@/entities/workspace.entity'; +import packageJSON from 'package.json'; import { WorkspaceMemberAddDto, WorkspaceMemberRemoveDto, @@ -262,20 +263,20 @@ export class ProjectService implements OnModuleInit { } const project = await this.findOne(workspaceID, uuid); if (project) { - const { collections, enviroments } = importDto; + const { collections, environments } = importDto; const data = { errors: { apiData: [], group: [], - enviroments: [], + environments: [], }, successes: { apiData: [], group: [], - enviroments: [], + environments: [], }, }; - this.importEnv(enviroments, uuid, data); + this.importEnv(environments, uuid, data); return this.importCollects(collections, uuid, importDto.groupID, data); } return '导入失败,项目不存在'; @@ -298,41 +299,43 @@ export class ProjectService implements OnModuleInit { .concat(apiDataFilters); } - async exportCollections(workspaceID: number, uuid: number) { - const project = await this.findOne(workspaceID, uuid); + async exportCollections(uuid: number) { + const project = await this.findOneBy(uuid); if (project) { const apiData = await this.apiDataService.findAll({ projectID: uuid }); const apiGroup = await this.apiGroupService.findAll({ projectID: uuid }); - const enviroments = await this.environmentService.findAll({ + const environments = await this.environmentService.findAll({ where: { projectID: uuid, }, }); return { + version: packageJSON.version, + project: project, collections: this.exportCollects(apiGroup, apiData), - enviroments, + environments, }; } return '导出失败,项目不存在'; } async importEnv( - enviroments: Environment[] = [], + environments: Environment[] = [], projectID: number, importResult: ImportResult, ) { - const promiseTask = enviroments.map(async (item) => { + const promiseTask = environments.map(async (item) => { const env = { ...item, parameters: item.parameters as unknown as string, }; const result = parseAndCheckEnv(env); if (!result.validate) { - importResult.errors.enviroments.push(result); + importResult.errors.environments.push(result); } else { result.data.projectID = projectID; const env = await this.environmentService.create(result.data); - importResult.successes.enviroments.push({ + importResult.successes.environments.push({ name: env.name, uuid: env.uuid, }); @@ -418,15 +421,4 @@ export class ProjectService implements OnModuleInit { apis, }; } - - async projectExport(projectID: number) { - return { - environment: await this.environmentService.findAll({ - where: { projectID }, - }), - group: await this.apiGroupService.findAll({ projectID }), - project: await this.repository.findOne({ where: { uuid: projectID } }), - apiData: await this.apiDataService.findAll({ projectID }), - }; - } } From 861237f0fd0381002995b06a3167422be19ec91c Mon Sep 17 00:00:00 2001 From: scarqin <1054139596@qq.com> Date: Tue, 27 Dec 2022 11:07:46 +0800 Subject: [PATCH 25/27] feat: errors to error --- src/modules/workspace/project/dto/import.dto.ts | 2 +- src/modules/workspace/project/project.service.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/workspace/project/dto/import.dto.ts b/src/modules/workspace/project/dto/import.dto.ts index 99abf80..e73e7fd 100644 --- a/src/modules/workspace/project/dto/import.dto.ts +++ b/src/modules/workspace/project/dto/import.dto.ts @@ -287,7 +287,7 @@ export interface ApiData { } export type ImportResult = { - errors: { + error: { apiData: any[]; group: any[]; environments: any[]; diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index a62e620..eea8619 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -265,7 +265,7 @@ export class ProjectService implements OnModuleInit { if (project) { const { collections, environments } = importDto; const data = { - errors: { + error: { apiData: [], group: [], environments: [], @@ -331,7 +331,7 @@ export class ProjectService implements OnModuleInit { }; const result = parseAndCheckEnv(env); if (!result.validate) { - importResult.errors.environments.push(result); + importResult.error.environments.push(result); } else { result.data.projectID = projectID; const env = await this.environmentService.create(result.data); From 971952f4012b3d6111a66205a0a23d150f06c182 Mon Sep 17 00:00:00 2001 From: scarqin <1054139596@qq.com> Date: Wed, 28 Dec 2022 20:56:33 +0800 Subject: [PATCH 26/27] feat: add workspace cancel auto create project --- src/modules/workspace/project/project.controller.ts | 6 +++--- src/modules/workspace/project/project.service.ts | 4 ++-- src/modules/workspace/workspace.controller.ts | 9 --------- src/modules/workspace/workspace.service.ts | 10 ---------- 4 files changed, 5 insertions(+), 24 deletions(-) diff --git a/src/modules/workspace/project/project.controller.ts b/src/modules/workspace/project/project.controller.ts index 120e13f..c570319 100644 --- a/src/modules/workspace/project/project.controller.ts +++ b/src/modules/workspace/project/project.controller.ts @@ -51,7 +51,7 @@ export class ProjectController { @Body() createDto: CreateDto, ) { const data = await this.service.create(createDto, workspaceID, user.userId); - return this.findOne(workspaceID, `${data.uuid}`); + return await this.findOne(data.uuid, workspaceID); } @ApiCreatedResponseData() @@ -76,7 +76,7 @@ export class ProjectController { @Param('projectID', ParseIntPipe) projectID, @Param('workspaceID', ParseIntPipe) workspaceID, ) { - return this.service.findOne(workspaceID, projectID); + return this.service.findOne(projectID, workspaceID); } @Permissions(PermissionEnum.UPDATE_PROJECT) @@ -89,7 +89,7 @@ export class ProjectController { ) { const data = await this.service.update(+projectID, updateDto); if (data) { - return await this.findOne(workspaceID, projectID); + return await this.findOne(projectID, workspaceID); } return new NotFoundException('更新失败!项目不存在'); diff --git a/src/modules/workspace/project/project.service.ts b/src/modules/workspace/project/project.service.ts index eea8619..cc2e1cd 100644 --- a/src/modules/workspace/project/project.service.ts +++ b/src/modules/workspace/project/project.service.ts @@ -239,7 +239,7 @@ export class ProjectService implements OnModuleInit { }); } - async findOne(workspaceID: number, uuid: number): Promise { + async findOne(uuid: number, workspaceID: number): Promise { return await this.repository.findOne({ where: { uuid: Number(uuid), workspace: { id: workspaceID } }, }); @@ -261,7 +261,7 @@ export class ProjectService implements OnModuleInit { if (!group) { return `导入失败,id为${importDto.groupID}的分组不存在`; } - const project = await this.findOne(workspaceID, uuid); + const project = await this.findOne(uuid, workspaceID); if (project) { const { collections, environments } = importDto; const data = { diff --git a/src/modules/workspace/workspace.controller.ts b/src/modules/workspace/workspace.controller.ts index 395d311..fb83651 100644 --- a/src/modules/workspace/workspace.controller.ts +++ b/src/modules/workspace/workspace.controller.ts @@ -34,7 +34,6 @@ import { IUser, User } from '@/common/decorators/user.decorator'; import { UserEntity } from '@/entities/user.entity'; import { ImportDto } 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, @@ -68,14 +67,6 @@ export class WorkspaceController { user.userId, createDto, ); - const project = workspace.projects.at(0); - this.apiDataService.batchCreate( - sampleApiData.map((item) => { - Reflect.deleteProperty(item, 'uuid'); - Reflect.deleteProperty(item, 'uniqueID'); - return { ...item, projectID: project.uuid, project }; - }), - ); return workspace; } diff --git a/src/modules/workspace/workspace.service.ts b/src/modules/workspace/workspace.service.ts index b67999f..246c53b 100644 --- a/src/modules/workspace/workspace.service.ts +++ b/src/modules/workspace/workspace.service.ts @@ -17,7 +17,6 @@ import { import { PermissionEntity } from '@/entities/permission.entity'; import { WorkspaceEntity } from '@/entities/workspace.entity'; import { UserService } from '@/modules/user/user.service'; -import { ProjectService } from '@/modules/workspace/project/project.service'; import { Project } from '@/entities/project.entity'; import { CreateDto as ProjectCreateDto } from '@/modules/workspace/project/dto/create.dto'; import { WorkspaceUserRoleEntity } from '@/entities/workspace-user-role.entity'; @@ -43,7 +42,6 @@ export class WorkspaceService implements OnModuleInit { private permissionRepo: Repository, @InjectRepository(RolePermissionEntity) private rolePermissionRepo: Repository, - private projectService: ProjectService, ) {} onModuleInit() { @@ -68,24 +66,16 @@ export class WorkspaceService implements OnModuleInit { }, }); - project ??= await this.projectService.save({ - name: 'My project', - description: createWorkspaceDto.title + 'My project', - }); - // this.projectUserRoleRepo.save({ // projectID: project.uuid, // userID: creator.id, // roleID: RoleEnum.ProjectOwnerRoleID, // }) - creator.projects = (creator?.projects || []).concat(project); - const workspace = await this.workspaceRepository.save({ ...createWorkspaceDto, creatorID, users: [await this.userService.updateUser(creator)], - projects: [project], }); this.workspaceUserRoleRepo.save({ workspaceID: workspace.id, From b1077ae9c9e1084f806c4c1b7f8c854eb592cd64 Mon Sep 17 00:00:00 2001 From: scarqin <1054139596@qq.com> Date: Fri, 30 Dec 2022 17:53:03 +0800 Subject: [PATCH 27/27] docs: update README --- README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index e36191f..172107a 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@ +此仓库已归档 + # eoapi-remote-server Eoapi 远程数据源后端服务,部署后即可通过公共数据源实现简单团队协作。 -如果你的数据不需要共享,也可以仅下载客户端单机使用。 +如果你的数据不需要共享,也可以仅下载 Eoapi 客户端单机使用。 ## 部署 -请访问 [部署文档](https://docs.eoapi.io/docs/storage.html) + +请访问 [部署文档](https://github.com/eolinker/eoapi-remote-server/wiki/%E4%BA%91%E7%AB%AF%E6%9C%8D%E5%8A%A1) + ## 开发 + Node.js 版本:^16 如果想提高开发效率,可以安装 NestJS 官方提供的命令行 nestjs/cli 快速生成组件、服务等模板。 @@ -24,10 +29,11 @@ docker-compose run -d --service-ports mysql 1. 安装依赖 ```bash -yarn +yarn ``` 2. 运行数据库迁移脚本 + ```bash yarn migration:run ``` @@ -40,8 +46,8 @@ yarn start:dev ### 运行 -| 命令 | 描述 | -| --------------- | ---------- | +| 命令 | 描述 | +| ------------------- | ---------- | | `npm run start:dev` | 运行服务器 | ### 更新数据库