diff --git a/backend-pg/src/db/migrations/20231201151432_backup-key.ts b/backend-pg/src/db/migrations/20231201151432_backup-key.ts index 3672fface..0c5dd2286 100644 --- a/backend-pg/src/db/migrations/20231201151432_backup-key.ts +++ b/backend-pg/src/db/migrations/20231201151432_backup-key.ts @@ -7,13 +7,13 @@ export async function up(knex: Knex): Promise { if (!doesTableExist) { await knex.schema.createTable(TableName.BackupPrivateKey, (t) => { t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); - t.string("encryptedPrivateKey").notNullable(); - t.string("iv").notNullable(); - t.string("tag").notNullable(); + t.text("encryptedPrivateKey").notNullable(); + t.text("iv").notNullable(); + t.text("tag").notNullable(); t.string("algorithm").notNullable(); t.string("keyEncoding").notNullable(); - t.string("salt").notNullable(); - t.string("verifier").notNullable(); + t.text("salt").notNullable(); + t.text("verifier").notNullable(); t.timestamps(true, true, true); t.uuid("userId").notNullable().unique(); t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE"); diff --git a/backend-pg/src/db/migrations/20231212110939_project.ts b/backend-pg/src/db/migrations/20231212110939_project.ts index 73f86802f..2689fe527 100644 --- a/backend-pg/src/db/migrations/20231212110939_project.ts +++ b/backend-pg/src/db/migrations/20231212110939_project.ts @@ -14,8 +14,8 @@ export async function up(knex: Knex): Promise { t.timestamps(true, true, true); }); } - // environments await createOnUpdateTrigger(knex, TableName.Project); + // environments if (!(await knex.schema.hasTable(TableName.Environment))) { await knex.schema.createTable(TableName.Environment, (t) => { t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); @@ -47,7 +47,8 @@ export async function up(knex: Knex): Promise { export async function down(knex: Knex): Promise { await knex.schema.dropTableIfExists(TableName.Environment); + await knex.schema.dropTableIfExists(TableName.ProjectKeys); await knex.schema.dropTableIfExists(TableName.Project); - await dropOnUpdateTrigger(knex, TableName.Project); await dropOnUpdateTrigger(knex, TableName.ProjectKeys); + await dropOnUpdateTrigger(knex, TableName.Project); } diff --git a/backend-pg/src/db/migrations/20231212110946_project-membership.ts b/backend-pg/src/db/migrations/20231212110946_project-membership.ts index 64b2efd6e..a5d81953d 100644 --- a/backend-pg/src/db/migrations/20231212110946_project-membership.ts +++ b/backend-pg/src/db/migrations/20231212110946_project-membership.ts @@ -14,7 +14,7 @@ export async function up(knex: Knex): Promise { // does not need update trigger we will do it manually t.timestamps(true, true, true); t.uuid("projectId").notNullable(); - t.foreign("projectId").references("id").inTable(TableName.ProjectRoles).onDelete("CASCADE"); + t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); }); } diff --git a/backend-pg/src/ee/routes/v1/project-role-router.ts b/backend-pg/src/ee/routes/v1/project-role-router.ts index 2acae5e42..98f41f98e 100644 --- a/backend-pg/src/ee/routes/v1/project-role-router.ts +++ b/backend-pg/src/ee/routes/v1/project-role-router.ts @@ -132,8 +132,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { }), response: { 200: z.object({ - membership: ProjectMembershipsSchema, - permissions: z.any().array() + data: z.object({ + membership: ProjectMembershipsSchema, + permissions: z.any().array() + }) }) } }, @@ -143,7 +145,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => { req.auth.userId, req.params.projectId ); - return { permissions, membership }; + return { data: { permissions, membership } }; } }); }; diff --git a/backend-pg/src/lib/knex/join.ts b/backend-pg/src/lib/knex/join.ts index 68deb014d..d66bb25fa 100644 --- a/backend-pg/src/lib/knex/join.ts +++ b/backend-pg/src/lib/knex/join.ts @@ -11,18 +11,22 @@ export const mergeOneToManyRelation = < childMapper: (arg: T) => C, childKey: Ck ) => { - const recordFirstVisitIndex: Record = {}; + let prevPkIndex = -1; + let prevPkId: null | string = null; + const groupedRecord: (P & Record)[] = []; for (let i = 0; i < data.length; i += 1) { const pk = data[i][key]; const row = data[i]; - if (typeof recordFirstVisitIndex[pk] === "undefined") { + if (pk !== prevPkId) { const parent = parentMapper(row) as any; parent[childKey] = []; groupedRecord.push(parent); - recordFirstVisitIndex[pk] = i; + prevPkId = pk; + prevPkIndex += 1; } - groupedRecord[recordFirstVisitIndex[pk]][childKey].push(childMapper(row)); + console.log(prevPkIndex, prevPkId); + groupedRecord[prevPkIndex][childKey].push(childMapper(row)); } return groupedRecord; }; diff --git a/backend-pg/src/server/plugins/auth/inject-permission.ts b/backend-pg/src/server/plugins/auth/inject-permission.ts index 23e5a0b35..90919dd35 100644 --- a/backend-pg/src/server/plugins/auth/inject-permission.ts +++ b/backend-pg/src/server/plugins/auth/inject-permission.ts @@ -6,6 +6,8 @@ import { ActorType } from "@app/services/auth/auth-type"; export const injectPermission = fp(async (server) => { server.decorateRequest("permission", null); server.addHook("onRequest", async (req) => { + if (!req.auth) return; + if (req.auth.actor === ActorType.USER) { req.permission = { type: ActorType.USER, id: req.auth.userId }; } diff --git a/backend-pg/src/server/routes/index.ts b/backend-pg/src/server/routes/index.ts index 402d85d8a..395391f71 100644 --- a/backend-pg/src/server/routes/index.ts +++ b/backend-pg/src/server/routes/index.ts @@ -38,6 +38,7 @@ import { injectIdentity } from "../plugins/auth/inject-identity"; import { registerV1Routes } from "./v1"; import { registerV2Routes } from "./v2"; import { registerV3Routes } from "./v3"; +import { injectPermission } from "../plugins/auth/inject-permission"; export const registerRoutes = async ( server: FastifyZodProvider, @@ -149,6 +150,7 @@ export const registerRoutes = async ( }); await server.register(injectIdentity); + await server.register(injectPermission); server.route({ url: "/status", diff --git a/backend-pg/src/server/routes/v1/password-router.ts b/backend-pg/src/server/routes/v1/password-router.ts index 92da5eeea..faf5cf3f7 100644 --- a/backend-pg/src/server/routes/v1/password-router.ts +++ b/backend-pg/src/server/routes/v1/password-router.ts @@ -82,7 +82,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { response: { 200: z.object({ message: z.string(), - backupPrivateKey: BackupPrivateKeySchema + backupPrivateKey: BackupPrivateKeySchema.omit({ verifier: true }) }) } }, @@ -105,7 +105,7 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => { response: { 200: z.object({ message: z.string(), - backupPrivateKey: BackupPrivateKeySchema + backupPrivateKey: BackupPrivateKeySchema.omit({ verifier: true }) }) } }, diff --git a/backend-pg/src/server/routes/v1/project-router.ts b/backend-pg/src/server/routes/v1/project-router.ts index 6f47d2021..cf3e7fb21 100644 --- a/backend-pg/src/server/routes/v1/project-router.ts +++ b/backend-pg/src/server/routes/v1/project-router.ts @@ -10,7 +10,13 @@ import { import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; -export const registerProjectRouter = (server: FastifyZodProvider) => { +const projectWithEnv = ProjectsSchema.merge( + z.object({ + environments: z.object({ name: z.string(), slug: z.string(), id: z.string() }).array() + }) +); + +export const registerProjectRouter = async (server: FastifyZodProvider) => { server.route({ url: "/:workspaceId/keys", method: "GET", @@ -81,9 +87,7 @@ export const registerProjectRouter = (server: FastifyZodProvider) => { schema: { response: { 200: z.object({ - workspaces: ProjectsSchema.merge( - z.object({ environments: z.object({ name: z.string(), slug: z.string() }).array() }) - ).array() + workspaces: projectWithEnv.array() }) } }, @@ -103,9 +107,7 @@ export const registerProjectRouter = (server: FastifyZodProvider) => { }), response: { 200: z.object({ - workspace: ProjectsSchema.merge( - z.object({ environments: z.object({ name: z.string(), slug: z.string() }).array() }) - ).optional() + workspace: projectWithEnv.optional() }) } }, @@ -130,7 +132,7 @@ export const registerProjectRouter = (server: FastifyZodProvider) => { }), response: { 200: z.object({ - workspace: ProjectsSchema + workspace: projectWithEnv }) } }, diff --git a/backend-pg/src/server/routes/v1/user-router.ts b/backend-pg/src/server/routes/v1/user-router.ts index 0b2074823..870520dbd 100644 --- a/backend-pg/src/server/routes/v1/user-router.ts +++ b/backend-pg/src/server/routes/v1/user-router.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -import { UsersSchema } from "@app/db/schemas"; +import { UserEncryptionKeysSchema, UsersSchema } from "@app/db/schemas"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -11,7 +11,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => { schema: { response: { 200: z.object({ - user: UsersSchema + user: UsersSchema.merge(UserEncryptionKeysSchema.omit({ verifier: true })) }) } }, diff --git a/backend-pg/src/server/routes/v2/user-router.ts b/backend-pg/src/server/routes/v2/user-router.ts index 3df7a3ba0..0280ca651 100644 --- a/backend-pg/src/server/routes/v2/user-router.ts +++ b/backend-pg/src/server/routes/v2/user-router.ts @@ -1,6 +1,11 @@ import { z } from "zod"; -import { AuthTokenSessionsSchema, OrganizationsSchema, UsersSchema } from "@app/db/schemas"; +import { + AuthTokenSessionsSchema, + OrganizationsSchema, + UserEncryptionKeysSchema, + UsersSchema +} from "@app/db/schemas"; import { ApiKeysSchema } from "@app/db/schemas/api-keys"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMethod, AuthMode } from "@app/services/auth/auth-type"; @@ -195,7 +200,7 @@ export const registerUserRouter = async (server: FastifyZodProvider) => { schema: { response: { 200: z.object({ - user: UsersSchema + user: UsersSchema.merge(UserEncryptionKeysSchema.omit({ verifier: true })) }) } }, diff --git a/backend-pg/src/services/auth-token/auth-token-service.ts b/backend-pg/src/services/auth-token/auth-token-service.ts index 88ea7ec46..42940feb7 100644 --- a/backend-pg/src/services/auth-token/auth-token-service.ts +++ b/backend-pg/src/services/auth-token/auth-token-service.ts @@ -63,14 +63,17 @@ export const tokenServiceFactory = ({ tokenDal }: TAuthTokenServiceFactoryDep) = const tokenHash = await bcrypt.hash(token, appCfg.SALT_ROUNDS); await tokenDal.transaction(async (tx) => { await tokenDal.delete({ userId, type, orgId: orgId || null }, tx); - const newToken = await tokenDal.create({ - tokenHash, - expiresAt: tkCfg.expiresAt.toUTCString(), - type, - userId, - orgId, - triesLeft: tkCfg?.triesLeft - }); + const newToken = await tokenDal.create( + { + tokenHash, + expiresAt: tkCfg.expiresAt.toUTCString(), + type, + userId, + orgId, + triesLeft: tkCfg?.triesLeft + }, + tx + ); return newToken; }); diff --git a/backend-pg/src/services/auth/auth-password-service.ts b/backend-pg/src/services/auth/auth-password-service.ts index 3e0837e08..1979f1710 100644 --- a/backend-pg/src/services/auth/auth-password-service.ts +++ b/backend-pg/src/services/auth/auth-password-service.ts @@ -14,6 +14,7 @@ import { TResetPasswordViaBackupKeyDTO } from "./auth-password-type"; import { AuthTokenType } from "./auth-type"; +import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas"; type TAuthPasswordServiceFactoryDep = { authDal: TAuthDalFactory; @@ -212,13 +213,19 @@ export const authPaswordServiceFactory = ({ ); if (!isValidClientProff) throw new Error("failed to create backup key"); const backup = await authDal.transaction(async (tx) => { - const backupKey = await authDal.upsertBackupKey(userEnc.userId, { - encryptedPrivateKey, - iv, - tag, - salt, - verifier - }); + const backupKey = await authDal.upsertBackupKey( + userEnc.userId, + { + encryptedPrivateKey, + iv, + tag, + salt, + verifier, + algorithm: SecretEncryptionAlgo.AES_256_GCM, + keyEncoding: SecretKeyEncoding.UTF8 + }, + tx + ); await userDal.updateUserEncryptionByUserId( userEnc.userId, 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 30cfaf363..5c5301d72 100644 --- a/backend-pg/src/services/project-role/project-role-service.ts +++ b/backend-pg/src/services/project-role/project-role-service.ts @@ -68,8 +68,11 @@ export const projectRoleServiceFactory = ({ if (existingRole && existingRole.id !== roleId) throw new BadRequestError({ name: "Update Role", message: "Duplicate role" }); } - const [updatedRole] = await projectRoleDal.update({ id: roleId, projectId }, { ...data }); - if (!updateRole) throw new BadRequestError({ message: "Role not found", name: "Update role" }); + const [updatedRole] = await projectRoleDal.update( + { id: roleId, projectId }, + { ...data, permissions: data.permissions ? JSON.stringify(data.permissions) : undefined } + ); + if (!updatedRole) throw new BadRequestError({ message: "Role not found", name: "Update role" }); return updatedRole; }; diff --git a/backend-pg/src/services/project/project-dal.ts b/backend-pg/src/services/project/project-dal.ts index 9f8260eb7..43c94012b 100644 --- a/backend-pg/src/services/project/project-dal.ts +++ b/backend-pg/src/services/project/project-dal.ts @@ -15,13 +15,13 @@ export const projectDalFactory = (db: TDbClient) => { const workspaces = await db(TableName.ProjectMembership) .where({ userId }) .join( - TableName.Environment, - `${TableName.Environment}.projectId`, + TableName.Project, + `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id` ) .join( - TableName.Project, - `${TableName.ProjectMembership}.projectId`, + TableName.Environment, + `${TableName.Environment}.projectId`, `${TableName.Project}.id` ) .select( @@ -57,13 +57,13 @@ export const projectDalFactory = (db: TDbClient) => { const workspaces = await db(TableName.ProjectMembership) .where(`${TableName.Project}.id`, id) .join( - TableName.Environment, - `${TableName.Environment}.projectId`, + TableName.Project, + `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id` ) .join( - TableName.Project, - `${TableName.ProjectMembership}.projectId`, + TableName.Environment, + `${TableName.Environment}.projectId`, `${TableName.Project}.id` ) .select( diff --git a/backend-pg/src/services/project/project-service.ts b/backend-pg/src/services/project/project-service.ts index 374740782..7c59a5328 100644 --- a/backend-pg/src/services/project/project-service.ts +++ b/backend-pg/src/services/project/project-service.ts @@ -50,11 +50,14 @@ export const projectServiceFactory = ({ // TODO(backend-pg): licence server const newProject = projectDal.transaction(async (tx) => { const project = await projectDal.create({ name: workspaceName, orgId }, tx); - await projectMembershipDal.create({ - userId: actorId, - role: ProjectMembershipRole.Admin, - projectId: project.id - }); + await projectMembershipDal.create( + { + userId: actorId, + role: ProjectMembershipRole.Admin, + projectId: project.id + }, + tx + ); const envs = await projectEnvDal.insertMany( DEFAULT_PROJECT_ENVS.map((el) => ({ ...el, projectId: project.id })), tx diff --git a/backend-pg/src/services/user/user-dal.ts b/backend-pg/src/services/user/user-dal.ts index 834f9f2c5..c8cfb0ab6 100644 --- a/backend-pg/src/services/user/user-dal.ts +++ b/backend-pg/src/services/user/user-dal.ts @@ -37,7 +37,7 @@ export const userDalFactory = (db: TDbClient) => { const findUserEncKeyByUserId = async (userId: string) => { try { - return await db(TableName.Users) + const user = await db(TableName.Users) .where(`${TableName.Users}.id`, userId) .join( TableName.UserEncryptionKey, @@ -45,6 +45,11 @@ export const userDalFactory = (db: TDbClient) => { `${TableName.UserEncryptionKey}.userId` ) .first(); + if (user?.id) { + // change to user id + user.id = user.userId; + } + return user; } catch (error) { throw new DatabaseError({ error, name: "Find user enc by user id" }); } diff --git a/frontend/src/hooks/api/identities/types.ts b/frontend/src/hooks/api/identities/types.ts index 604163032..7a8e207e2 100644 --- a/frontend/src/hooks/api/identities/types.ts +++ b/frontend/src/hooks/api/identities/types.ts @@ -1,123 +1,123 @@ -import { TRole } from "../roles/types"; +import { TOrgRole, TProjectRole } from "../roles/types"; import { IdentityAuthMethod } from "./enums"; export type IdentityTrustedIp = { - id: string; - ipAddress: string; - type: "ipv4" | "ipv6"; - prefix?: number; -} + id: string; + ipAddress: string; + type: "ipv4" | "ipv6"; + prefix?: number; +}; export type Identity = { - id: string; - name: string; - authMethod?: IdentityAuthMethod; - createdAt: string; - updatedAt: string; + id: string; + name: string; + authMethod?: IdentityAuthMethod; + createdAt: string; + updatedAt: string; }; export type IdentityMembershipOrg = { - id: string; - identity: Identity; - organization: string; - role: "admin" | "member" | "viewer" | "no-access" | "custom"; - customRole?: TRole; - createdAt: string; - updatedAt: string; -} + id: string; + identity: Identity; + organization: string; + role: "admin" | "member" | "viewer" | "no-access" | "custom"; + customRole?: TOrgRole; + createdAt: string; + updatedAt: string; +}; export type IdentityMembership = { - id: string; - identity: Identity; - organization: string; - role: "admin" | "member" | "viewer" | "no-access" | "custom"; - customRole?: TRole; - createdAt: string; - updatedAt: string; -} + id: string; + identity: Identity; + organization: string; + role: "admin" | "member" | "viewer" | "no-access" | "custom"; + customRole?: TProjectRole; + createdAt: string; + updatedAt: string; +}; export type CreateIdentityDTO = { - name: string; - organizationId: string; - role?: string; -} + name: string; + organizationId: string; + role?: string; +}; export type UpdateIdentityDTO = { - identityId: string; - name?: string; - role?: string; - organizationId: string; -} + identityId: string; + name?: string; + role?: string; + organizationId: string; +}; export type DeleteIdentityDTO = { - identityId: string; - organizationId: string; -} + identityId: string; + organizationId: string; +}; export type IdentityUniversalAuth = { - identityId: string; - clientId: string; - clientSecretTrustedIps: IdentityTrustedIp[]; - accessTokenTTL: number; - accessTokenMaxTTL: number; - accessTokenNumUsesLimit: number; - accessTokenTrustedIps: IdentityTrustedIp[]; -} + identityId: string; + clientId: string; + clientSecretTrustedIps: IdentityTrustedIp[]; + accessTokenTTL: number; + accessTokenMaxTTL: number; + accessTokenNumUsesLimit: number; + accessTokenTrustedIps: IdentityTrustedIp[]; +}; export type AddIdentityUniversalAuthDTO = { - organizationId: string; - identityId: string; - clientSecretTrustedIps: { - ipAddress: string; - }[]; - accessTokenTTL: number; - accessTokenMaxTTL: number; - accessTokenNumUsesLimit: number; - accessTokenTrustedIps: { - ipAddress: string; - }[]; -} + organizationId: string; + identityId: string; + clientSecretTrustedIps: { + ipAddress: string; + }[]; + accessTokenTTL: number; + accessTokenMaxTTL: number; + accessTokenNumUsesLimit: number; + accessTokenTrustedIps: { + ipAddress: string; + }[]; +}; export type UpdateIdentityUniversalAuthDTO = { - organizationId: string; - identityId: string; - clientSecretTrustedIps?: { - ipAddress: string; - }[]; - accessTokenTTL?: number; - accessTokenMaxTTL?: number; - accessTokenNumUsesLimit?: number; - accessTokenTrustedIps?: { - ipAddress: string; - }[]; -} + organizationId: string; + identityId: string; + clientSecretTrustedIps?: { + ipAddress: string; + }[]; + accessTokenTTL?: number; + accessTokenMaxTTL?: number; + accessTokenNumUsesLimit?: number; + accessTokenTrustedIps?: { + ipAddress: string; + }[]; +}; export type CreateIdentityUniversalAuthClientSecretDTO = { - identityId: string; - description?: string; - ttl?: number; - numUsesLimit?: number; -} + identityId: string; + description?: string; + ttl?: number; + numUsesLimit?: number; +}; export type ClientSecretData = { - id: string; - identityUniversalAuth: string; - isClientSecretRevoked: boolean; - description: string; - clientSecretPrefix: string; - clientSecretNumUses: number; - clientSecretNumUsesLimit: number; - clientSecretTTL: number; - createdAt: string; - updatedAt: string; -} + id: string; + identityUniversalAuth: string; + isClientSecretRevoked: boolean; + description: string; + clientSecretPrefix: string; + clientSecretNumUses: number; + clientSecretNumUsesLimit: number; + clientSecretTTL: number; + createdAt: string; + updatedAt: string; +}; export type CreateIdentityUniversalAuthClientSecretRes = { - clientSecret: string; - clientSecretData: ClientSecretData; -} + clientSecret: string; + clientSecretData: ClientSecretData; +}; export type DeleteIdentityUniversalAuthClientSecretDTO = { - identityId: string; - clientSecretId: string; -} \ No newline at end of file + identityId: string; + clientSecretId: string; +}; diff --git a/frontend/src/hooks/api/roles/index.tsx b/frontend/src/hooks/api/roles/index.tsx index 6afd209ba..50736b30d 100644 --- a/frontend/src/hooks/api/roles/index.tsx +++ b/frontend/src/hooks/api/roles/index.tsx @@ -1,14 +1,14 @@ export { useCreateOrgRole, - useCreateRole, + useCreateProjectRole, useDeleteOrgRole, - useDeleteRole, + useDeleteProjectRole, useUpdateOrgRole, - useUpdateRole + useUpdateProjectRole } from "./mutation"; export { useGetOrgRoles, - useGetRoles, + useGetProjectRoles, useGetUserOrgPermissions, useGetUserProjectPermissions } from "./queries"; diff --git a/frontend/src/hooks/api/roles/mutation.tsx b/frontend/src/hooks/api/roles/mutation.tsx index 5a0ad3190..0a727441f 100644 --- a/frontend/src/hooks/api/roles/mutation.tsx +++ b/frontend/src/hooks/api/roles/mutation.tsx @@ -6,45 +6,53 @@ import { apiRequest } from "@app/config/request"; import { roleQueryKeys } from "./queries"; import { TCreateOrgRoleDTO, - TCreateRoleDTO, + TCreateProjectRoleDTO, TDeleteOrgRoleDTO, - TDeleteRoleDTO, + TDeleteProjectRoleDTO, TUpdateOrgRoleDTO, - TUpdateRoleDTO + TUpdateProjectRoleDTO } from "./types"; -export const useCreateRole = () => { +export const useCreateProjectRole = () => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: (dto: TCreateRoleDTO) => apiRequest.post("/api/v1/roles", dto), - onSuccess: (_, { orgId, workspaceId }) => { - queryClient.invalidateQueries(roleQueryKeys.getRoles({ orgId, workspaceId })); - } - }); -}; - -export const useUpdateRole = () => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: ({ id, ...dto }: TUpdateRoleDTO) => apiRequest.patch(`/api/v1/roles/${id}`, dto), - onSuccess: (_, { orgId, workspaceId }) => { - queryClient.invalidateQueries(roleQueryKeys.getRoles({ orgId, workspaceId })); - } - }); -}; - -export const useDeleteRole = () => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: ({ orgId, id }: TDeleteRoleDTO) => - apiRequest.delete(`/api/v1/roles/${id}`, { - data: { orgId } + mutationFn: ({ projectId, permissions, ...dto }: TCreateProjectRoleDTO) => + apiRequest.post(`/api/ee/v1/workspace/${projectId}/roles`, { + ...dto, + permissions: permissions.length ? packRules(permissions) : [] }), - onSuccess: (_, { orgId, workspaceId }) => { - queryClient.invalidateQueries(roleQueryKeys.getRoles({ orgId, workspaceId })); + onSuccess: (_, { projectId }) => { + queryClient.invalidateQueries(roleQueryKeys.getProjectRoles(projectId)); + } + }); +}; + +export const useUpdateProjectRole = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ id, projectId, permissions, ...dto }: TUpdateProjectRoleDTO) => + apiRequest.patch(`/api/ee/v1/workspace/${projectId}/roles/${id}`, { + ...dto, + permissions: permissions?.length ? packRules(permissions) : [] + }), + onSuccess: (_, { projectId }) => { + queryClient.invalidateQueries(roleQueryKeys.getProjectRoles(projectId)); + } + }); +}; + +export const useDeleteProjectRole = () => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: ({ projectId, id }: TDeleteProjectRoleDTO) => + apiRequest.delete(`/api/ee/v1/workspace/${projectId}/roles/${id}`, { + data: { projectId } + }), + onSuccess: (_, { projectId }) => { + queryClient.invalidateQueries(roleQueryKeys.getProjectRoles(projectId)); } }); }; @@ -71,7 +79,7 @@ export const useUpdateOrgRole = () => { mutationFn: ({ id, orgId, permissions, ...dto }: TUpdateOrgRoleDTO) => apiRequest.patch(`/api/ee/v1/organization/${orgId}/roles/${id}`, { ...dto, - permissions: permissions?.length ? packRules(permissions) : undefined + permissions: permissions?.length ? packRules(permissions) : [] }), onSuccess: (_, { orgId }) => { queryClient.invalidateQueries(roleQueryKeys.getOrgRoles(orgId)); diff --git a/frontend/src/hooks/api/roles/queries.tsx b/frontend/src/hooks/api/roles/queries.tsx index 336b9da06..77bb538eb 100644 --- a/frontend/src/hooks/api/roles/queries.tsx +++ b/frontend/src/hooks/api/roles/queries.tsx @@ -10,12 +10,11 @@ import { ProjectPermissionSet } from "@app/context/ProjectPermissionContext/type import { OrgUser } from "../users/types"; import { - TGetRolesDTO, TGetUserOrgPermissionsDTO, TGetUserProjectPermissionDTO, TOrgRole, TPermission, - TRole + TProjectRole } from "./types"; const $glob: FieldInstruction = { @@ -37,7 +36,7 @@ const glob: JsInterpreter> = (node, object, context) => { const conditionsMatcher = buildMongoQueryMatcher({ $glob }, { glob }); export const roleQueryKeys = { - getRoles: ({ orgId, workspaceId }: TGetRolesDTO) => ["roles", { orgId, workspaceId }] as const, + getProjectRoles: (projectId: string) => ["roles", { projectId }] as const, getOrgRoles: (orgId: string) => ["org-roles", { orgId }] as const, getUserOrgPermissions: ({ orgId }: TGetUserOrgPermissionsDTO) => ["user-permissions", { orgId }] as const, @@ -45,25 +44,21 @@ export const roleQueryKeys = { ["user-project-permissions", { workspaceId }] as const }; -const getRoles = async ({ orgId, workspaceId }: TGetRolesDTO) => { - const { data } = await apiRequest.get<{ data: { roles: TRole[] } }>( - "/api/v1/roles", - { - params: { - workspaceId, - orgId - } - } - ); - - return data.data.roles; +const getProjectRoles = async (projectId: string) => { + const { data } = await apiRequest.get<{ + data: { roles: Array & { permissions: unknown }> }; + }>(`/api/ee/v1/workspace/${projectId}/roles`); + return data.data.roles.map(({ permissions, ...el }) => ({ + ...el, + permissions: unpackRules(permissions as PackRule[]) + })); }; -export const useGetRoles = ({ orgId, workspaceId }: TGetRolesDTO) => +export const useGetProjectRoles = (projectId: string) => useQuery({ - queryKey: roleQueryKeys.getRoles({ orgId, workspaceId }), - queryFn: () => getRoles({ orgId, workspaceId }), - enabled: Boolean(orgId) + queryKey: roleQueryKeys.getProjectRoles(projectId), + queryFn: () => getProjectRoles(projectId), + enabled: Boolean(projectId) }); const getOrgRoles = async (orgId: string) => { @@ -109,7 +104,7 @@ export const useGetUserOrgPermissions = ({ orgId }: TGetUserOrgPermissionsDTO) = const getUserProjectPermissions = async ({ workspaceId }: TGetUserProjectPermissionDTO) => { const { data } = await apiRequest.get<{ data: { permissions: PackRule>>[] }; - }>(`/api/v1/roles/workspace/${workspaceId}/permissions`, {}); + }>(`/api/ee/v1/workspace/${workspaceId}/permissions`, {}); return data.data.permissions; }; diff --git a/frontend/src/hooks/api/roles/types.ts b/frontend/src/hooks/api/roles/types.ts index 71c7f2a6d..43dc39e27 100644 --- a/frontend/src/hooks/api/roles/types.ts +++ b/frontend/src/hooks/api/roles/types.ts @@ -1,19 +1,16 @@ -export type TGetRolesDTO = { - orgId: string; +export type TGetProjectRolesDTO = { workspaceId?: string; }; -// @depreciated -export type TRole = { - id: string; - organization: string; - workspace: T; - name: string; - description: string; +export type TProjectRole = { slug: string; - permissions: T extends string ? TProjectPermission[] : TPermission[]; + name: string; + projectId: string; + id: string; createdAt: string; updatedAt: string; + description?: string; + permissions: TPermission[]; }; export type TOrgRole = { @@ -36,28 +33,7 @@ export type TPermission = { export type TProjectPermission = { conditions?: Record; action: string; - subject: string; -}; - -export type TCreateRoleDTO = { - orgId: string; - workspaceId?: T; - name: string; - description?: string; - slug: string; - permissions: T extends string ? TProjectPermission[] : TPermission[]; -}; - -export type TUpdateRoleDTO = { - orgId: string; - id: string; - workspaceId?: T; -} & Partial, "orgId" | "workspaceId">>; - -export type TDeleteRoleDTO = { - orgId: string; - id: string; - workspaceId?: string; + subject: [string]; }; export type TGetUserOrgPermissionsDTO = { @@ -85,3 +61,21 @@ export type TDeleteOrgRoleDTO = { orgId: string; id: string; }; + +export type TCreateProjectRoleDTO = { + projectId: string; + name: string; + description?: string; + slug: string; + permissions: TPermission[]; +}; + +export type TUpdateProjectRoleDTO = { + projectId: string; + id: string; +} & Partial>; + +export type TDeleteProjectRoleDTO = { + projectId: string; + id: string; +}; diff --git a/frontend/src/hooks/api/workspace/queries.tsx b/frontend/src/hooks/api/workspace/queries.tsx index 2ffbf4d1a..c18830649 100644 --- a/frontend/src/hooks/api/workspace/queries.tsx +++ b/frontend/src/hooks/api/workspace/queries.tsx @@ -196,7 +196,7 @@ export const useToggleAutoCapitalization = () => { return useMutation<{}, {}, ToggleAutoCapitalizationDTO>({ mutationFn: ({ workspaceID, state }) => - apiRequest.patch(`/api/v2/workspace/${workspaceID}/auto-capitalization`, { + apiRequest.post(`/api/v1/workspace/${workspaceID}/auto-capitalization`, { autoCapitalization: state }), onSuccess: () => { @@ -245,7 +245,7 @@ export const useReorderWsEnvironment = () => { otherEnvironmentSlug, otherEnvironmentName }) => { - return apiRequest.patch(`/api/v2/workspace/${workspaceId}/environments`, { + return apiRequest.patch(`/api/v1/workspace/${workspaceId}/environments`, { environmentSlug, environmentName, otherEnvironmentSlug, @@ -263,7 +263,7 @@ export const useUpdateWsEnvironment = () => { return useMutation<{}, {}, UpdateEnvironmentDTO>({ mutationFn: ({ workspaceId, id, name, slug }) => { - return apiRequest.put(`/api/v2/workspace/${workspaceId}/environments/${id}`, { + return apiRequest.patch(`/api/v1/workspace/${workspaceId}/environments/${id}`, { name, slug }); diff --git a/frontend/src/hooks/api/workspace/types.ts b/frontend/src/hooks/api/workspace/types.ts index e82cb97f3..77657f9c5 100644 --- a/frontend/src/hooks/api/workspace/types.ts +++ b/frontend/src/hooks/api/workspace/types.ts @@ -2,7 +2,7 @@ export type Workspace = { __v: number; id: string; name: string; - organization: string; + orgId: string; autoCapitalization: boolean; environments: WorkspaceEnv[]; }; diff --git a/frontend/src/layouts/AppLayout/AppLayout.tsx b/frontend/src/layouts/AppLayout/AppLayout.tsx index b652bb1f3..3009c0726 100644 --- a/frontend/src/layouts/AppLayout/AppLayout.tsx +++ b/frontend/src/layouts/AppLayout/AppLayout.tsx @@ -4,7 +4,6 @@ /* eslint-disable vars-on-top */ /* eslint-disable no-var */ /* eslint-disable func-names */ -// @ts-nocheck import crypto from "crypto"; import { useEffect } from "react"; @@ -167,7 +166,7 @@ export const AppLayout = ({ children }: LayoutProps) => { } }; - const changeOrg = async (orgId) => { + const changeOrg = async (orgId: string) => { localStorage.setItem("orgData.id", orgId); router.push(`/org/${orgId}/overview`); }; @@ -178,15 +177,15 @@ export const AppLayout = ({ children }: LayoutProps) => { useEffect(() => { // Put a user in an org if they're not in one yet const putUserInOrg = async () => { - if (tempLocalStorage("orgData.id") === "") { - localStorage.setItem("orgData.id", orgs[0]?.id); + if (tempLocalStorage("orgData.id") === "" && orgs?.[0]?.id) { + localStorage.setItem("orgData.id", orgs?.[0]?.id); } if ( currentOrg && ((workspaces?.length === 0 && router.asPath.includes("project")) || router.asPath.includes("/project/undefined") || - (!orgs?.map((org) => org.id)?.includes(router.query.id) && + (!orgs?.map((org) => org.id)?.includes(router.query.id as string) && !router.asPath.includes("project") && !router.asPath.includes("personal") && !router.asPath.includes("integration"))) @@ -320,7 +319,7 @@ export const AppLayout = ({ children }: LayoutProps) => { size="xs" className="flex w-full items-center justify-start p-0 font-normal" leftIcon={ - currentOrg.id === org.id && ( + currentOrg?.id === org.id && ( ) } @@ -433,7 +432,7 @@ export const AppLayout = ({ children }: LayoutProps) => { >
{workspaces - .filter((ws) => ws.organization === currentOrg?.id) + .filter((ws) => ws.orgId === currentOrg?.id) .map(({ id, name }) => ( { {supportOptions.map(([icon, text, url]) => ( - + workspace.organization === localStorage.getItem("orgData.id") - ) || []; + workspaces?.filter((workspace) => workspace.orgId === localStorage.getItem("orgData.id")) || + []; const currentOrg = String(router.query.id); const { createNotification } = useNotificationContext(); const addWsUser = useAddUserToWs(); diff --git a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal.tsx b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal.tsx index 992cda52b..6f582a96f 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityModal.tsx @@ -14,7 +14,7 @@ import { SelectItem } from "@app/components/v2"; import { useOrganization } from "@app/context"; -import { useCreateIdentity, useGetRoles, useUpdateIdentity } from "@app/hooks/api"; +import { useCreateIdentity, useGetOrgRoles, useUpdateIdentity } from "@app/hooks/api"; import { IdentityAuthMethod } from "@app/hooks/api/identities"; import { UsePopUpState } from "@app/hooks/usePopUp"; @@ -46,9 +46,7 @@ export const IdentityModal = ({ popUp, handlePopUpOpen, handlePopUpToggle }: Pro const { currentOrg } = useOrganization(); const orgId = currentOrg?.id || ""; - const { data: roles } = useGetRoles({ - orgId - }); + const { data: roles } = useGetOrgRoles(orgId); const { mutateAsync: createMutateAsync } = useCreateIdentity(); const { mutateAsync: updateMutateAsync } = useUpdateIdentity(); diff --git a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx index 2a6d076cb..9f58d7cdb 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgIdentityTab/components/IdentitySection/IdentityTable.tsx @@ -19,7 +19,7 @@ import { Tr } from "@app/components/v2"; import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context"; -import { useGetIdentityMembershipOrgs, useGetRoles, useUpdateIdentity } from "@app/hooks/api"; +import { useGetIdentityMembershipOrgs, useGetOrgRoles, useUpdateIdentity } from "@app/hooks/api"; import { IdentityAuthMethod, identityAuthToNameMap } from "@app/hooks/api/identities"; import { UsePopUpState } from "@app/hooks/usePopUp"; @@ -51,9 +51,7 @@ export const IdentityTable = ({ handlePopUpOpen }: Props) => { const { mutateAsync: updateMutateAsync } = useUpdateIdentity(); const { data, isLoading } = useGetIdentityMembershipOrgs(orgId); - const { data: roles } = useGetRoles({ - orgId - }); + const { data: roles } = useGetOrgRoles(orgId); const handleChangeRole = async ({ identityId, role }: { identityId: string; role: string }) => { try { diff --git a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx index 795962179..acd4fe735 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgMembersTab/components/OrgMembersSection/OrgMembersTable.tsx @@ -32,7 +32,6 @@ import { useFetchServerStatus, useGetOrgRoles, useGetOrgUsers, - useGetRoles, useUpdateOrgUserRole } from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; @@ -58,6 +57,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLink }: Prop const orgId = currentOrg?.id || ""; const { data: roles, isLoading: isRolesLoading } = useGetOrgRoles(orgId); + console.log(roles); const [searchMemberFilter, setSearchMemberFilter] = useState(""); diff --git a/frontend/src/views/Org/MembersPage/components/OrgMembersTable/OrgMembersTable.tsx b/frontend/src/views/Org/MembersPage/components/OrgMembersTable/OrgMembersTable.tsx index 2a4087678..08ec1ba6c 100644 --- a/frontend/src/views/Org/MembersPage/components/OrgMembersTable/OrgMembersTable.tsx +++ b/frontend/src/views/Org/MembersPage/components/OrgMembersTable/OrgMembersTable.tsx @@ -61,11 +61,11 @@ import { useUpdateOrgUserRole, useUploadWsKey } from "@app/hooks/api"; -import { TRole } from "@app/hooks/api/roles/types"; +import { TProjectRole } from "@app/hooks/api/roles/types"; import { useFetchServerStatus } from "@app/hooks/api/serverDetails"; type Props = { - roles?: TRole[]; + roles?: TProjectRole[]; isRolesLoading?: boolean; }; diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityModal.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityModal.tsx index c61ebefe2..309a43683 100644 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityModal.tsx +++ b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityModal.tsx @@ -10,7 +10,7 @@ import { useOrganization, useWorkspace } from "@app/context"; import { useAddIdentityToWorkspace, useGetIdentityMembershipOrgs, - useGetRoles, + useGetProjectRoles, useGetWorkspaceIdentityMemberships } from "@app/hooks/api"; import { UsePopUpState } from "@app/hooks/usePopUp"; @@ -40,10 +40,7 @@ export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => { const { data: identityMembershipOrgs } = useGetIdentityMembershipOrgs(orgId); const { data: identityMemberships } = useGetWorkspaceIdentityMemberships(workspaceId); - const { data: roles } = useGetRoles({ - orgId, - workspaceId - }); + const { data: roles } = useGetProjectRoles(workspaceId); const { mutateAsync: addIdentityToWorkspaceMutateAsync } = useAddIdentityToWorkspace(); @@ -171,7 +168,7 @@ export const IdentityModal = ({ popUp, handlePopUpToggle }: Props) => {
All identities in your organization have already been added to this project.
- +
diff --git a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityTable.tsx b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityTable.tsx index 91b41f153..eb7320b89 100644 --- a/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityTable.tsx +++ b/frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentitySection/IdentityTable.tsx @@ -18,14 +18,9 @@ import { THead, Tr } from "@app/components/v2"; +import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import { - ProjectPermissionActions, - ProjectPermissionSub, - useOrganization, - useWorkspace -} from "@app/context"; -import { - useGetRoles, + useGetProjectRoles, useGetWorkspaceIdentityMemberships, useUpdateIdentityWorkspaceRole } from "@app/hooks/api"; @@ -43,16 +38,11 @@ type Props = { export const IdentityTable = ({ handlePopUpOpen }: Props) => { const { createNotification } = useNotificationContext(); - const { currentOrg } = useOrganization(); const { currentWorkspace } = useWorkspace(); - const orgId = currentOrg?.id || ""; const workspaceId = currentWorkspace?.id || ""; const { data, isLoading } = useGetWorkspaceIdentityMemberships(currentWorkspace?.id || ""); - const { data: roles } = useGetRoles({ - orgId, - workspaceId - }); + const { data: roles } = useGetProjectRoles(workspaceId); const { mutateAsync: updateMutateAsync } = useUpdateIdentityWorkspaceRole(); diff --git a/frontend/src/views/Project/MembersPage/components/MemberListTab/MemberListTab.tsx b/frontend/src/views/Project/MembersPage/components/MemberListTab/MemberListTab.tsx index acfb55b7d..3a643c4d0 100644 --- a/frontend/src/views/Project/MembersPage/components/MemberListTab/MemberListTab.tsx +++ b/frontend/src/views/Project/MembersPage/components/MemberListTab/MemberListTab.tsx @@ -47,7 +47,7 @@ import { useAddUserToWs, useDeleteUserFromWorkspace, useGetOrgUsers, - useGetRoles, + useGetProjectRoles, useGetUserWsKey, useGetWorkspaceUsers, useUpdateUserWorkspaceRole, @@ -73,10 +73,7 @@ export const MemberListTab = () => { const orgId = currentOrg?.id || ""; const workspaceId = currentWorkspace?.id || ""; - const { data: roles, isLoading: isRolesLoading } = useGetRoles({ - orgId, - workspaceId - }); + const { data: roles, isLoading: isRolesLoading } = useGetProjectRoles(workspaceId); const { data: wsKey } = useGetUserWsKey(workspaceId); const { data: members, isLoading: isMembersLoading } = useGetWorkspaceUsers(workspaceId); @@ -433,7 +430,7 @@ export const MemberListTab = () => { ) : (
All the users in your organization are already invited.
- +
diff --git a/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/ProjectRoleListTab.tsx b/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/ProjectRoleListTab.tsx index d3bd3b39c..6cfec25ea 100644 --- a/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/ProjectRoleListTab.tsx +++ b/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/ProjectRoleListTab.tsx @@ -3,7 +3,7 @@ import { motion } from "framer-motion"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context"; import { withProjectPermission } from "@app/hoc"; import { usePopUp } from "@app/hooks"; -import { TRole } from "@app/hooks/api/roles/types"; +import { TProjectRole } from "@app/hooks/api/roles/types"; import { ProjectRoleList } from "./components/ProjectRoleList"; import { ProjectRoleModifySection } from "./components/ProjectRoleModifySection"; @@ -21,7 +21,7 @@ export const ProjectRoleListTab = withProjectPermission( exit={{ opacity: 0, translateX: 30 }} > } + role={popUp.editRole.data as TProjectRole} onGoBack={() => handlePopUpClose("editRole")} /> diff --git a/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleList/ProjectRoleList.tsx b/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleList/ProjectRoleList.tsx index 2e3a91a61..ac4038711 100644 --- a/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleList/ProjectRoleList.tsx +++ b/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleList/ProjectRoleList.tsx @@ -18,42 +18,32 @@ import { THead, Tr } from "@app/components/v2"; -import { - ProjectPermissionActions, - ProjectPermissionSub, - useOrganization, - useWorkspace -} from "@app/context"; +import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; import { usePopUp } from "@app/hooks"; -import { useDeleteRole, useGetRoles } from "@app/hooks/api"; -import { TRole } from "@app/hooks/api/roles/types"; +import { useDeleteProjectRole, useGetProjectRoles } from "@app/hooks/api"; +import { TProjectRole } from "@app/hooks/api/roles/types"; type Props = { - onSelectRole: (role?: TRole) => void; + onSelectRole: (role?: TProjectRole) => void; }; export const ProjectRoleList = ({ onSelectRole }: Props) => { const [searchRoles, setSearchRoles] = useState(""); const { createNotification } = useNotificationContext(); const { popUp, handlePopUpOpen, handlePopUpClose } = usePopUp(["deleteRole"] as const); - const { currentOrg } = useOrganization(); const { currentWorkspace } = useWorkspace(); - const orgId = currentOrg?.id || ""; const workspaceId = currentWorkspace?.id || ""; - const { data: roles, isLoading: isRolesLoading } = useGetRoles({ - orgId, - workspaceId - }); + const { data: roles, isLoading: isRolesLoading } = useGetProjectRoles(workspaceId); + console.log(roles); - const { mutateAsync: deleteRole } = useDeleteRole(); + const { mutateAsync: deleteRole } = useDeleteProjectRole(); const handleRoleDelete = async () => { - const { id } = popUp?.deleteRole?.data as TRole; + const { id } = popUp?.deleteRole?.data as TProjectRole; try { await deleteRole({ - orgId, - workspaceId, + projectId: workspaceId, id }); createNotification({ type: "success", text: "Successfully removed the role" }); @@ -99,7 +89,7 @@ export const ProjectRoleList = ({ onSelectRole }: Props) => { {isRolesLoading && } - {(roles as TRole[])?.map((role) => { + {(roles as TProjectRole[])?.map((role) => { const { id, name, slug } = role; const isNonMutatable = ["admin", "member", "viewer", "no-access"].includes(slug); @@ -157,9 +147,9 @@ export const ProjectRoleList = ({ onSelectRole }: Props) => { )?.name || " " + (popUp?.deleteRole?.data as TProjectRole)?.name || " " } role?`} - deleteKey={(popUp?.deleteRole?.data as TRole)?.slug || ""} + deleteKey={(popUp?.deleteRole?.data as TProjectRole)?.slug || ""} onClose={() => handlePopUpClose("deleteRole")} onDeleteApproved={handleRoleDelete} /> diff --git a/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.tsx b/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.tsx index 0c0f935c1..8f09527d8 100644 --- a/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.tsx +++ b/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.tsx @@ -20,9 +20,9 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { Button, FormControl, Input } from "@app/components/v2"; -import { ProjectPermissionSub, useOrganization, useWorkspace } from "@app/context"; -import { useCreateRole, useUpdateRole } from "@app/hooks/api"; -import { TRole } from "@app/hooks/api/roles/types"; +import { ProjectPermissionSub, useWorkspace } from "@app/context"; +import { useCreateProjectRole, useUpdateProjectRole } from "@app/hooks/api"; +import { TProjectRole } from "@app/hooks/api/roles/types"; import { MultiEnvProjectPermission } from "./MultiEnvProjectPermission"; import { @@ -111,7 +111,7 @@ const SINGLE_PERMISSION_LIST = [ ] as const; type Props = { - role?: TRole; + role?: TProjectRole; onGoBack: VoidFunction; }; @@ -120,8 +120,6 @@ export const ProjectRoleModifySection = ({ role, onGoBack }: Props) => { const isNewRole = !role?.slug; const { createNotification } = useNotificationContext(); - const { currentOrg } = useOrganization(); - const orgId = currentOrg?.id || ""; const { currentWorkspace } = useWorkspace(); const workspaceId = currentWorkspace?.id || ""; @@ -135,17 +133,16 @@ export const ProjectRoleModifySection = ({ role, onGoBack }: Props) => { defaultValues: role ? { ...role, permissions: rolePermission2Form(role.permissions) } : {}, resolver: zodResolver(formSchema) }); - const { mutateAsync: createRole } = useCreateRole(); - const { mutateAsync: updateRole } = useUpdateRole(); + const { mutateAsync: createRole } = useCreateProjectRole(); + const { mutateAsync: updateRole } = useUpdateProjectRole(); const handleRoleUpdate = async (el: TFormSchema) => { if (!role?.id) return; try { await updateRole({ - orgId, id: role?.id, - workspaceId, + projectId: workspaceId, ...el, permissions: formRolePermission2API(el.permissions) }); @@ -165,8 +162,7 @@ export const ProjectRoleModifySection = ({ role, onGoBack }: Props) => { try { await createRole({ - orgId, - workspaceId, + projectId: workspaceId, ...el, permissions: formRolePermission2API(el.permissions) }); diff --git a/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.utils.ts b/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.utils.ts index c6175bd58..472d966bb 100644 --- a/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.utils.ts +++ b/frontend/src/views/Project/MembersPage/components/ProjectRoleListTab/components/ProjectRoleModifySection/ProjectRoleModifySection.utils.ts @@ -33,7 +33,7 @@ export const formSchema = z.object({ .object({ secrets: z.record(multiEnvPermissionSchema).optional(), member: generalPermissionSchema, - "identity": generalPermissionSchema, + identity: generalPermissionSchema, role: generalPermissionSchema, integrations: generalPermissionSchema, webhooks: generalPermissionSchema, @@ -90,7 +90,10 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => { const formVal: Record = {}; permissions.forEach((permission) => { - const { subject, action } = permission; + const { + subject: [subject], + action + } = permission; if (!formVal?.[subject]) formVal[subject] = {}; if (subject === "secrets") { diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentTable.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentTable.tsx index e4d0fa3c2..4d0171d00 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentTable.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/EnvironmentTable.tsx @@ -93,7 +93,7 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => { {!isLoading && currentWorkspace && currentWorkspace.environments.map(({ name, slug, id }, pos) => ( - + {name} {slug} diff --git a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/UpdateEnvironmentModal.tsx b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/UpdateEnvironmentModal.tsx index 4ef0b66b0..ecd91303d 100644 --- a/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/UpdateEnvironmentModal.tsx +++ b/frontend/src/views/Settings/ProjectSettingsPage/components/EnvironmentSection/UpdateEnvironmentModal.tsx @@ -15,8 +15,8 @@ type Props = { }; const schema = yup.object({ - environmentName: yup.string().label("Environment Name").required(), - environmentSlug: yup.string().label("Environment Slug").required() + name: yup.string().label("Environment Name").required(), + slug: yup.string().label("Environment Slug").required() }); export type FormData = yup.InferType; @@ -26,19 +26,20 @@ export const UpdateEnvironmentModal = ({ popUp, handlePopUpClose, handlePopUpTog const { currentWorkspace } = useWorkspace(); const { mutateAsync, isLoading } = useUpdateWsEnvironment(); const { control, handleSubmit, reset } = useForm({ - resolver: yupResolver(schema) + resolver: yupResolver(schema), + values: popUp.updateEnv.data as FormData }); const oldEnvId = (popUp?.updateEnv?.data as { id: string })?.id; - const onFormSubmit = async ({ environmentName, environmentSlug }: FormData) => { + const onFormSubmit = async ({ name, slug }: FormData) => { try { if (!currentWorkspace?.id) return; await mutateAsync({ workspaceId: currentWorkspace.id, - name: environmentName, - slug: environmentSlug, + name, + slug, id: oldEnvId }); @@ -70,7 +71,7 @@ export const UpdateEnvironmentModal = ({ popUp, handlePopUpClose, handlePopUpTog ( (