diff --git a/backend-pg/src/@types/fastify.d.ts b/backend-pg/src/@types/fastify.d.ts index da1fe1f6b..1d8c2c2c8 100644 --- a/backend-pg/src/@types/fastify.d.ts +++ b/backend-pg/src/@types/fastify.d.ts @@ -10,6 +10,11 @@ import { AuthMode } from "@app/services/auth/auth-signup-type"; import { ActorType } from "@app/services/auth/auth-type"; import { TOrgRoleServiceFactory } from "@app/services/org/org-role-service"; import { TOrgServiceFactory } from "@app/services/org/org-service"; +import { TProjectServiceFactory } from "@app/services/project/project-service"; +import { TProjectEnvServiceFactory } from "@app/services/project-env/project-env-service"; +import { TProjectKeyServiceFactory } from "@app/services/project-key/project-key-service"; +import { TProjectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service"; +import { TProjectRoleServiceFactory } from "@app/services/project-role/project-role-service"; import { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin-service"; import { TAuthTokenServiceFactory } from "@app/services/token/token-service"; import { TUserDalFactory } from "@app/services/user/user-dal"; @@ -54,6 +59,11 @@ declare module "fastify" { superAdmin: TSuperAdminServiceFactory; user: TUserServiceFactory; apiKey: TApiKeyServiceFactory; + project: TProjectServiceFactory; + projectMembership: TProjectMembershipServiceFactory; + projectEnv: TProjectEnvServiceFactory; + projectKey: TProjectKeyServiceFactory; + projectRole: TProjectRoleServiceFactory; }; // this is exclusive use for middlewares in which we need to inject data diff --git a/backend-pg/src/ee/routes/v1/index.ts b/backend-pg/src/ee/routes/v1/index.ts index 83510891d..d4477f9fd 100644 --- a/backend-pg/src/ee/routes/v1/index.ts +++ b/backend-pg/src/ee/routes/v1/index.ts @@ -1,6 +1,8 @@ import { registerOrgRoleRouter } from "./org-role-router"; +import { registerProjectRoleRouter } from "./project-role-router"; export const registerV1EERoutes = async (server: FastifyZodProvider) => { // org role starts with organization await server.register(registerOrgRoleRouter, { prefix: "/organization" }); + await server.register(registerProjectRoleRouter, { prefix: "/workspace" }); }; diff --git a/backend-pg/src/ee/routes/v1/org-role-router.ts b/backend-pg/src/ee/routes/v1/org-role-router.ts index 65af3cebd..942e1318e 100644 --- a/backend-pg/src/ee/routes/v1/org-role-router.ts +++ b/backend-pg/src/ee/routes/v1/org-role-router.ts @@ -16,7 +16,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { slug: z.string().trim(), name: z.string().trim(), description: z.string().trim().optional(), - workspaceId: z.string().trim().optional(), permissions: z.any().array() }), response: { @@ -62,10 +61,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => { req.auth.userId, req.params.organizationId, req.params.roleId, - { - ...req.body, - permissions: req.body.permissions ? JSON.stringify(req.body.permissions) : undefined - } + req.body ); return { role }; } diff --git a/backend-pg/src/ee/routes/v1/project-role-router.ts b/backend-pg/src/ee/routes/v1/project-role-router.ts new file mode 100644 index 000000000..2acae5e42 --- /dev/null +++ b/backend-pg/src/ee/routes/v1/project-role-router.ts @@ -0,0 +1,149 @@ +import { z } from "zod"; + +import { ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas"; +import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; +import { AuthMode } from "@app/services/auth/auth-type"; + +export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { + server.route({ + method: "POST", + url: "/:projectId/roles", + schema: { + params: z.object({ + projectId: z.string().trim() + }), + body: z.object({ + slug: z.string().trim(), + name: z.string().trim(), + description: z.string().trim().optional(), + permissions: z.any().array() + }), + response: { + 200: z.object({ + role: ProjectRolesSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const role = await server.services.projectRole.createRole( + req.permission.type, + req.permission.id, + req.params.projectId, + req.body + ); + return { role }; + } + }); + + server.route({ + method: "PATCH", + url: "/:projectId/roles/:roleId", + schema: { + params: z.object({ + projectId: z.string().trim(), + roleId: z.string().trim() + }), + body: z.object({ + slug: z.string().trim().optional(), + name: z.string().trim().optional(), + description: z.string().trim().optional(), + permissions: z.any().array() + }), + response: { + 200: z.object({ + role: ProjectRolesSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const role = await server.services.projectRole.updateRole( + req.permission.type, + req.permission.id, + req.params.projectId, + req.params.roleId, + req.body + ); + return { role }; + } + }); + + server.route({ + method: "DELETE", + url: "/:projectId/roles/:roleId", + schema: { + params: z.object({ + projectId: z.string().trim(), + roleId: z.string().trim() + }), + response: { + 200: z.object({ + role: ProjectRolesSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const role = await server.services.projectRole.deleteRole( + req.permission.type, + req.permission.id, + req.params.projectId, + req.params.roleId + ); + return { role }; + } + }); + + server.route({ + method: "GET", + url: "/:projectId/roles", + schema: { + params: z.object({ + projectId: z.string().trim() + }), + response: { + 200: z.object({ + data: z.object({ + roles: ProjectRolesSchema.omit({ permissions: true }) + .merge(z.object({ permissions: z.unknown() })) + .array() + }) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const roles = await server.services.projectRole.listRoles( + req.permission.type, + req.permission.id, + req.params.projectId + ); + return { data: { roles } }; + } + }); + + server.route({ + method: "GET", + url: "/:projectId/permissions", + schema: { + params: z.object({ + projectId: z.string().trim() + }), + response: { + 200: z.object({ + membership: ProjectMembershipsSchema, + permissions: z.any().array() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const { permissions, membership } = await server.services.projectRole.getUserPermission( + req.auth.userId, + req.params.projectId + ); + return { permissions, membership }; + } + }); +}; diff --git a/backend-pg/src/ee/services/permission/permission-service.ts b/backend-pg/src/ee/services/permission/permission-service.ts index 713b2e1dc..2dcf70157 100644 --- a/backend-pg/src/ee/services/permission/permission-service.ts +++ b/backend-pg/src/ee/services/permission/permission-service.ts @@ -16,7 +16,9 @@ import { projectAdminPermissions, projectMemberPermissions, projectNoAccessPermissions, - projectViewerPermission} from "./project-permission"; + ProjectPermissionSet, + projectViewerPermission +} from "./project-permission"; type TPermissionServiceFactoryDep = { permissionDal: TPermissionDalFactory; @@ -68,9 +70,9 @@ export const permissionServiceFactory = ({ permissionDal }: TPermissionServiceFa if (membership.role === ProjectMembershipRole.NoAccess) return { permission: projectNoAccessPermissions, membership }; if (membership.role === ProjectMembershipRole.Custom) { - const permission = createMongoAbility( + const permission = createMongoAbility( // akhilmhdh: putting any due to ts incompatiable matching with string and the other - unpackRules>>(membership.permissions as any), + unpackRules>>(membership.permissions as any), { conditionsMatcher } diff --git a/backend-pg/src/server/routes/index.ts b/backend-pg/src/server/routes/index.ts index 96ff66062..402d85d8a 100644 --- a/backend-pg/src/server/routes/index.ts +++ b/backend-pg/src/server/routes/index.ts @@ -18,6 +18,16 @@ import { orgDalFactory } from "@app/services/org/org-dal"; import { orgRoleDalFactory } from "@app/services/org/org-role-dal"; import { orgRoleServiceFactory } from "@app/services/org/org-role-service"; import { orgServiceFactory } from "@app/services/org/org-service"; +import { projectDalFactory } from "@app/services/project/project-dal"; +import { projectServiceFactory } from "@app/services/project/project-service"; +import { projectEnvDalFactory } from "@app/services/project-env/project-env-dal"; +import { projectEnvServiceFactory } from "@app/services/project-env/project-env-service"; +import { projectKeyDalFactory } from "@app/services/project-key/project-key-dal"; +import { projectKeyServiceFactory } from "@app/services/project-key/project-key-service"; +import { projectMembershipDalFactory } from "@app/services/project-membership/project-membership-dal"; +import { projectMembershipServiceFactory } from "@app/services/project-membership/project-membership-service"; +import { projectRoleDalFactory } from "@app/services/project-role/project-role-dal"; +import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service"; import { TSmtpService } from "@app/services/smtp/smtp-service"; import { superAdminDalFactory } from "@app/services/super-admin/super-admin-dal"; import { superAdminServiceFactory } from "@app/services/super-admin/super-admin-service"; @@ -43,6 +53,12 @@ export const registerRoutes = async ( const superAdminDal = superAdminDalFactory(db); const apiKeyDal = apiKeyDalFactory(db); + const projectDal = projectDalFactory(db); + const projectMembershipDal = projectMembershipDalFactory(db); + const projectRoleDal = projectRoleDalFactory(db); + const projectEnvDal = projectEnvDalFactory(db); + const projectKeyDal = projectKeyDalFactory(db); + // ee db layer ops const permissionDal = permissionDalFactory(db); @@ -84,6 +100,30 @@ export const registerRoutes = async ( }); const apiKeyService = apiKeyServiceFactory({ apiKeyDal }); + const projectService = projectServiceFactory({ + permissionService, + projectDal, + projectEnvDal, + projectMembershipDal + }); + const projectMembershipService = projectMembershipServiceFactory({ + projectMembershipDal, + projectDal, + permissionService, + orgDal, + userDal, + smtpService, + projectKeyDal, + projectRoleDal + }); + const projectEnvService = projectEnvServiceFactory({ permissionService, projectEnvDal }); + const projectKeyService = projectKeyServiceFactory({ + permissionService, + projectKeyDal, + projectMembershipDal + }); + const projectRoleService = projectRoleServiceFactory({ permissionService, projectRoleDal }); + await superAdminService.initServerCfg(); // inject all services server.decorate("services", { @@ -96,7 +136,12 @@ export const registerRoutes = async ( orgRole: orgRoleService, apiKey: apiKeyService, authToken: tokenService, - superAdmin: superAdminService + superAdmin: superAdminService, + project: projectService, + projectMembership: projectMembershipService, + projectKey: projectKeyService, + projectEnv: projectEnvService, + projectRole: projectRoleService }); server.decorate("store", { diff --git a/backend-pg/src/server/routes/v1/index.ts b/backend-pg/src/server/routes/v1/index.ts index 12fa0d96a..5aadc47a7 100644 --- a/backend-pg/src/server/routes/v1/index.ts +++ b/backend-pg/src/server/routes/v1/index.ts @@ -3,6 +3,10 @@ import { registerAuthRoutes } from "./auth-router"; import { registerInviteOrgRouter } from "./invite-org-router"; import { registerOrgRouter } from "./organization-router"; import { registerPasswordRouter } from "./password-router"; +import { registerProjectEnvRouter } from "./project-env-router"; +import { registerProjectKeyRouter } from "./project-key-router"; +import { registerProjectMembershipRouter } from "./project-membership-router"; +import { registerProjectRouter } from "./project-router"; import { registerSsoRouter } from "./sso-router"; import { registerUserActionRouter } from "./user-action-router"; import { registerUserRouter } from "./user-router"; @@ -10,11 +14,20 @@ import { registerUserRouter } from "./user-router"; export const registerV1Routes = async (server: FastifyZodProvider) => { await server.register(registerSsoRouter, { prefix: "/sso" }); await server.register(registerAuthRoutes, { prefix: "/auth" }); - // await server.register(registerSSORouter, { prefix: "/sso" }); await server.register(registerPasswordRouter, { prefix: "/password" }); await server.register(registerOrgRouter, { prefix: "/organization" }); await server.register(registerAdminRouter, { prefix: "/admin" }); await server.register(registerUserRouter, { prefix: "/user" }); await server.register(registerInviteOrgRouter, { prefix: "/invite-org" }); await server.register(registerUserActionRouter, { prefix: "/user-action" }); + + await server.register( + async (projectServer) => { + await projectServer.register(registerProjectRouter); + await projectServer.register(registerProjectEnvRouter); + await projectServer.register(registerProjectKeyRouter); + await projectServer.register(registerProjectMembershipRouter); + }, + { prefix: "/workspace" } + ); }; diff --git a/backend-pg/src/server/routes/v1/project-env-router.ts b/backend-pg/src/server/routes/v1/project-env-router.ts new file mode 100644 index 000000000..0c413b537 --- /dev/null +++ b/backend-pg/src/server/routes/v1/project-env-router.ts @@ -0,0 +1,111 @@ +import { z } from "zod"; + +import { ProjectEnvironmentsSchema } from "@app/db/schemas"; +import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; +import { AuthMode } from "@app/services/auth/auth-type"; + +export const registerProjectEnvRouter = async (server: FastifyZodProvider) => { + server.route({ + url: "/:workspaceId/environments", + method: "POST", + schema: { + params: z.object({ + workspaceId: z.string().trim() + }), + body: z.object({ + name: z.string().trim(), + slug: z.string().trim() + }), + response: { + 200: z.object({ + message: z.string(), + workspace: z.string(), + environment: ProjectEnvironmentsSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + handler: async (req) => { + const environment = await server.services.projectEnv.createEnvironment({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId, + ...req.body + }); + return { + message: "Successfully created new environment", + workspace: req.params.workspaceId, + environment + }; + } + }); + + server.route({ + url: "/:workspaceId/environments/:id", + method: "PATCH", + schema: { + params: z.object({ + workspaceId: z.string().trim(), + id: z.string().trim() + }), + body: z.object({ + slug: z.string().trim().optional(), + name: z.string().trim().optional() + }), + response: { + 200: z.object({ + message: z.string(), + workspace: z.string(), + environment: ProjectEnvironmentsSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + handler: async (req) => { + const environment = await server.services.projectEnv.updateEnvironment({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId, + id: req.params.id, + ...req.body + }); + return { + message: "Successfully updated environment", + workspace: req.params.workspaceId, + environment + }; + } + }); + + server.route({ + url: "/:workspaceId/environments/:id", + method: "DELETE", + schema: { + params: z.object({ + workspaceId: z.string().trim(), + id: z.string().trim() + }), + response: { + 200: z.object({ + message: z.string(), + workspace: z.string(), + environment: ProjectEnvironmentsSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + handler: async (req) => { + const environment = await server.services.projectEnv.deleteEnvironment({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId, + id: req.params.id + }); + return { + message: "Successfully deleted environment", + workspace: req.params.workspaceId, + environment + }; + } + }); +}; diff --git a/backend-pg/src/server/routes/v1/project-key-router.ts b/backend-pg/src/server/routes/v1/project-key-router.ts new file mode 100644 index 000000000..482392947 --- /dev/null +++ b/backend-pg/src/server/routes/v1/project-key-router.ts @@ -0,0 +1,40 @@ +import { z } from "zod"; + +import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; +import { AuthMode } from "@app/services/auth/auth-type"; + +export const registerProjectKeyRouter = async (server: FastifyZodProvider) => { + server.route({ + url: "/:workspaceId/key", + method: "POST", + schema: { + params: z.object({ + workspaceId: z.string().trim() + }), + body: z.object({ + key: z.object({ + encryptedKey: z.string().trim(), + nonce: z.string().trim(), + userId: z.string().trim() + }) + }), + response: { + 200: z.object({ + message: z.string() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + await server.services.projectKey.uploadProjectKeys({ + projectId: req.params.workspaceId, + actor: req.permission.type, + actorId: req.permission.id, + nonce: req.body.key.nonce, + receiverId: req.body.key.userId, + encryptedKey: req.body.key.encryptedKey + }); + return { message: "Successfully uploaded key to workspace" }; + } + }); +}; diff --git a/backend-pg/src/server/routes/v1/project-membership-router.ts b/backend-pg/src/server/routes/v1/project-membership-router.ts new file mode 100644 index 000000000..c78281e69 --- /dev/null +++ b/backend-pg/src/server/routes/v1/project-membership-router.ts @@ -0,0 +1,100 @@ +import { z } from "zod"; + +import { ProjectMembershipsSchema, UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas"; +import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; +import { AuthMode } from "@app/services/auth/auth-type"; + +export const registerProjectMembershipRouter = async (server: FastifyZodProvider) => { + // TODO(akhilmhdh-pg): missing adding multiple user workspace refer v2/membership + + server.route({ + url: "/:workspaceId/memberships", + method: "GET", + schema: { + params: z.object({ + workspaceId: z.string().trim() + }), + response: { + 200: z.object({ + memberships: ProjectMembershipsSchema.merge( + z.object({ + user: UsersSchema.pick({ + email: true, + firstName: true, + lastName: true, + id: true + }).merge(UserEncryptionKeysSchema.pick({ publicKey: true })) + }) + ) + .omit({ createdAt: true, updatedAt: true }) + .array() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const memberships = await server.services.projectMembership.getProjectMemberships({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId + }); + return { memberships }; + } + }); + + server.route({ + url: "/:workspaceId/memberships/:membershipId", + method: "PATCH", + schema: { + params: z.object({ + workspaceId: z.string().trim(), + membershipId: z.string().trim() + }), + body: z.object({ + role: z.string().trim() + }), + response: { + 200: z.object({ + membership: ProjectMembershipsSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const membership = await server.services.projectMembership.updateProjectMembership({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId, + membershipId: req.params.membershipId, + role: req.body.role + }); + return { membership }; + } + }); + + server.route({ + url: "/:workspaceId/memberships/:membershipId", + method: "DELETE", + schema: { + params: z.object({ + workspaceId: z.string().trim(), + membershipId: z.string().trim() + }), + response: { + 200: z.object({ + membership: ProjectMembershipsSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const membership = await server.services.projectMembership.deleteProjectMembership({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId, + membershipId: req.params.membershipId + }); + return { membership }; + } + }); +}; diff --git a/backend-pg/src/server/routes/v1/project-router.ts b/backend-pg/src/server/routes/v1/project-router.ts new file mode 100644 index 000000000..6f47d2021 --- /dev/null +++ b/backend-pg/src/server/routes/v1/project-router.ts @@ -0,0 +1,265 @@ +import { z } from "zod"; + +import { + ProjectKeysSchema, + ProjectMembershipsSchema, + ProjectsSchema, + UserEncryptionKeysSchema, + UsersSchema +} from "@app/db/schemas"; +import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; +import { AuthMode } from "@app/services/auth/auth-type"; + +export const registerProjectRouter = (server: FastifyZodProvider) => { + server.route({ + url: "/:workspaceId/keys", + method: "GET", + schema: { + params: z.object({ + workspaceId: z.string().trim() + }), + response: { + 200: z.object({ + publicKeys: z + .object({ + publicKey: z.string().optional(), + userId: z.string() + }) + .array() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const publicKeys = await server.services.projectKey.getProjectPublicKeys({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId + }); + return { publicKeys }; + } + }); + + server.route({ + url: "/:workspaceId/users", + method: "GET", + schema: { + params: z.object({ + workspaceId: z.string().trim() + }), + response: { + 200: z.object({ + users: ProjectMembershipsSchema.merge( + z.object({ + user: UsersSchema.pick({ + email: true, + firstName: true, + lastName: true, + id: true + }).merge(UserEncryptionKeysSchema.pick({ publicKey: true })) + }) + ) + .omit({ createdAt: true, updatedAt: true }) + .array() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const users = await server.services.projectMembership.getProjectMemberships({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId + }); + return { users }; + } + }); + + server.route({ + url: "/", + method: "GET", + schema: { + response: { + 200: z.object({ + workspaces: ProjectsSchema.merge( + z.object({ environments: z.object({ name: z.string(), slug: z.string() }).array() }) + ).array() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + handler: async (req) => { + const workspaces = await server.services.project.getProjects(req.permission.id); + return { workspaces }; + } + }); + + server.route({ + url: "/:workspaceId", + method: "GET", + schema: { + params: z.object({ + workspaceId: z.string().trim() + }), + response: { + 200: z.object({ + workspace: ProjectsSchema.merge( + z.object({ environments: z.object({ name: z.string(), slug: z.string() }).array() }) + ).optional() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const workspace = await server.services.project.getAProject({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId + }); + return { workspace }; + } + }); + + server.route({ + url: "/", + method: "POST", + schema: { + body: z.object({ + workspaceName: z.string().trim(), + organizationId: z.string().trim() + }), + response: { + 200: z.object({ + workspace: ProjectsSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const workspace = await server.services.project.createProject({ + actorId: req.permission.id, + actor: req.permission.type, + orgId: req.body.organizationId, + workspaceName: req.body.workspaceName + }); + return { workspace }; + } + }); + + server.route({ + url: "/:workspaceId", + method: "DELETE", + schema: { + params: z.object({ + workspaceId: z.string().trim() + }), + response: { + 200: z.object({ + workspace: ProjectsSchema.optional() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const workspace = await server.services.project.deleteProject({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId + }); + return { workspace }; + } + }); + + server.route({ + url: "/:workspaceId/name", + method: "POST", + schema: { + params: z.object({ + workspaceId: z.string().trim() + }), + body: z.object({ + name: z.string().trim() + }), + response: { + 200: z.object({ + message: z.string(), + workspace: ProjectsSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const workspace = await server.services.project.updateName({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId, + name: req.body.name + }); + return { + message: "Successfully changed workspace name", + workspace + }; + } + }); + + server.route({ + url: "/:workspaceId/auto-capitalization", + method: "POST", + schema: { + params: z.object({ + workspaceId: z.string().trim() + }), + body: z.object({ + autoCapitalization: z.boolean() + }), + response: { + 200: z.object({ + message: z.string(), + workspace: ProjectsSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const workspace = await server.services.project.toggleAutoCapitalization({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId, + autoCapitalization: req.body.autoCapitalization + }); + return { + message: "Successfully changed workspace settings", + workspace + }; + } + }); + + server.route({ + url: "/:workspaceId/invite-signup", + method: "POST", + schema: { + params: z.object({ + workspaceId: z.string().trim() + }), + body: z.object({ + email: z.string().trim() + }), + response: { + 200: z.object({ + invitee: UsersSchema, + latestKey: ProjectKeysSchema + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const { invitee, latestKey } = await server.services.projectMembership.inviteUserToProject({ + actorId: req.permission.id, + actor: req.permission.type, + projectId: req.params.workspaceId, + email: req.body.email + }); + return { invitee, latestKey }; + } + }); +}; diff --git a/backend-pg/src/server/routes/v2/index.ts b/backend-pg/src/server/routes/v2/index.ts index f1abeb95f..32661105f 100644 --- a/backend-pg/src/server/routes/v2/index.ts +++ b/backend-pg/src/server/routes/v2/index.ts @@ -1,7 +1,14 @@ import { registerMfaRouter } from "./mfa-router"; +import { registerProjectRouter } from "./project-router"; import { registerUserRouter } from "./user-router"; export const registerV2Routes = async (server: FastifyZodProvider) => { await server.register(registerMfaRouter, { prefix: "/auth" }); await server.register(registerUserRouter, { prefix: "/users" }); + await server.register( + async (projectServer) => { + projectServer.register(registerProjectRouter); + }, + { prefix: "/workspace" } + ); }; diff --git a/backend-pg/src/server/routes/v2/project-router.ts b/backend-pg/src/server/routes/v2/project-router.ts new file mode 100644 index 000000000..76b3fbc3a --- /dev/null +++ b/backend-pg/src/server/routes/v2/project-router.ts @@ -0,0 +1,35 @@ +import { ProjectKeysSchema } from "@app/db/schemas"; +import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; +import { AuthMode } from "@app/services/auth/auth-type"; +import { z } from "zod"; + +export const registerProjectRouter = async (server: FastifyZodProvider) => { + server.route({ + url: "/:workspaceId/encrypted-key", + method: "GET", + schema: { + params: z.object({ + workspaceId: z.string().trim() + }), + response: { + 200: ProjectKeysSchema.merge( + z.object({ + sender: z.object({ + publicKey: z.string() + }) + }) + ) + } + }, + onResponse: verifyAuth([AuthMode.JWT, AuthMode.API_KEY]), + handler: async (req) => { + const key = await server.services.projectKey.getLatestProjectKey({ + actor: req.permission.type, + actorId: req.permission.id, + projectId: req.params.workspaceId + }); + + return key; + } + }); +}; diff --git a/backend-pg/src/services/org/org-role-service.ts b/backend-pg/src/services/org/org-role-service.ts index 38cc827ba..9dfe1ce07 100644 --- a/backend-pg/src/services/org/org-role-service.ts +++ b/backend-pg/src/services/org/org-role-service.ts @@ -60,7 +60,10 @@ export const orgRoleServiceFactory = ({ if (existingRole && existingRole.id !== roleId) throw new BadRequestError({ name: "Update Role", message: "Duplicate role" }); } - const [updatedRole] = await orgRoleDal.update({ id: roleId, orgId }, { ...data }); + const [updatedRole] = await orgRoleDal.update( + { id: roleId, orgId }, + { ...data, permissions: data.permissions ? JSON.stringify(data.permissions) : undefined } + ); if (!updateRole) throw new BadRequestError({ message: "Role not found", name: "Update role" }); return updatedRole; }; diff --git a/backend-pg/src/services/project-key/project-key-dal.ts b/backend-pg/src/services/project-key/project-key-dal.ts index cd60c8d0c..48d8c0a2b 100644 --- a/backend-pg/src/services/project-key/project-key-dal.ts +++ b/backend-pg/src/services/project-key/project-key-dal.ts @@ -1,5 +1,5 @@ import { TDbClient } from "@app/db"; -import { TableName,TProjectKeys } from "@app/db/schemas"; +import { TableName, TProjectKeys } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; @@ -35,8 +35,26 @@ export const projectKeyDalFactory = (db: TDbClient) => { } }; + const findAllProjectUserPubKeys = async (projectId: string) => { + try { + const pubKeys = await db(TableName.ProjectMembership) + .where({ projectId }) + .join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) + .join( + TableName.UserEncryptionKey, + `${TableName.Users}.id`, + `${TableName.UserEncryptionKey}.userId` + ) + .select("userId", "publicKey"); + return pubKeys; + } catch (error) { + throw new DatabaseError({ error, name: "Find all workspace pub keys" }); + } + }; + return { ...projectKeyOrm, + findAllProjectUserPubKeys, findLatestProjectKey }; }; diff --git a/backend-pg/src/services/project-key/project-key-service.ts b/backend-pg/src/services/project-key/project-key-service.ts index 994cb3b8a..9baa0ecca 100644 --- a/backend-pg/src/services/project-key/project-key-service.ts +++ b/backend-pg/src/services/project-key/project-key-service.ts @@ -57,8 +57,18 @@ export const projectKeyServiceFactory = ({ return latestKey; }; + const getProjectPublicKeys = async ({ actor, actorId, projectId }: TGetLatestProjectKeyDTO) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionActions.Read, + ProjectPermissionSub.Member + ); + return projectKeyDal.findAllProjectUserPubKeys(projectId); + }; + return { uploadProjectKeys, + getProjectPublicKeys, getLatestProjectKey }; }; diff --git a/backend-pg/src/services/project-membership/project-membership-dal.ts b/backend-pg/src/services/project-membership/project-membership-dal.ts index ef4c489a2..060004eb9 100644 --- a/backend-pg/src/services/project-membership/project-membership-dal.ts +++ b/backend-pg/src/services/project-membership/project-membership-dal.ts @@ -21,20 +21,18 @@ export const projectMembershipDalFactory = (db: TDbClient) => { ) .select( db.ref("id").withSchema(TableName.ProjectMembership), - db.ref("inviteEmail").withSchema(TableName.ProjectMembership), - db.ref("orgId").withSchema(TableName.ProjectMembership), + db.ref("projectId").withSchema(TableName.ProjectMembership), db.ref("role").withSchema(TableName.ProjectMembership), db.ref("roleId").withSchema(TableName.ProjectMembership), - db.ref("status").withSchema(TableName.ProjectMembership), db.ref("email").withSchema(TableName.Users), db.ref("firstName").withSchema(TableName.Users), db.ref("lastName").withSchema(TableName.Users), db.ref("id").withSchema(TableName.Users).as("userId"), db.ref("publicKey").withSchema(TableName.UserEncryptionKey) ); - return members.map(({ email, firstName, lastName, userId, publicKey, ...data }) => ({ + return members.map(({ email, firstName, lastName, publicKey, ...data }) => ({ ...data, - user: { email, firstName, lastName, id: userId, publicKey } + user: { email, firstName, lastName, id: data.userId, publicKey } })); } catch (error) { throw new DatabaseError({ error, name: "Find all project members" }); diff --git a/backend-pg/src/services/project-membership/project-membership-service.ts b/backend-pg/src/services/project-membership/project-membership-service.ts index 2caf46a04..f1ed76449 100644 --- a/backend-pg/src/services/project-membership/project-membership-service.ts +++ b/backend-pg/src/services/project-membership/project-membership-service.ts @@ -177,6 +177,7 @@ export const projectMembershipServiceFactory = ({ tx ); await projectKeyDal.delete({ receiverId: deletedMembership.userId, projectId }, tx); + return deletedMembership; }); return membership; }; diff --git a/backend-pg/src/services/project-role/project-role-service.ts b/backend-pg/src/services/project-role/project-role-service.ts index 4ce36d0b4..30cfaf363 100644 --- a/backend-pg/src/services/project-role/project-role-service.ts +++ b/backend-pg/src/services/project-role/project-role-service.ts @@ -2,15 +2,13 @@ import { ForbiddenError } from "@casl/ability"; import { packRules } from "@casl/ability/extra"; import { ProjectMembershipRole, TOrgRolesUpdate, TProjectRolesInsert } from "@app/db/schemas"; -import { - OrgPermissionActions, - OrgPermissionSubjects -} from "@app/ee/services/permission/org-permission"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { projectAdminPermissions, projectMemberPermissions, projectNoAccessPermissions, + ProjectPermissionActions, + ProjectPermissionSub, projectViewerPermission } from "@app/ee/services/permission/project-permission"; import { BadRequestError } from "@app/lib/errors"; @@ -22,7 +20,7 @@ type TProjectRoleServiceFactoryDep = { projectRoleDal: TProjectRoleDalFactory; permissionService: Pick< TPermissionServiceFactory, - "getProjectPermission" | "getUserOrgPermission" + "getProjectPermission" | "getUserProjectPermission" >; }; @@ -40,8 +38,8 @@ export const projectRoleServiceFactory = ({ ) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Create, - OrgPermissionSubjects.Role + ProjectPermissionActions.Create, + ProjectPermissionSub.Role ); const existingRole = await projectRoleDal.findOne({ slug: data.slug, projectId }); if (existingRole) throw new BadRequestError({ name: "Create Role", message: "Duplicate role" }); @@ -62,8 +60,8 @@ export const projectRoleServiceFactory = ({ ) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Edit, - OrgPermissionSubjects.Role + ProjectPermissionActions.Edit, + ProjectPermissionSub.Role ); if (data?.slug) { const existingRole = await projectRoleDal.findOne({ slug: data.slug, projectId }); @@ -83,8 +81,8 @@ export const projectRoleServiceFactory = ({ ) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Delete, - OrgPermissionSubjects.Role + ProjectPermissionActions.Delete, + ProjectPermissionSub.Role ); const [deletedRole] = await projectRoleDal.delete({ id: roleId, projectId }); if (!deleteRole) throw new BadRequestError({ message: "Role not found", name: "Update role" }); @@ -95,8 +93,8 @@ export const projectRoleServiceFactory = ({ const listRoles = async (actor: ActorType, actorId: string, projectId: string) => { const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); ForbiddenError.from(permission).throwUnlessCan( - OrgPermissionActions.Read, - OrgPermissionSubjects.Role + ProjectPermissionActions.Read, + ProjectPermissionSub.Role ); const customRoles = await projectRoleDal.find({ projectId }); const roles = [ @@ -149,8 +147,11 @@ export const projectRoleServiceFactory = ({ return roles; }; - const getUserPermission = async (userId: string, orgId: string) => { - const { permission, membership } = await permissionService.getUserOrgPermission(userId, orgId); + const getUserPermission = async (userId: string, projectId: string) => { + const { permission, membership } = await permissionService.getUserProjectPermission( + userId, + projectId + ); return { permissions: packRules(permission.rules), membership }; }; diff --git a/backend-pg/src/services/project/project-dal.ts b/backend-pg/src/services/project/project-dal.ts index 38db9dd00..9f8260eb7 100644 --- a/backend-pg/src/services/project/project-dal.ts +++ b/backend-pg/src/services/project/project-dal.ts @@ -1,5 +1,5 @@ import { TDbClient } from "@app/db"; -import { TableName,TProjects } from "@app/db/schemas"; +import { TableName, TProjects } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { mergeOneToManyRelation, ormify } from "@app/lib/knex"; @@ -90,27 +90,9 @@ export const projectDalFactory = (db: TDbClient) => { } }; - const findAllProjectUserPubKeys = async (projectId: string) => { - try { - const pubKeys = await db(TableName.ProjectMembership) - .where({ projectId }) - .join(TableName.Users, `${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`) - .join( - TableName.UserEncryptionKey, - `${TableName.Users}.id`, - `${TableName.UserEncryptionKey}.userId` - ) - .select("userId", "publicKey"); - return pubKeys; - } catch (error) { - throw new DatabaseError({ error, name: "Find all workspace pub keys" }); - } - }; - return { ...projectOrm, findAllProjects, - findProjectById, - findAllProjectUserPubKeys + findProjectById }; }; diff --git a/backend-pg/src/services/project/project-service.ts b/backend-pg/src/services/project/project-service.ts index d48496b02..374740782 100644 --- a/backend-pg/src/services/project/project-service.ts +++ b/backend-pg/src/services/project/project-service.ts @@ -103,11 +103,28 @@ export const projectServiceFactory = ({ return updatedProject; }; + const updateName = async ({ + projectId, + actor, + actorId, + name + }: TGetProjectDTO & { name: string }) => { + const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionActions.Edit, + ProjectPermissionSub.Settings + ); + + const updatedProject = await projectDal.updateById(projectId, { name }); + return updatedProject; + }; + return { createProject, deleteProject, getProjects, getAProject, - toggleAutoCapitalization + toggleAutoCapitalization, + updateName }; }; diff --git a/frontend/src/components/basic/table/ProjectUsersTable.tsx b/frontend/src/components/basic/table/ProjectUsersTable.tsx index adc4b7a9a..9f109b931 100644 --- a/frontend/src/components/basic/table/ProjectUsersTable.tsx +++ b/frontend/src/components/basic/table/ProjectUsersTable.tsx @@ -60,11 +60,12 @@ const ProjectUsersTable = ({ userData, changeData, myUser, filter, isUserListLoa // Delete the row in the table (e.g. a user) // #TODO: Add a pop-up that warns you that the user is going to be deleted. const handleDelete = async (membershipId: string) => { - await deleteUserFromWorkspaceMutateAsync(membershipId); + await deleteUserFromWorkspaceMutateAsync({ membershipId, workspaceId }); }; const handleRoleUpdate = async (index: number, e: string) => { await updateUserWorkspaceRoleMutateAsync({ + workspaceId, membershipId: userData[index].membershipId, role: e.toLowerCase() }); diff --git a/frontend/src/hooks/api/keys/queries.tsx b/frontend/src/hooks/api/keys/queries.tsx index 143266d40..3cf11e189 100644 --- a/frontend/src/hooks/api/keys/queries.tsx +++ b/frontend/src/hooks/api/keys/queries.tsx @@ -9,11 +9,11 @@ const encKeyKeys = { }; export const fetchUserWsKey = async (workspaceID: string) => { - const { data } = await apiRequest.get<{ latestKey: UserWsKeyPair }>( - `/api/v1/key/${workspaceID}/latest` + const { data } = await apiRequest.get( + `/api/v2/workspace/${workspaceID}/encrypted-key` ); - return data.latestKey; + return data; }; export const useGetUserWsKey = (workspaceID: string) => @@ -24,14 +24,11 @@ export const useGetUserWsKey = (workspaceID: string) => }); // mutations -export const uploadWsKey = async ({ - workspaceId, - userId, - encryptedKey, - nonce -}: UploadWsKeyDTO) => { - return apiRequest.post(`/api/v1/key/${workspaceId}`, { key: { userId, encryptedKey, nonce } }) -} +export const uploadWsKey = async ({ workspaceId, userId, encryptedKey, nonce }: UploadWsKeyDTO) => { + return apiRequest.post(`/api/v1/workspace/${workspaceId}/key`, { + key: { userId, encryptedKey, nonce } + }); +}; export const useUploadWsKey = () => useMutation<{}, {}, UploadWsKeyDTO>({ diff --git a/frontend/src/hooks/api/workspace/queries.tsx b/frontend/src/hooks/api/workspace/queries.tsx index bc16d5508..2ffbf4d1a 100644 --- a/frontend/src/hooks/api/workspace/queries.tsx +++ b/frontend/src/hooks/api/workspace/queries.tsx @@ -31,7 +31,8 @@ export const workspaceKeys = { getAllUserWorkspace: ["workspaces"] as const, getWorkspaceAuditLogs: (workspaceId: string) => [{ workspaceId }] as const, getWorkspaceUsers: (workspaceId: string) => [{ workspaceId }] as const, - getWorkspaceIdentityMemberships: (workspaceId: string) => [{ workspaceId }, "workspace-identity-memberships"] as const + getWorkspaceIdentityMemberships: (workspaceId: string) => + [{ workspaceId }, "workspace-identity-memberships"] as const }; const fetchWorkspaceById = async (workspaceId: string) => { @@ -96,7 +97,7 @@ const fetchUserWorkspaceMemberships = async (orgId: string) => { const { data } = await apiRequest.get>( `/api/v1/organization/${orgId}/workspace-memberships` ); - + return data; }; @@ -221,10 +222,10 @@ export const useCreateWsEnvironment = () => { const queryClient = useQueryClient(); return useMutation<{}, {}, CreateEnvironmentDTO>({ - mutationFn: ({ workspaceID, environmentName, environmentSlug }) => { - return apiRequest.post(`/api/v2/workspace/${workspaceID}/environments`, { - environmentName, - environmentSlug + mutationFn: ({ workspaceId, name, slug }) => { + return apiRequest.post(`/api/v1/workspace/${workspaceId}/environments`, { + name, + slug }); }, onSuccess: () => { @@ -238,13 +239,13 @@ export const useReorderWsEnvironment = () => { return useMutation<{}, {}, ReorderEnvironmentsDTO>({ mutationFn: ({ - workspaceID, + workspaceId, environmentSlug, environmentName, otherEnvironmentSlug, otherEnvironmentName }) => { - return apiRequest.patch(`/api/v2/workspace/${workspaceID}/environments`, { + return apiRequest.patch(`/api/v2/workspace/${workspaceId}/environments`, { environmentSlug, environmentName, otherEnvironmentSlug, @@ -261,11 +262,10 @@ export const useUpdateWsEnvironment = () => { const queryClient = useQueryClient(); return useMutation<{}, {}, UpdateEnvironmentDTO>({ - mutationFn: ({ workspaceID, environmentName, environmentSlug, oldEnvironmentSlug }) => { - return apiRequest.put(`/api/v2/workspace/${workspaceID}/environments`, { - environmentName, - environmentSlug, - oldEnvironmentSlug + mutationFn: ({ workspaceId, id, name, slug }) => { + return apiRequest.put(`/api/v2/workspace/${workspaceId}/environments/${id}`, { + name, + slug }); }, onSuccess: () => { @@ -278,10 +278,8 @@ export const useDeleteWsEnvironment = () => { const queryClient = useQueryClient(); return useMutation<{}, {}, DeleteEnvironmentDTO>({ - mutationFn: ({ workspaceID, environmentSlug }) => { - return apiRequest.delete(`/api/v2/workspace/${workspaceID}/environments`, { - data: { environmentSlug } - }); + mutationFn: ({ id, workspaceId }) => { + return apiRequest.delete(`/api/v1/workspace/${workspaceId}/environments/${id}`); }, onSuccess: () => { queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace); @@ -328,14 +326,20 @@ export const useDeleteUserFromWorkspace = () => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async (membershipId: string) => { + mutationFn: async ({ + membershipId, + workspaceId + }: { + workspaceId: string; + membershipId: string; + }) => { const { data: { deletedMembership } - } = await apiRequest.delete(`/api/v1/membership/${membershipId}`); + } = await apiRequest.delete(`/api/v1/${workspaceId}/membership/${membershipId}`); return deletedMembership; }, - onSuccess: (res) => { - queryClient.invalidateQueries(workspaceKeys.getWorkspaceUsers(res.workspace)); + onSuccess: (_, { workspaceId }) => { + queryClient.invalidateQueries(workspaceKeys.getWorkspaceUsers(workspaceId)); } }); }; @@ -343,10 +347,18 @@ export const useDeleteUserFromWorkspace = () => { export const useUpdateUserWorkspaceRole = () => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ({ membershipId, role }: { membershipId: string; role: string }) => { + mutationFn: async ({ + membershipId, + role, + workspaceId + }: { + membershipId: string; + role: string; + workspaceId: string; + }) => { const { data: { membership } - } = await apiRequest.post(`/api/v1/membership/${membershipId}/change-role`, { + } = await apiRequest.post(`/api/v1/${workspaceId}/membership/${membershipId}`, { role }); return membership; @@ -369,13 +381,15 @@ export const useAddIdentityToWorkspace = () => { workspaceId: string; role?: string; }) => { - const { data: { identityMembership } - } = await apiRequest.post(`/api/v2/workspace/${workspaceId}/identity-memberships/${identityId}`, { - role - }); - + } = await apiRequest.post( + `/api/v2/workspace/${workspaceId}/identity-memberships/${identityId}`, + { + role + } + ); + return identityMembership; }, onSuccess: (_, { workspaceId }) => { @@ -396,12 +410,14 @@ export const useUpdateIdentityWorkspaceRole = () => { workspaceId: string; role?: string; }) => { - const { data: { identityMembership } - } = await apiRequest.patch(`/api/v2/workspace/${workspaceId}/identity-memberships/${identityId}`, { - role - }); + } = await apiRequest.patch( + `/api/v2/workspace/${workspaceId}/identity-memberships/${identityId}`, + { + role + } + ); return identityMembership; }, @@ -416,14 +432,16 @@ export const useDeleteIdentityFromWorkspace = () => { return useMutation({ mutationFn: async ({ identityId, - workspaceId, + workspaceId }: { identityId: string; workspaceId: string; }) => { const { data: { identityMembership } - } = await apiRequest.delete(`/api/v2/workspace/${workspaceId}/identity-memberships/${identityId}`); + } = await apiRequest.delete( + `/api/v2/workspace/${workspaceId}/identity-memberships/${identityId}` + ); return identityMembership; }, onSuccess: (_, { workspaceId }) => { @@ -445,4 +463,4 @@ export const useGetWorkspaceIdentityMemberships = (workspaceId: string) => { }, enabled: true }); -}; \ No newline at end of file +}; diff --git a/frontend/src/hooks/api/workspace/types.ts b/frontend/src/hooks/api/workspace/types.ts index a4acc2cf6..e82cb97f3 100644 --- a/frontend/src/hooks/api/workspace/types.ts +++ b/frontend/src/hooks/api/workspace/types.ts @@ -8,6 +8,7 @@ export type Workspace = { }; export type WorkspaceEnv = { + id: string; name: string; slug: string; isReadDenied: boolean; @@ -36,13 +37,13 @@ export type ToggleAutoCapitalizationDTO = { workspaceID: string; state: boolean export type DeleteWorkspaceDTO = { workspaceID: string }; export type CreateEnvironmentDTO = { - workspaceID: string; - environmentSlug: string; - environmentName: string; + workspaceId: string; + name: string; + slug: string; }; export type ReorderEnvironmentsDTO = { - workspaceID: string; + workspaceId: string; environmentSlug: string; environmentName: string; otherEnvironmentSlug: string; @@ -50,10 +51,10 @@ export type ReorderEnvironmentsDTO = { }; export type UpdateEnvironmentDTO = { - workspaceID: string; - oldEnvironmentSlug: string; - environmentSlug: string; - environmentName: string; + workspaceId: string; + id: string; + name?: string; + slug?: string; }; -export type DeleteEnvironmentDTO = { workspaceID: string; environmentSlug: string }; +export type DeleteEnvironmentDTO = { workspaceId: string; id: string }; diff --git a/frontend/src/views/Project/MembersPage/components/MemberListTab/MemberListTab.tsx b/frontend/src/views/Project/MembersPage/components/MemberListTab/MemberListTab.tsx index 60949233c..acfb55b7d 100644 --- a/frontend/src/views/Project/MembersPage/components/MemberListTab/MemberListTab.tsx +++ b/frontend/src/views/Project/MembersPage/components/MemberListTab/MemberListTab.tsx @@ -142,7 +142,7 @@ export const MemberListTab = () => { if (!currentOrg?.id) return; try { - await removeUserFromWorkspace(membershipId); + await removeUserFromWorkspace({ workspaceId, membershipId }); createNotification({ text: "Successfully removed user from workspace", type: "success" @@ -182,7 +182,7 @@ export const MemberListTab = () => { return; } - await updateUserWorkspaceRole({ membershipId, role }); + await updateUserWorkspaceRole({ membershipId, role, workspaceId }); createNotification({ text: "Successfully updated user role", type: "success" diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/AddEnvironmentModal.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/AddEnvironmentModal.tsx index 51f92d6fb..477816670 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/AddEnvironmentModal.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/AddEnvironmentModal.tsx @@ -34,9 +34,9 @@ export const AddEnvironmentModal = ({ popUp, handlePopUpClose, handlePopUpToggle if (!currentWorkspace?.id) return; await mutateAsync({ - workspaceID: currentWorkspace.id, - environmentName, - environmentSlug + workspaceId: currentWorkspace.id, + name: environmentName, + slug: environmentSlug }); createNotification({ diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentSection.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentSection.tsx index 29945922b..bd155a2f0 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentSection.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentSection.tsx @@ -38,13 +38,13 @@ export const EnvironmentSection = () => { "upgradePlan" ] as const); - const onEnvDeleteSubmit = async (environmentSlug: string) => { + const onEnvDeleteSubmit = async (id: string) => { try { if (!currentWorkspace?.id) return; await deleteWsEnvironment.mutateAsync({ - workspaceID: currentWorkspace.id, - environmentSlug + workspaceId: currentWorkspace.id, + id }); createNotification({ @@ -116,9 +116,7 @@ export const EnvironmentSection = () => { }?`} onChange={(isOpen) => handlePopUpToggle("deleteEnv", isOpen)} deleteKey={(popUp?.deleteEnv?.data as { slug: string })?.slug || ""} - onDeleteApproved={() => - onEnvDeleteSubmit((popUp?.deleteEnv?.data as { slug: string })?.slug) - } + onDeleteApproved={() => onEnvDeleteSubmit((popUp?.deleteEnv?.data as { id: string })?.id)} /> , { name, - slug + slug, + id }: { name: string; slug: string; + id: string; } ) => void; }; @@ -56,7 +58,7 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => { const indexToSwap = shouldMoveUp ? indexOfEnv - 1 : indexOfEnv + 1; await reorderWsEnvironment.mutateAsync({ - workspaceID: currentWorkspace.id, + workspaceId: currentWorkspace.id, environmentSlug: slug, environmentName: name, otherEnvironmentSlug: currentWorkspace.environments[indexToSwap].slug, @@ -90,7 +92,7 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => { {isLoading && } {!isLoading && currentWorkspace && - currentWorkspace.environments.map(({ name, slug }, pos) => ( + currentWorkspace.environments.map(({ name, slug, id }, pos) => ( {name} {slug} @@ -142,7 +144,7 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => { { - handlePopUpOpen("updateEnv", { name, slug }); + handlePopUpOpen("updateEnv", { name, slug, id }); }} isDisabled={!isAllowed} colorSchema="primary" @@ -160,7 +162,7 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => { {(isAllowed) => ( { - handlePopUpOpen("deleteEnv", { name, slug }); + handlePopUpOpen("deleteEnv", { name, slug, id }); }} size="lg" colorSchema="danger" diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/UpdateEnvironmentModal.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/UpdateEnvironmentModal.tsx index be4f19595..4ef0b66b0 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/UpdateEnvironmentModal.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/UpdateEnvironmentModal.tsx @@ -29,17 +29,17 @@ export const UpdateEnvironmentModal = ({ popUp, handlePopUpClose, handlePopUpTog resolver: yupResolver(schema) }); - const oldEnvironmentSlug = (popUp?.updateEnv?.data as { slug: string })?.slug; + const oldEnvId = (popUp?.updateEnv?.data as { id: string })?.id; const onFormSubmit = async ({ environmentName, environmentSlug }: FormData) => { try { if (!currentWorkspace?.id) return; await mutateAsync({ - workspaceID: currentWorkspace.id, - environmentName, - environmentSlug, - oldEnvironmentSlug + workspaceId: currentWorkspace.id, + name: environmentName, + slug: environmentSlug, + id: oldEnvId }); createNotification({ diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 42ea0e773..2a1451300 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -23,8 +23,7 @@ "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve", - "incremental": true + "jsx": "preserve" }, "include": [ "next-i18next.config.js",