feat(infisical-pg): added routes for workspaces and updated frontend

This commit is contained in:
Akhil Mohan
2024-01-27 12:38:22 +05:30
parent 498a90c484
commit 4f3582a98b
31 changed files with 953 additions and 132 deletions
+10
View File
@@ -10,6 +10,11 @@ import { AuthMode } from "@app/services/auth/auth-signup-type";
import { ActorType } from "@app/services/auth/auth-type"; import { ActorType } from "@app/services/auth/auth-type";
import { TOrgRoleServiceFactory } from "@app/services/org/org-role-service"; import { TOrgRoleServiceFactory } from "@app/services/org/org-role-service";
import { TOrgServiceFactory } from "@app/services/org/org-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 { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
import { TAuthTokenServiceFactory } from "@app/services/token/token-service"; import { TAuthTokenServiceFactory } from "@app/services/token/token-service";
import { TUserDalFactory } from "@app/services/user/user-dal"; import { TUserDalFactory } from "@app/services/user/user-dal";
@@ -54,6 +59,11 @@ declare module "fastify" {
superAdmin: TSuperAdminServiceFactory; superAdmin: TSuperAdminServiceFactory;
user: TUserServiceFactory; user: TUserServiceFactory;
apiKey: TApiKeyServiceFactory; 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 // this is exclusive use for middlewares in which we need to inject data
+2
View File
@@ -1,6 +1,8 @@
import { registerOrgRoleRouter } from "./org-role-router"; import { registerOrgRoleRouter } from "./org-role-router";
import { registerProjectRoleRouter } from "./project-role-router";
export const registerV1EERoutes = async (server: FastifyZodProvider) => { export const registerV1EERoutes = async (server: FastifyZodProvider) => {
// org role starts with organization // org role starts with organization
await server.register(registerOrgRoleRouter, { prefix: "/organization" }); await server.register(registerOrgRoleRouter, { prefix: "/organization" });
await server.register(registerProjectRoleRouter, { prefix: "/workspace" });
}; };
@@ -16,7 +16,6 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
slug: z.string().trim(), slug: z.string().trim(),
name: z.string().trim(), name: z.string().trim(),
description: z.string().trim().optional(), description: z.string().trim().optional(),
workspaceId: z.string().trim().optional(),
permissions: z.any().array() permissions: z.any().array()
}), }),
response: { response: {
@@ -62,10 +61,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
req.auth.userId, req.auth.userId,
req.params.organizationId, req.params.organizationId,
req.params.roleId, req.params.roleId,
{ req.body
...req.body,
permissions: req.body.permissions ? JSON.stringify(req.body.permissions) : undefined
}
); );
return { role }; return { role };
} }
@@ -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 };
}
});
};
@@ -16,7 +16,9 @@ import {
projectAdminPermissions, projectAdminPermissions,
projectMemberPermissions, projectMemberPermissions,
projectNoAccessPermissions, projectNoAccessPermissions,
projectViewerPermission} from "./project-permission"; ProjectPermissionSet,
projectViewerPermission
} from "./project-permission";
type TPermissionServiceFactoryDep = { type TPermissionServiceFactoryDep = {
permissionDal: TPermissionDalFactory; permissionDal: TPermissionDalFactory;
@@ -68,9 +70,9 @@ export const permissionServiceFactory = ({ permissionDal }: TPermissionServiceFa
if (membership.role === ProjectMembershipRole.NoAccess) if (membership.role === ProjectMembershipRole.NoAccess)
return { permission: projectNoAccessPermissions, membership }; return { permission: projectNoAccessPermissions, membership };
if (membership.role === ProjectMembershipRole.Custom) { if (membership.role === ProjectMembershipRole.Custom) {
const permission = createMongoAbility<OrgPermissionSet>( const permission = createMongoAbility<ProjectPermissionSet>(
// akhilmhdh: putting any due to ts incompatiable matching with string and the other // akhilmhdh: putting any due to ts incompatiable matching with string and the other
unpackRules<RawRuleOf<MongoAbility<OrgPermissionSet>>>(membership.permissions as any), unpackRules<RawRuleOf<MongoAbility<ProjectPermissionSet>>>(membership.permissions as any),
{ {
conditionsMatcher conditionsMatcher
} }
+46 -1
View File
@@ -18,6 +18,16 @@ import { orgDalFactory } from "@app/services/org/org-dal";
import { orgRoleDalFactory } from "@app/services/org/org-role-dal"; import { orgRoleDalFactory } from "@app/services/org/org-role-dal";
import { orgRoleServiceFactory } from "@app/services/org/org-role-service"; import { orgRoleServiceFactory } from "@app/services/org/org-role-service";
import { orgServiceFactory } from "@app/services/org/org-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 { TSmtpService } from "@app/services/smtp/smtp-service";
import { superAdminDalFactory } from "@app/services/super-admin/super-admin-dal"; import { superAdminDalFactory } from "@app/services/super-admin/super-admin-dal";
import { superAdminServiceFactory } from "@app/services/super-admin/super-admin-service"; import { superAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
@@ -43,6 +53,12 @@ export const registerRoutes = async (
const superAdminDal = superAdminDalFactory(db); const superAdminDal = superAdminDalFactory(db);
const apiKeyDal = apiKeyDalFactory(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 // ee db layer ops
const permissionDal = permissionDalFactory(db); const permissionDal = permissionDalFactory(db);
@@ -84,6 +100,30 @@ export const registerRoutes = async (
}); });
const apiKeyService = apiKeyServiceFactory({ apiKeyDal }); 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(); await superAdminService.initServerCfg();
// inject all services // inject all services
server.decorate<FastifyZodProvider["services"]>("services", { server.decorate<FastifyZodProvider["services"]>("services", {
@@ -96,7 +136,12 @@ export const registerRoutes = async (
orgRole: orgRoleService, orgRole: orgRoleService,
apiKey: apiKeyService, apiKey: apiKeyService,
authToken: tokenService, authToken: tokenService,
superAdmin: superAdminService superAdmin: superAdminService,
project: projectService,
projectMembership: projectMembershipService,
projectKey: projectKeyService,
projectEnv: projectEnvService,
projectRole: projectRoleService
}); });
server.decorate<FastifyZodProvider["store"]>("store", { server.decorate<FastifyZodProvider["store"]>("store", {
+14 -1
View File
@@ -3,6 +3,10 @@ import { registerAuthRoutes } from "./auth-router";
import { registerInviteOrgRouter } from "./invite-org-router"; import { registerInviteOrgRouter } from "./invite-org-router";
import { registerOrgRouter } from "./organization-router"; import { registerOrgRouter } from "./organization-router";
import { registerPasswordRouter } from "./password-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 { registerSsoRouter } from "./sso-router";
import { registerUserActionRouter } from "./user-action-router"; import { registerUserActionRouter } from "./user-action-router";
import { registerUserRouter } from "./user-router"; import { registerUserRouter } from "./user-router";
@@ -10,11 +14,20 @@ import { registerUserRouter } from "./user-router";
export const registerV1Routes = async (server: FastifyZodProvider) => { export const registerV1Routes = async (server: FastifyZodProvider) => {
await server.register(registerSsoRouter, { prefix: "/sso" }); await server.register(registerSsoRouter, { prefix: "/sso" });
await server.register(registerAuthRoutes, { prefix: "/auth" }); await server.register(registerAuthRoutes, { prefix: "/auth" });
// await server.register(registerSSORouter, { prefix: "/sso" });
await server.register(registerPasswordRouter, { prefix: "/password" }); await server.register(registerPasswordRouter, { prefix: "/password" });
await server.register(registerOrgRouter, { prefix: "/organization" }); await server.register(registerOrgRouter, { prefix: "/organization" });
await server.register(registerAdminRouter, { prefix: "/admin" }); await server.register(registerAdminRouter, { prefix: "/admin" });
await server.register(registerUserRouter, { prefix: "/user" }); await server.register(registerUserRouter, { prefix: "/user" });
await server.register(registerInviteOrgRouter, { prefix: "/invite-org" }); await server.register(registerInviteOrgRouter, { prefix: "/invite-org" });
await server.register(registerUserActionRouter, { prefix: "/user-action" }); 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" }
);
}; };
@@ -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
};
}
});
};
@@ -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" };
}
});
};
@@ -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 };
}
});
};
@@ -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 };
}
});
};
+7
View File
@@ -1,7 +1,14 @@
import { registerMfaRouter } from "./mfa-router"; import { registerMfaRouter } from "./mfa-router";
import { registerProjectRouter } from "./project-router";
import { registerUserRouter } from "./user-router"; import { registerUserRouter } from "./user-router";
export const registerV2Routes = async (server: FastifyZodProvider) => { export const registerV2Routes = async (server: FastifyZodProvider) => {
await server.register(registerMfaRouter, { prefix: "/auth" }); await server.register(registerMfaRouter, { prefix: "/auth" });
await server.register(registerUserRouter, { prefix: "/users" }); await server.register(registerUserRouter, { prefix: "/users" });
await server.register(
async (projectServer) => {
projectServer.register(registerProjectRouter);
},
{ prefix: "/workspace" }
);
}; };
@@ -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;
}
});
};
@@ -60,7 +60,10 @@ export const orgRoleServiceFactory = ({
if (existingRole && existingRole.id !== roleId) if (existingRole && existingRole.id !== roleId)
throw new BadRequestError({ name: "Update Role", message: "Duplicate role" }); 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" }); if (!updateRole) throw new BadRequestError({ message: "Role not found", name: "Update role" });
return updatedRole; return updatedRole;
}; };
@@ -1,5 +1,5 @@
import { TDbClient } from "@app/db"; 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 { DatabaseError } from "@app/lib/errors";
import { ormify } from "@app/lib/knex"; 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 { return {
...projectKeyOrm, ...projectKeyOrm,
findAllProjectUserPubKeys,
findLatestProjectKey findLatestProjectKey
}; };
}; };
@@ -57,8 +57,18 @@ export const projectKeyServiceFactory = ({
return latestKey; 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 { return {
uploadProjectKeys, uploadProjectKeys,
getProjectPublicKeys,
getLatestProjectKey getLatestProjectKey
}; };
}; };
@@ -21,20 +21,18 @@ export const projectMembershipDalFactory = (db: TDbClient) => {
) )
.select( .select(
db.ref("id").withSchema(TableName.ProjectMembership), db.ref("id").withSchema(TableName.ProjectMembership),
db.ref("inviteEmail").withSchema(TableName.ProjectMembership), db.ref("projectId").withSchema(TableName.ProjectMembership),
db.ref("orgId").withSchema(TableName.ProjectMembership),
db.ref("role").withSchema(TableName.ProjectMembership), db.ref("role").withSchema(TableName.ProjectMembership),
db.ref("roleId").withSchema(TableName.ProjectMembership), db.ref("roleId").withSchema(TableName.ProjectMembership),
db.ref("status").withSchema(TableName.ProjectMembership),
db.ref("email").withSchema(TableName.Users), db.ref("email").withSchema(TableName.Users),
db.ref("firstName").withSchema(TableName.Users), db.ref("firstName").withSchema(TableName.Users),
db.ref("lastName").withSchema(TableName.Users), db.ref("lastName").withSchema(TableName.Users),
db.ref("id").withSchema(TableName.Users).as("userId"), db.ref("id").withSchema(TableName.Users).as("userId"),
db.ref("publicKey").withSchema(TableName.UserEncryptionKey) db.ref("publicKey").withSchema(TableName.UserEncryptionKey)
); );
return members.map(({ email, firstName, lastName, userId, publicKey, ...data }) => ({ return members.map(({ email, firstName, lastName, publicKey, ...data }) => ({
...data, ...data,
user: { email, firstName, lastName, id: userId, publicKey } user: { email, firstName, lastName, id: data.userId, publicKey }
})); }));
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "Find all project members" }); throw new DatabaseError({ error, name: "Find all project members" });
@@ -177,6 +177,7 @@ export const projectMembershipServiceFactory = ({
tx tx
); );
await projectKeyDal.delete({ receiverId: deletedMembership.userId, projectId }, tx); await projectKeyDal.delete({ receiverId: deletedMembership.userId, projectId }, tx);
return deletedMembership;
}); });
return membership; return membership;
}; };
@@ -2,15 +2,13 @@ import { ForbiddenError } from "@casl/ability";
import { packRules } from "@casl/ability/extra"; import { packRules } from "@casl/ability/extra";
import { ProjectMembershipRole, TOrgRolesUpdate, TProjectRolesInsert } from "@app/db/schemas"; 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 { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { import {
projectAdminPermissions, projectAdminPermissions,
projectMemberPermissions, projectMemberPermissions,
projectNoAccessPermissions, projectNoAccessPermissions,
ProjectPermissionActions,
ProjectPermissionSub,
projectViewerPermission projectViewerPermission
} from "@app/ee/services/permission/project-permission"; } from "@app/ee/services/permission/project-permission";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
@@ -22,7 +20,7 @@ type TProjectRoleServiceFactoryDep = {
projectRoleDal: TProjectRoleDalFactory; projectRoleDal: TProjectRoleDalFactory;
permissionService: Pick< permissionService: Pick<
TPermissionServiceFactory, TPermissionServiceFactory,
"getProjectPermission" | "getUserOrgPermission" "getProjectPermission" | "getUserProjectPermission"
>; >;
}; };
@@ -40,8 +38,8 @@ export const projectRoleServiceFactory = ({
) => { ) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Create, ProjectPermissionActions.Create,
OrgPermissionSubjects.Role ProjectPermissionSub.Role
); );
const existingRole = await projectRoleDal.findOne({ slug: data.slug, projectId }); const existingRole = await projectRoleDal.findOne({ slug: data.slug, projectId });
if (existingRole) throw new BadRequestError({ name: "Create Role", message: "Duplicate role" }); 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); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Edit, ProjectPermissionActions.Edit,
OrgPermissionSubjects.Role ProjectPermissionSub.Role
); );
if (data?.slug) { if (data?.slug) {
const existingRole = await projectRoleDal.findOne({ slug: data.slug, projectId }); const existingRole = await projectRoleDal.findOne({ slug: data.slug, projectId });
@@ -83,8 +81,8 @@ export const projectRoleServiceFactory = ({
) => { ) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Delete, ProjectPermissionActions.Delete,
OrgPermissionSubjects.Role ProjectPermissionSub.Role
); );
const [deletedRole] = await projectRoleDal.delete({ id: roleId, projectId }); const [deletedRole] = await projectRoleDal.delete({ id: roleId, projectId });
if (!deleteRole) throw new BadRequestError({ message: "Role not found", name: "Update role" }); 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 listRoles = async (actor: ActorType, actorId: string, projectId: string) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Read, ProjectPermissionActions.Read,
OrgPermissionSubjects.Role ProjectPermissionSub.Role
); );
const customRoles = await projectRoleDal.find({ projectId }); const customRoles = await projectRoleDal.find({ projectId });
const roles = [ const roles = [
@@ -149,8 +147,11 @@ export const projectRoleServiceFactory = ({
return roles; return roles;
}; };
const getUserPermission = async (userId: string, orgId: string) => { const getUserPermission = async (userId: string, projectId: string) => {
const { permission, membership } = await permissionService.getUserOrgPermission(userId, orgId); const { permission, membership } = await permissionService.getUserProjectPermission(
userId,
projectId
);
return { permissions: packRules(permission.rules), membership }; return { permissions: packRules(permission.rules), membership };
}; };
+2 -20
View File
@@ -1,5 +1,5 @@
import { TDbClient } from "@app/db"; 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 { DatabaseError } from "@app/lib/errors";
import { mergeOneToManyRelation, ormify } from "@app/lib/knex"; 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 { return {
...projectOrm, ...projectOrm,
findAllProjects, findAllProjects,
findProjectById, findProjectById
findAllProjectUserPubKeys
}; };
}; };
@@ -103,11 +103,28 @@ export const projectServiceFactory = ({
return updatedProject; 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 { return {
createProject, createProject,
deleteProject, deleteProject,
getProjects, getProjects,
getAProject, getAProject,
toggleAutoCapitalization toggleAutoCapitalization,
updateName
}; };
}; };
@@ -60,11 +60,12 @@ const ProjectUsersTable = ({ userData, changeData, myUser, filter, isUserListLoa
// Delete the row in the table (e.g. a user) // 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. // #TODO: Add a pop-up that warns you that the user is going to be deleted.
const handleDelete = async (membershipId: string) => { const handleDelete = async (membershipId: string) => {
await deleteUserFromWorkspaceMutateAsync(membershipId); await deleteUserFromWorkspaceMutateAsync({ membershipId, workspaceId });
}; };
const handleRoleUpdate = async (index: number, e: string) => { const handleRoleUpdate = async (index: number, e: string) => {
await updateUserWorkspaceRoleMutateAsync({ await updateUserWorkspaceRoleMutateAsync({
workspaceId,
membershipId: userData[index].membershipId, membershipId: userData[index].membershipId,
role: e.toLowerCase() role: e.toLowerCase()
}); });
+8 -11
View File
@@ -9,11 +9,11 @@ const encKeyKeys = {
}; };
export const fetchUserWsKey = async (workspaceID: string) => { export const fetchUserWsKey = async (workspaceID: string) => {
const { data } = await apiRequest.get<{ latestKey: UserWsKeyPair }>( const { data } = await apiRequest.get<UserWsKeyPair>(
`/api/v1/key/${workspaceID}/latest` `/api/v2/workspace/${workspaceID}/encrypted-key`
); );
return data.latestKey; return data;
}; };
export const useGetUserWsKey = (workspaceID: string) => export const useGetUserWsKey = (workspaceID: string) =>
@@ -24,14 +24,11 @@ export const useGetUserWsKey = (workspaceID: string) =>
}); });
// mutations // mutations
export const uploadWsKey = async ({ export const uploadWsKey = async ({ workspaceId, userId, encryptedKey, nonce }: UploadWsKeyDTO) => {
workspaceId, return apiRequest.post(`/api/v1/workspace/${workspaceId}/key`, {
userId, key: { userId, encryptedKey, nonce }
encryptedKey, });
nonce };
}: UploadWsKeyDTO) => {
return apiRequest.post(`/api/v1/key/${workspaceId}`, { key: { userId, encryptedKey, nonce } })
}
export const useUploadWsKey = () => export const useUploadWsKey = () =>
useMutation<{}, {}, UploadWsKeyDTO>({ useMutation<{}, {}, UploadWsKeyDTO>({
+53 -35
View File
@@ -31,7 +31,8 @@ export const workspaceKeys = {
getAllUserWorkspace: ["workspaces"] as const, getAllUserWorkspace: ["workspaces"] as const,
getWorkspaceAuditLogs: (workspaceId: string) => [{ workspaceId }] as const, getWorkspaceAuditLogs: (workspaceId: string) => [{ workspaceId }] as const,
getWorkspaceUsers: (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) => { const fetchWorkspaceById = async (workspaceId: string) => {
@@ -96,7 +97,7 @@ const fetchUserWorkspaceMemberships = async (orgId: string) => {
const { data } = await apiRequest.get<Record<string, Workspace[]>>( const { data } = await apiRequest.get<Record<string, Workspace[]>>(
`/api/v1/organization/${orgId}/workspace-memberships` `/api/v1/organization/${orgId}/workspace-memberships`
); );
return data; return data;
}; };
@@ -221,10 +222,10 @@ export const useCreateWsEnvironment = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<{}, {}, CreateEnvironmentDTO>({ return useMutation<{}, {}, CreateEnvironmentDTO>({
mutationFn: ({ workspaceID, environmentName, environmentSlug }) => { mutationFn: ({ workspaceId, name, slug }) => {
return apiRequest.post(`/api/v2/workspace/${workspaceID}/environments`, { return apiRequest.post(`/api/v1/workspace/${workspaceId}/environments`, {
environmentName, name,
environmentSlug slug
}); });
}, },
onSuccess: () => { onSuccess: () => {
@@ -238,13 +239,13 @@ export const useReorderWsEnvironment = () => {
return useMutation<{}, {}, ReorderEnvironmentsDTO>({ return useMutation<{}, {}, ReorderEnvironmentsDTO>({
mutationFn: ({ mutationFn: ({
workspaceID, workspaceId,
environmentSlug, environmentSlug,
environmentName, environmentName,
otherEnvironmentSlug, otherEnvironmentSlug,
otherEnvironmentName otherEnvironmentName
}) => { }) => {
return apiRequest.patch(`/api/v2/workspace/${workspaceID}/environments`, { return apiRequest.patch(`/api/v2/workspace/${workspaceId}/environments`, {
environmentSlug, environmentSlug,
environmentName, environmentName,
otherEnvironmentSlug, otherEnvironmentSlug,
@@ -261,11 +262,10 @@ export const useUpdateWsEnvironment = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<{}, {}, UpdateEnvironmentDTO>({ return useMutation<{}, {}, UpdateEnvironmentDTO>({
mutationFn: ({ workspaceID, environmentName, environmentSlug, oldEnvironmentSlug }) => { mutationFn: ({ workspaceId, id, name, slug }) => {
return apiRequest.put(`/api/v2/workspace/${workspaceID}/environments`, { return apiRequest.put(`/api/v2/workspace/${workspaceId}/environments/${id}`, {
environmentName, name,
environmentSlug, slug
oldEnvironmentSlug
}); });
}, },
onSuccess: () => { onSuccess: () => {
@@ -278,10 +278,8 @@ export const useDeleteWsEnvironment = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<{}, {}, DeleteEnvironmentDTO>({ return useMutation<{}, {}, DeleteEnvironmentDTO>({
mutationFn: ({ workspaceID, environmentSlug }) => { mutationFn: ({ id, workspaceId }) => {
return apiRequest.delete(`/api/v2/workspace/${workspaceID}/environments`, { return apiRequest.delete(`/api/v1/workspace/${workspaceId}/environments/${id}`);
data: { environmentSlug }
});
}, },
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace); queryClient.invalidateQueries(workspaceKeys.getAllUserWorkspace);
@@ -328,14 +326,20 @@ export const useDeleteUserFromWorkspace = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async (membershipId: string) => { mutationFn: async ({
membershipId,
workspaceId
}: {
workspaceId: string;
membershipId: string;
}) => {
const { const {
data: { deletedMembership } data: { deletedMembership }
} = await apiRequest.delete(`/api/v1/membership/${membershipId}`); } = await apiRequest.delete(`/api/v1/${workspaceId}/membership/${membershipId}`);
return deletedMembership; return deletedMembership;
}, },
onSuccess: (res) => { onSuccess: (_, { workspaceId }) => {
queryClient.invalidateQueries(workspaceKeys.getWorkspaceUsers(res.workspace)); queryClient.invalidateQueries(workspaceKeys.getWorkspaceUsers(workspaceId));
} }
}); });
}; };
@@ -343,10 +347,18 @@ export const useDeleteUserFromWorkspace = () => {
export const useUpdateUserWorkspaceRole = () => { export const useUpdateUserWorkspaceRole = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async ({ membershipId, role }: { membershipId: string; role: string }) => { mutationFn: async ({
membershipId,
role,
workspaceId
}: {
membershipId: string;
role: string;
workspaceId: string;
}) => {
const { const {
data: { membership } data: { membership }
} = await apiRequest.post(`/api/v1/membership/${membershipId}/change-role`, { } = await apiRequest.post(`/api/v1/${workspaceId}/membership/${membershipId}`, {
role role
}); });
return membership; return membership;
@@ -369,13 +381,15 @@ export const useAddIdentityToWorkspace = () => {
workspaceId: string; workspaceId: string;
role?: string; role?: string;
}) => { }) => {
const { const {
data: { identityMembership } data: { identityMembership }
} = await apiRequest.post(`/api/v2/workspace/${workspaceId}/identity-memberships/${identityId}`, { } = await apiRequest.post(
role `/api/v2/workspace/${workspaceId}/identity-memberships/${identityId}`,
}); {
role
}
);
return identityMembership; return identityMembership;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { workspaceId }) => {
@@ -396,12 +410,14 @@ export const useUpdateIdentityWorkspaceRole = () => {
workspaceId: string; workspaceId: string;
role?: string; role?: string;
}) => { }) => {
const { const {
data: { identityMembership } data: { identityMembership }
} = await apiRequest.patch(`/api/v2/workspace/${workspaceId}/identity-memberships/${identityId}`, { } = await apiRequest.patch(
role `/api/v2/workspace/${workspaceId}/identity-memberships/${identityId}`,
}); {
role
}
);
return identityMembership; return identityMembership;
}, },
@@ -416,14 +432,16 @@ export const useDeleteIdentityFromWorkspace = () => {
return useMutation({ return useMutation({
mutationFn: async ({ mutationFn: async ({
identityId, identityId,
workspaceId, workspaceId
}: { }: {
identityId: string; identityId: string;
workspaceId: string; workspaceId: string;
}) => { }) => {
const { const {
data: { identityMembership } data: { identityMembership }
} = await apiRequest.delete(`/api/v2/workspace/${workspaceId}/identity-memberships/${identityId}`); } = await apiRequest.delete(
`/api/v2/workspace/${workspaceId}/identity-memberships/${identityId}`
);
return identityMembership; return identityMembership;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { workspaceId }) => {
@@ -445,4 +463,4 @@ export const useGetWorkspaceIdentityMemberships = (workspaceId: string) => {
}, },
enabled: true enabled: true
}); });
}; };
+10 -9
View File
@@ -8,6 +8,7 @@ export type Workspace = {
}; };
export type WorkspaceEnv = { export type WorkspaceEnv = {
id: string;
name: string; name: string;
slug: string; slug: string;
isReadDenied: boolean; isReadDenied: boolean;
@@ -36,13 +37,13 @@ export type ToggleAutoCapitalizationDTO = { workspaceID: string; state: boolean
export type DeleteWorkspaceDTO = { workspaceID: string }; export type DeleteWorkspaceDTO = { workspaceID: string };
export type CreateEnvironmentDTO = { export type CreateEnvironmentDTO = {
workspaceID: string; workspaceId: string;
environmentSlug: string; name: string;
environmentName: string; slug: string;
}; };
export type ReorderEnvironmentsDTO = { export type ReorderEnvironmentsDTO = {
workspaceID: string; workspaceId: string;
environmentSlug: string; environmentSlug: string;
environmentName: string; environmentName: string;
otherEnvironmentSlug: string; otherEnvironmentSlug: string;
@@ -50,10 +51,10 @@ export type ReorderEnvironmentsDTO = {
}; };
export type UpdateEnvironmentDTO = { export type UpdateEnvironmentDTO = {
workspaceID: string; workspaceId: string;
oldEnvironmentSlug: string; id: string;
environmentSlug: string; name?: string;
environmentName: string; slug?: string;
}; };
export type DeleteEnvironmentDTO = { workspaceID: string; environmentSlug: string }; export type DeleteEnvironmentDTO = { workspaceId: string; id: string };
@@ -142,7 +142,7 @@ export const MemberListTab = () => {
if (!currentOrg?.id) return; if (!currentOrg?.id) return;
try { try {
await removeUserFromWorkspace(membershipId); await removeUserFromWorkspace({ workspaceId, membershipId });
createNotification({ createNotification({
text: "Successfully removed user from workspace", text: "Successfully removed user from workspace",
type: "success" type: "success"
@@ -182,7 +182,7 @@ export const MemberListTab = () => {
return; return;
} }
await updateUserWorkspaceRole({ membershipId, role }); await updateUserWorkspaceRole({ membershipId, role, workspaceId });
createNotification({ createNotification({
text: "Successfully updated user role", text: "Successfully updated user role",
type: "success" type: "success"
@@ -34,9 +34,9 @@ export const AddEnvironmentModal = ({ popUp, handlePopUpClose, handlePopUpToggle
if (!currentWorkspace?.id) return; if (!currentWorkspace?.id) return;
await mutateAsync({ await mutateAsync({
workspaceID: currentWorkspace.id, workspaceId: currentWorkspace.id,
environmentName, name: environmentName,
environmentSlug slug: environmentSlug
}); });
createNotification({ createNotification({
@@ -38,13 +38,13 @@ export const EnvironmentSection = () => {
"upgradePlan" "upgradePlan"
] as const); ] as const);
const onEnvDeleteSubmit = async (environmentSlug: string) => { const onEnvDeleteSubmit = async (id: string) => {
try { try {
if (!currentWorkspace?.id) return; if (!currentWorkspace?.id) return;
await deleteWsEnvironment.mutateAsync({ await deleteWsEnvironment.mutateAsync({
workspaceID: currentWorkspace.id, workspaceId: currentWorkspace.id,
environmentSlug id
}); });
createNotification({ createNotification({
@@ -116,9 +116,7 @@ export const EnvironmentSection = () => {
}?`} }?`}
onChange={(isOpen) => handlePopUpToggle("deleteEnv", isOpen)} onChange={(isOpen) => handlePopUpToggle("deleteEnv", isOpen)}
deleteKey={(popUp?.deleteEnv?.data as { slug: string })?.slug || ""} deleteKey={(popUp?.deleteEnv?.data as { slug: string })?.slug || ""}
onDeleteApproved={() => onDeleteApproved={() => onEnvDeleteSubmit((popUp?.deleteEnv?.data as { id: string })?.id)}
onEnvDeleteSubmit((popUp?.deleteEnv?.data as { slug: string })?.slug)
}
/> />
<UpgradePlanModal <UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen} isOpen={popUp.upgradePlan.isOpen}
@@ -24,10 +24,12 @@ type Props = {
popUpName: keyof UsePopUpState<["updateEnv", "deleteEnv", "upgradePlan"]>, popUpName: keyof UsePopUpState<["updateEnv", "deleteEnv", "upgradePlan"]>,
{ {
name, name,
slug slug,
id
}: { }: {
name: string; name: string;
slug: string; slug: string;
id: string;
} }
) => void; ) => void;
}; };
@@ -56,7 +58,7 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
const indexToSwap = shouldMoveUp ? indexOfEnv - 1 : indexOfEnv + 1; const indexToSwap = shouldMoveUp ? indexOfEnv - 1 : indexOfEnv + 1;
await reorderWsEnvironment.mutateAsync({ await reorderWsEnvironment.mutateAsync({
workspaceID: currentWorkspace.id, workspaceId: currentWorkspace.id,
environmentSlug: slug, environmentSlug: slug,
environmentName: name, environmentName: name,
otherEnvironmentSlug: currentWorkspace.environments[indexToSwap].slug, otherEnvironmentSlug: currentWorkspace.environments[indexToSwap].slug,
@@ -90,7 +92,7 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
{isLoading && <TableSkeleton columns={3} innerKey="project-envs" />} {isLoading && <TableSkeleton columns={3} innerKey="project-envs" />}
{!isLoading && {!isLoading &&
currentWorkspace && currentWorkspace &&
currentWorkspace.environments.map(({ name, slug }, pos) => ( currentWorkspace.environments.map(({ name, slug, id }, pos) => (
<Tr key={name}> <Tr key={name}>
<Td>{name}</Td> <Td>{name}</Td>
<Td>{slug}</Td> <Td>{slug}</Td>
@@ -142,7 +144,7 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
<IconButton <IconButton
className="mr-3 py-2" className="mr-3 py-2"
onClick={() => { onClick={() => {
handlePopUpOpen("updateEnv", { name, slug }); handlePopUpOpen("updateEnv", { name, slug, id });
}} }}
isDisabled={!isAllowed} isDisabled={!isAllowed}
colorSchema="primary" colorSchema="primary"
@@ -160,7 +162,7 @@ export const EnvironmentTable = ({ handlePopUpOpen }: Props) => {
{(isAllowed) => ( {(isAllowed) => (
<IconButton <IconButton
onClick={() => { onClick={() => {
handlePopUpOpen("deleteEnv", { name, slug }); handlePopUpOpen("deleteEnv", { name, slug, id });
}} }}
size="lg" size="lg"
colorSchema="danger" colorSchema="danger"
@@ -29,17 +29,17 @@ export const UpdateEnvironmentModal = ({ popUp, handlePopUpClose, handlePopUpTog
resolver: yupResolver(schema) 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) => { const onFormSubmit = async ({ environmentName, environmentSlug }: FormData) => {
try { try {
if (!currentWorkspace?.id) return; if (!currentWorkspace?.id) return;
await mutateAsync({ await mutateAsync({
workspaceID: currentWorkspace.id, workspaceId: currentWorkspace.id,
environmentName, name: environmentName,
environmentSlug, slug: environmentSlug,
oldEnvironmentSlug id: oldEnvId
}); });
createNotification({ createNotification({
+1 -2
View File
@@ -23,8 +23,7 @@
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve", "jsx": "preserve"
"incremental": true
}, },
"include": [ "include": [
"next-i18next.config.js", "next-i18next.config.js",