From 6eae2ed93cfb4babe2da6a83d4fc15580cbb88d0 Mon Sep 17 00:00:00 2001 From: devleejb Date: Wed, 17 Jan 2024 18:37:42 +0900 Subject: [PATCH 1/2] Implement workspace creation logic --- backend/package-lock.json | 35 +++++++++++++++++++ backend/package.json | 2 ++ backend/src/app.controller.spec.ts | 22 ------------ backend/src/app.controller.ts | 12 ------- backend/src/app.module.ts | 8 ++--- backend/src/app.service.ts | 8 ----- backend/src/auth/auth.controller.ts | 11 ++++-- backend/src/auth/github.strategy.ts | 2 +- backend/src/auth/interfaces/LoginResponse.ts | 3 -- backend/src/auth/jwt.strategy.ts | 3 +- .../login-request.type.ts} | 0 backend/src/auth/types/login-response.type.ts | 6 ++++ backend/src/main.ts | 21 +++++++---- backend/src/utils/types/req.type.ts | 6 ++++ .../src/workspaces/dto/CreateWorkspace.dto.ts | 6 ++++ .../types/create-workspace-response.type.ts | 12 +++++++ .../workspaces/workspaces.controller.spec.ts | 18 ++++++++++ .../src/workspaces/workspaces.controller.ts | 27 ++++++++++++++ backend/src/workspaces/workspaces.module.ts | 10 ++++++ .../src/workspaces/workspaces.service.spec.ts | 18 ++++++++++ backend/src/workspaces/workspaces.service.ts | 25 +++++++++++++ 21 files changed, 194 insertions(+), 61 deletions(-) delete mode 100644 backend/src/app.controller.spec.ts delete mode 100644 backend/src/app.controller.ts delete mode 100644 backend/src/app.service.ts delete mode 100644 backend/src/auth/interfaces/LoginResponse.ts rename backend/src/auth/{interfaces/LoginRequest.ts => types/login-request.type.ts} (100%) create mode 100644 backend/src/auth/types/login-response.type.ts create mode 100644 backend/src/utils/types/req.type.ts create mode 100644 backend/src/workspaces/dto/CreateWorkspace.dto.ts create mode 100644 backend/src/workspaces/types/create-workspace-response.type.ts create mode 100644 backend/src/workspaces/workspaces.controller.spec.ts create mode 100644 backend/src/workspaces/workspaces.controller.ts create mode 100644 backend/src/workspaces/workspaces.module.ts create mode 100644 backend/src/workspaces/workspaces.service.spec.ts create mode 100644 backend/src/workspaces/workspaces.service.ts diff --git a/backend/package-lock.json b/backend/package-lock.json index 0f2da728..6deda2ea 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -17,6 +17,8 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.1.17", "@prisma/client": "^5.8.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "passport-github": "^1.1.0", "passport-jwt": "^4.0.1", "reflect-metadata": "^0.1.13", @@ -2390,6 +2392,11 @@ "@types/superagent": "^8.1.0" } }, + "node_modules/@types/validator": { + "version": "13.11.8", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.8.tgz", + "integrity": "sha512-c/hzNDBh7eRF+KbCf+OoZxKbnkpaK/cKp9iLQWqB7muXtM+MtL9SUUH8vCFcLn6dH1Qm05jiexK0ofWY7TfOhQ==" + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -3460,6 +3467,21 @@ "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, + "node_modules/class-validator": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -6346,6 +6368,11 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.10.53", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.53.tgz", + "integrity": "sha512-sDTnnqlWK4vH4AlDQuswz3n4Hx7bIQWTpIcScJX+Sp7St3LXHmfiax/ZFfyYxHmkdCvydOLSuvtAO/XpXiSySw==" + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -8819,6 +8846,14 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/backend/package.json b/backend/package.json index 8a7bf36e..506a2a41 100644 --- a/backend/package.json +++ b/backend/package.json @@ -29,6 +29,8 @@ "@nestjs/platform-express": "^10.0.0", "@nestjs/swagger": "^7.1.17", "@prisma/client": "^5.8.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "passport-github": "^1.1.0", "passport-jwt": "^4.0.1", "reflect-metadata": "^0.1.13", diff --git a/backend/src/app.controller.spec.ts b/backend/src/app.controller.spec.ts deleted file mode 100644 index 0250afec..00000000 --- a/backend/src/app.controller.spec.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Test, TestingModule } from "@nestjs/testing"; -import { AppController } from "./app.controller"; -import { AppService } from "./app.service"; - -describe("AppController", () => { - let appController: AppController; - - beforeEach(async () => { - const app: TestingModule = await Test.createTestingModule({ - controllers: [AppController], - providers: [AppService], - }).compile(); - - appController = app.get(AppController); - }); - - describe("root", () => { - it('should return "Hello World!"', () => { - expect(appController.getHello()).toBe("Hello World!"); - }); - }); -}); diff --git a/backend/src/app.controller.ts b/backend/src/app.controller.ts deleted file mode 100644 index 96072ca6..00000000 --- a/backend/src/app.controller.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Controller, Get } from "@nestjs/common"; -import { AppService } from "./app.service"; - -@Controller() -export class AppController { - constructor(private readonly appService: AppService) {} - - @Get() - getHello(): string { - return this.appService.getHello(); - } -} diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 9233e913..8e8dc6fd 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -1,18 +1,16 @@ import { Module } from "@nestjs/common"; -import { AppController } from "./app.controller"; -import { AppService } from "./app.service"; import { PrismaService } from "./db/prisma.service"; import { UsersModule } from "./users/users.module"; import { AuthModule } from "./auth/auth.module"; import { ConfigModule } from "@nestjs/config"; import { APP_GUARD } from "@nestjs/core/constants"; import { JwtAuthGuard } from "./auth/jwt.guard"; +import { WorkspacesModule } from "./workspaces/workspaces.module"; @Module({ - imports: [ConfigModule.forRoot({ isGlobal: true }), UsersModule, AuthModule], - controllers: [AppController], + imports: [ConfigModule.forRoot({ isGlobal: true }), UsersModule, AuthModule, WorkspacesModule], + controllers: [], providers: [ - AppService, PrismaService, { provide: APP_GUARD, diff --git a/backend/src/app.service.ts b/backend/src/app.service.ts deleted file mode 100644 index b65d3467..00000000 --- a/backend/src/app.service.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { Injectable } from "@nestjs/common"; - -@Injectable() -export class AppService { - getHello(): string { - return "Hello World!"; - } -} diff --git a/backend/src/auth/auth.controller.ts b/backend/src/auth/auth.controller.ts index b869de77..17f1dc7e 100644 --- a/backend/src/auth/auth.controller.ts +++ b/backend/src/auth/auth.controller.ts @@ -1,11 +1,13 @@ import { Controller, Get, Req, UseGuards } from "@nestjs/common"; import { AuthGuard } from "@nestjs/passport"; -import { LoginRequest } from "./interfaces/LoginRequest"; +import { LoginRequest } from "./types/login-request.type"; import { JwtService } from "@nestjs/jwt"; -import { LoginResponse } from "./interfaces/LoginResponse"; +import { LoginResponse } from "./types/login-response.type"; import { UsersService } from "src/users/users.service"; import { Public } from "src/utils/decorators/auth.decorator"; +import { ApiOperation, ApiResponse, ApiTags } from "@nestjs/swagger"; +@ApiTags("Auth") @Controller("auth") export class AuthController { constructor( @@ -17,6 +19,11 @@ export class AuthController { @Get("login/github") @Get("callback/github") @UseGuards(AuthGuard("github")) + @ApiOperation({ + summary: "SignUp/LogIn with GitHub", + description: "SignUp/Login with GitHub social login", + }) + @ApiResponse({ type: LoginResponse }) async login(@Req() req: LoginRequest): Promise { const user = await this.usersService.findOrCreate( req.user.socialProvider, diff --git a/backend/src/auth/github.strategy.ts b/backend/src/auth/github.strategy.ts index 32102f96..e052c56e 100644 --- a/backend/src/auth/github.strategy.ts +++ b/backend/src/auth/github.strategy.ts @@ -2,7 +2,7 @@ import { Injectable } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { PassportStrategy } from "@nestjs/passport"; import { Profile, Strategy } from "passport-github"; -import { LoginUserInfo } from "./interfaces/LoginRequest"; +import { LoginUserInfo } from "./types/login-request.type"; @Injectable() export class GithubStrategy extends PassportStrategy(Strategy, "github") { diff --git a/backend/src/auth/interfaces/LoginResponse.ts b/backend/src/auth/interfaces/LoginResponse.ts deleted file mode 100644 index 80a52ff4..00000000 --- a/backend/src/auth/interfaces/LoginResponse.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface LoginResponse { - accessToken: string; -} diff --git a/backend/src/auth/jwt.strategy.ts b/backend/src/auth/jwt.strategy.ts index af88cda8..60dfc31d 100644 --- a/backend/src/auth/jwt.strategy.ts +++ b/backend/src/auth/jwt.strategy.ts @@ -3,6 +3,7 @@ import { ConfigService } from "@nestjs/config"; import { Injectable } from "@nestjs/common"; import { PassportStrategy } from "@nestjs/passport"; import { JwtPayload } from "src/utils/types/jwt.type"; +import { AuthorizedUser } from "src/utils/types/req.type"; @Injectable() export class JwtStrategy extends PassportStrategy(PassportJwtStrategy) { @@ -14,7 +15,7 @@ export class JwtStrategy extends PassportStrategy(PassportJwtStrategy) { }); } - async validate(payload: JwtPayload) { + async validate(payload: JwtPayload): Promise { return { id: payload.sub, nickname: payload.nickname }; } } diff --git a/backend/src/auth/interfaces/LoginRequest.ts b/backend/src/auth/types/login-request.type.ts similarity index 100% rename from backend/src/auth/interfaces/LoginRequest.ts rename to backend/src/auth/types/login-request.type.ts diff --git a/backend/src/auth/types/login-response.type.ts b/backend/src/auth/types/login-response.type.ts new file mode 100644 index 00000000..99542f23 --- /dev/null +++ b/backend/src/auth/types/login-response.type.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class LoginResponse { + @ApiProperty({ type: String, description: "Access token for CodePair" }) + accessToken: string; +} diff --git a/backend/src/main.ts b/backend/src/main.ts index 77a326ff..0d30fb5e 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -1,19 +1,26 @@ import { NestFactory } from "@nestjs/core"; import { AppModule } from "./app.module"; import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger"; +import { ValidationPipe } from "@nestjs/common"; async function bootstrap() { const app = await NestFactory.create(AppModule); - const config = new DocumentBuilder() - .setTitle("CodePair") - .setDescription("The CodePair API description") - .setVersion("1.0") - .addTag("codepair") - .build(); - const document = SwaggerModule.createDocument(app, config); + // Swagger + const document = SwaggerModule.createDocument( + app, + new DocumentBuilder() + .setTitle("CodePair") + .setDescription("The CodePair API description") + .setVersion("1.0") + .addBearerAuth() + .build() + ); SwaggerModule.setup("api", app, document); + // Auto Validation + app.useGlobalPipes(new ValidationPipe()); + await app.listen(3000); } bootstrap(); diff --git a/backend/src/utils/types/req.type.ts b/backend/src/utils/types/req.type.ts new file mode 100644 index 00000000..1ca157a6 --- /dev/null +++ b/backend/src/utils/types/req.type.ts @@ -0,0 +1,6 @@ +export type AuthorizedUser = { + id: string; + nickname: string; +}; + +export type AuthroizedRequest = Request & { user: AuthorizedUser }; diff --git a/backend/src/workspaces/dto/CreateWorkspace.dto.ts b/backend/src/workspaces/dto/CreateWorkspace.dto.ts new file mode 100644 index 00000000..2397bf18 --- /dev/null +++ b/backend/src/workspaces/dto/CreateWorkspace.dto.ts @@ -0,0 +1,6 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class CreateWorkspaceDto { + @ApiProperty({ description: "Title of project to create", type: String }) + title: string; +} diff --git a/backend/src/workspaces/types/create-workspace-response.type.ts b/backend/src/workspaces/types/create-workspace-response.type.ts new file mode 100644 index 00000000..f462cc9e --- /dev/null +++ b/backend/src/workspaces/types/create-workspace-response.type.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from "@nestjs/swagger"; + +export class CreateWorkspaceResponse { + @ApiProperty({ type: String, description: "ID of new workspace" }) + id: string; + @ApiProperty({ type: String, description: "Title of new workspace" }) + title: string; + @ApiProperty({ type: Date, description: "Created date of new workspace" }) + createdAt: Date; + @ApiProperty({ type: Date, description: "Updated date of new workspace" }) + updatedAt: Date; +} diff --git a/backend/src/workspaces/workspaces.controller.spec.ts b/backend/src/workspaces/workspaces.controller.spec.ts new file mode 100644 index 00000000..1f851234 --- /dev/null +++ b/backend/src/workspaces/workspaces.controller.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { WorkspacesController } from "./workspaces.controller"; + +describe("WorkspacesController", () => { + let controller: WorkspacesController; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [WorkspacesController], + }).compile(); + + controller = module.get(WorkspacesController); + }); + + it("should be defined", () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/backend/src/workspaces/workspaces.controller.ts b/backend/src/workspaces/workspaces.controller.ts new file mode 100644 index 00000000..8cc43d94 --- /dev/null +++ b/backend/src/workspaces/workspaces.controller.ts @@ -0,0 +1,27 @@ +import { Body, Controller, Post, Req } from "@nestjs/common"; +import { WorkspacesService } from "./workspaces.service"; +import { CreateWorkspaceDto } from "./dto/CreateWorkspace.dto"; +import { ApiBearerAuth, ApiBody, ApiCreatedResponse, ApiOperation, ApiTags } from "@nestjs/swagger"; +import { AuthroizedRequest } from "src/utils/types/req.type"; +import { CreateWorkspaceResponse } from "./types/create-workspace-response.type"; + +@ApiTags("Workspaces") +@ApiBearerAuth() +@Controller("workspaces") +export class WorkspacesController { + constructor(private workspacesService: WorkspacesService) {} + + @Post() + @ApiOperation({ + summary: "Create a Workspace", + description: "Create a workspace with the title.", + }) + @ApiBody({ type: CreateWorkspaceDto }) + @ApiCreatedResponse({ type: CreateWorkspaceResponse }) + async create( + @Req() req: AuthroizedRequest, + @Body() createWorkspaceDto: CreateWorkspaceDto + ): Promise { + return this.workspacesService.create(req.user.id, createWorkspaceDto.title); + } +} diff --git a/backend/src/workspaces/workspaces.module.ts b/backend/src/workspaces/workspaces.module.ts new file mode 100644 index 00000000..ed01ef3d --- /dev/null +++ b/backend/src/workspaces/workspaces.module.ts @@ -0,0 +1,10 @@ +import { Module } from "@nestjs/common"; +import { WorkspacesController } from "./workspaces.controller"; +import { WorkspacesService } from "./workspaces.service"; +import { PrismaService } from "src/db/prisma.service"; + +@Module({ + controllers: [WorkspacesController], + providers: [WorkspacesService, PrismaService], +}) +export class WorkspacesModule {} diff --git a/backend/src/workspaces/workspaces.service.spec.ts b/backend/src/workspaces/workspaces.service.spec.ts new file mode 100644 index 00000000..e05372cc --- /dev/null +++ b/backend/src/workspaces/workspaces.service.spec.ts @@ -0,0 +1,18 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { WorkspacesService } from "./workspaces.service"; + +describe("WorkspacesService", () => { + let service: WorkspacesService; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [WorkspacesService], + }).compile(); + + service = module.get(WorkspacesService); + }); + + it("should be defined", () => { + expect(service).toBeDefined(); + }); +}); diff --git a/backend/src/workspaces/workspaces.service.ts b/backend/src/workspaces/workspaces.service.ts new file mode 100644 index 00000000..2475d6f3 --- /dev/null +++ b/backend/src/workspaces/workspaces.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from "@nestjs/common"; +import { Workspace } from "@prisma/client"; +import { PrismaService } from "src/db/prisma.service"; + +@Injectable() +export class WorkspacesService { + constructor(private prismaService: PrismaService) {} + + async create(userId: string, title: string): Promise { + const workspace = await this.prismaService.workspace.create({ + data: { + title, + }, + }); + + await this.prismaService.userWorkspace.create({ + data: { + workspaceId: workspace.id, + userId, + }, + }); + + return workspace; + } +} From 20622efb42b9848945c22b53c4eccd055c0d3d79 Mon Sep 17 00:00:00 2001 From: devleejb Date: Wed, 17 Jan 2024 18:47:57 +0900 Subject: [PATCH 2/2] Add workspace role --- backend/package-lock.json | 128 +++++++++--------- backend/prisma/schema.prisma | 1 + .../src/utils/types/workspace-role.type.ts | 1 + backend/src/workspaces/workspaces.service.ts | 1 + 4 files changed, 67 insertions(+), 64 deletions(-) create mode 100644 backend/src/utils/types/workspace-role.type.ts diff --git a/backend/package-lock.json b/backend/package-lock.json index 6deda2ea..77ce0f52 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1886,15 +1886,15 @@ } }, "node_modules/@nestjs/swagger": { - "version": "7.1.17", - "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.1.17.tgz", - "integrity": "sha512-ASCxBrvMEN2o/8vEEmrIPMNzrr/hVi7QIR4y1oNYvoBNXHuwoF1VSI3+4Rq/3xmwVnVveJxHlBIs2u5xY9VgGQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-7.2.0.tgz", + "integrity": "sha512-W7WPq561/79w27ZEgViXS7c5hqPwT7QXhsLsSeu2jeBROUhMM825QKDFKbMmtb643IW5dznJ4PjherlZZgtMvg==", "dependencies": { "@nestjs/mapped-types": "2.0.4", "js-yaml": "4.1.0", "lodash": "4.17.21", "path-to-regexp": "3.2.0", - "swagger-ui-dist": "5.10.3" + "swagger-ui-dist": "5.11.0" }, "peerDependencies": { "@fastify/static": "^6.0.0", @@ -2006,9 +2006,9 @@ } }, "node_modules/@pkgr/core": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.0.tgz", - "integrity": "sha512-Zwq5OCzuwJC2jwqmpEQt7Ds1DTi6BWSwoGkbb1n9pO3hzb35BoJELx7c0T23iDkBGkh2e7tvOtjF3tr3OaQHDQ==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", "dev": true, "engines": { "node": "^12.20.0 || ^14.18.0 || >=16.0.0" @@ -2319,9 +2319,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.1.tgz", - "integrity": "sha512-DsXojJUES2M+FE8CpptJTKpg+r54moV9ZEncPstni1WHFmTcCzeFLnMFfyhCVS8XNOy/OQG+8lVxRLRrVHmV5A==", + "version": "20.11.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.5.tgz", + "integrity": "sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==", "dependencies": { "undici-types": "~5.26.4" } @@ -2413,16 +2413,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.1.tgz", - "integrity": "sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.19.0.tgz", + "integrity": "sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.18.1", - "@typescript-eslint/type-utils": "6.18.1", - "@typescript-eslint/utils": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/type-utils": "6.19.0", + "@typescript-eslint/utils": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -2448,15 +2448,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.1.tgz", - "integrity": "sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.19.0.tgz", + "integrity": "sha512-1DyBLG5SH7PYCd00QlroiW60YJ4rWMuUGa/JBV0iZuqi4l4IK3twKPq5ZkEebmGqRjXWVgsUzfd3+nZveewgow==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.18.1", - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/typescript-estree": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4" }, "engines": { @@ -2476,13 +2476,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz", - "integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", + "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1" + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2493,13 +2493,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.1.tgz", - "integrity": "sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.0.tgz", + "integrity": "sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.18.1", - "@typescript-eslint/utils": "6.18.1", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/utils": "6.19.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -2520,9 +2520,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz", - "integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2533,13 +2533,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz", - "integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", + "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/visitor-keys": "6.18.1", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2561,17 +2561,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz", - "integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz", + "integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.18.1", - "@typescript-eslint/types": "6.18.1", - "@typescript-eslint/typescript-estree": "6.18.1", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", "semver": "^7.5.4" }, "engines": { @@ -2586,12 +2586,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.18.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz", - "integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.1", + "@typescript-eslint/types": "6.19.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -3361,9 +3361,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001576", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz", - "integrity": "sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==", + "version": "1.0.30001578", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001578.tgz", + "integrity": "sha512-J/jkFgsQ3NEl4w2lCoM9ZPxrD+FoBNJ7uJUpGVjIg/j0OwJosWM36EPDv+Yyi0V4twBk9pPmlFS+PLykgEvUmg==", "dev": true, "funding": [ { @@ -3998,9 +3998,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.4.630", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.630.tgz", - "integrity": "sha512-osHqhtjojpCsACVnuD11xO5g9xaCyw7Qqn/C2KParkMv42i8jrJJgx3g7mkHfpxwhy9MnOJr8+pKOdZ7qzgizg==", + "version": "1.4.634", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.634.tgz", + "integrity": "sha512-gQNahJfF5AE4MZo+pMSwmnwkzVZ+F4ZGGj4Z/MMddOXVQM0y9OHy6ts3W9SDzAJaiZM3p6eixn5ABCQ+AfXzcQ==", "dev": true }, "node_modules/emittery": { @@ -7227,9 +7227,9 @@ } }, "node_modules/prettier": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.2.tgz", - "integrity": "sha512-HTByuKZzw7utPiDO523Tt2pLtEyK7OibUD9suEJQrPUCYQqrHr74GGX6VidMrovbf/I50mPqr8j/II6oBAuc5A==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.3.tgz", + "integrity": "sha512-QNhUTBq+mqt1oH1dTfY3phOKNhcDdJkfttHI6u0kj7M2+c+7fmNKlgh2GhnHiqMcbxJ+a0j2igz/2jfl9QKLuw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -8251,9 +8251,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.10.3", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.10.3.tgz", - "integrity": "sha512-fu3aozjxFWsmcO1vyt1q1Ji2kN7KlTd1vHy27E9WgPyXo9nrEzhQPqgxaAjbMsOmb8XFKNGo4Sa3Q+84Fh+pFw==" + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.0.tgz", + "integrity": "sha512-j0PIATqQSEFGOLmiJOJZj1X1Jt6bFIur3JpY7+ghliUnfZs0fpWDdHEkn9q7QUlBtKbkn6TepvSxTqnE8l3s0A==" }, "node_modules/symbol-observable": { "version": "4.0.0", diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma index e5e018fd..bcefcb5c 100644 --- a/backend/prisma/schema.prisma +++ b/backend/prisma/schema.prisma @@ -26,6 +26,7 @@ model UserWorkspace { id String @id @default(auto()) @map("_id") @db.ObjectId user User @relation(fields: [userId], references: [id]) userId String @map("user_id") @db.ObjectId + role String workspace Workspace @relation(fields: [workspaceId], references: [id]) workspaceId String @map("workspace_id") @db.ObjectId createdAt DateTime @default(now()) @map("created_at") diff --git a/backend/src/utils/types/workspace-role.type.ts b/backend/src/utils/types/workspace-role.type.ts new file mode 100644 index 00000000..381594c1 --- /dev/null +++ b/backend/src/utils/types/workspace-role.type.ts @@ -0,0 +1 @@ +export type WorkspaceRole = "OWNER" | "MEMBER"; diff --git a/backend/src/workspaces/workspaces.service.ts b/backend/src/workspaces/workspaces.service.ts index 2475d6f3..913f2851 100644 --- a/backend/src/workspaces/workspaces.service.ts +++ b/backend/src/workspaces/workspaces.service.ts @@ -17,6 +17,7 @@ export class WorkspacesService { data: { workspaceId: workspace.id, userId, + role: "OWNER", }, });