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