feat: completed all workspace prefix routes

This commit is contained in:
=
2025-09-16 00:08:40 +05:30
parent cd2a39b374
commit fd2b067e14
24 changed files with 1207 additions and 316 deletions
@@ -146,7 +146,7 @@ export enum EventType {
MOVE_SECRETS = "move-secrets", MOVE_SECRETS = "move-secrets",
DELETE_SECRET = "delete-secret", DELETE_SECRET = "delete-secret",
DELETE_SECRETS = "delete-secrets", DELETE_SECRETS = "delete-secrets",
GET_WORKSPACE_KEY = "get-workspace-key", GET_PROJECT_KEY = "get-project-key",
AUTHORIZE_INTEGRATION = "authorize-integration", AUTHORIZE_INTEGRATION = "authorize-integration",
UPDATE_INTEGRATION_AUTH = "update-integration-auth", UPDATE_INTEGRATION_AUTH = "update-integration-auth",
UNAUTHORIZE_INTEGRATION = "unauthorize-integration", UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
@@ -249,9 +249,9 @@ export enum EventType {
UPDATE_ENVIRONMENT = "update-environment", UPDATE_ENVIRONMENT = "update-environment",
DELETE_ENVIRONMENT = "delete-environment", DELETE_ENVIRONMENT = "delete-environment",
GET_ENVIRONMENT = "get-environment", GET_ENVIRONMENT = "get-environment",
ADD_WORKSPACE_MEMBER = "add-workspace-member", ADD_PROJECT_MEMBER = "add-project-member",
ADD_BATCH_WORKSPACE_MEMBER = "add-workspace-members", ADD_BATCH_PROJECT_MEMBER = "add-project-members",
REMOVE_WORKSPACE_MEMBER = "remove-workspace-member", REMOVE_PROJECT_MEMBER = "remove-project-member",
CREATE_FOLDER = "create-folder", CREATE_FOLDER = "create-folder",
UPDATE_FOLDER = "update-folder", UPDATE_FOLDER = "update-folder",
DELETE_FOLDER = "delete-folder", DELETE_FOLDER = "delete-folder",
@@ -264,8 +264,8 @@ export enum EventType {
CREATE_SECRET_IMPORT = "create-secret-import", CREATE_SECRET_IMPORT = "create-secret-import",
UPDATE_SECRET_IMPORT = "update-secret-import", UPDATE_SECRET_IMPORT = "update-secret-import",
DELETE_SECRET_IMPORT = "delete-secret-import", DELETE_SECRET_IMPORT = "delete-secret-import",
UPDATE_USER_WORKSPACE_ROLE = "update-user-workspace-role", UPDATE_USER_PROJECT_ROLE = "update-user-project-role",
UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS = "update-user-workspace-denied-permissions", UPDATE_USER_PROJECT_DENIED_PERMISSIONS = "update-user-project-denied-permissions",
SECRET_APPROVAL_MERGED = "secret-approval-merged", SECRET_APPROVAL_MERGED = "secret-approval-merged",
SECRET_APPROVAL_REQUEST = "secret-approval-request", SECRET_APPROVAL_REQUEST = "secret-approval-request",
SECRET_APPROVAL_CLOSED = "secret-approval-closed", SECRET_APPROVAL_CLOSED = "secret-approval-closed",
@@ -664,8 +664,8 @@ interface DeleteSecretBatchEvent {
}; };
} }
interface GetWorkspaceKeyEvent { interface GetProjectKeyEvent {
type: EventType.GET_WORKSPACE_KEY; type: EventType.GET_PROJECT_KEY;
metadata: { metadata: {
keyId: string; keyId: string;
}; };
@@ -1557,24 +1557,24 @@ interface DeleteEnvironmentEvent {
}; };
} }
interface AddWorkspaceMemberEvent { interface AddProjectMemberEvent {
type: EventType.ADD_WORKSPACE_MEMBER; type: EventType.ADD_PROJECT_MEMBER;
metadata: { metadata: {
userId: string; userId: string;
email: string; email: string;
}; };
} }
interface AddBatchWorkspaceMemberEvent { interface AddBatchProjectMemberEvent {
type: EventType.ADD_BATCH_WORKSPACE_MEMBER; type: EventType.ADD_BATCH_PROJECT_MEMBER;
metadata: Array<{ metadata: Array<{
userId: string; userId: string;
email: string; email: string;
}>; }>;
} }
interface RemoveWorkspaceMemberEvent { interface RemoveProjectMemberEvent {
type: EventType.REMOVE_WORKSPACE_MEMBER; type: EventType.REMOVE_PROJECT_MEMBER;
metadata: { metadata: {
userId: string; userId: string;
email: string; email: string;
@@ -1713,7 +1713,7 @@ interface DeleteSecretImportEvent {
} }
interface UpdateUserRole { interface UpdateUserRole {
type: EventType.UPDATE_USER_WORKSPACE_ROLE; type: EventType.UPDATE_USER_PROJECT_ROLE;
metadata: { metadata: {
userId: string; userId: string;
email: string; email: string;
@@ -1723,7 +1723,7 @@ interface UpdateUserRole {
} }
interface UpdateUserDeniedPermissions { interface UpdateUserDeniedPermissions {
type: EventType.UPDATE_USER_WORKSPACE_DENIED_PERMISSIONS; type: EventType.UPDATE_USER_PROJECT_DENIED_PERMISSIONS;
metadata: { metadata: {
userId: string; userId: string;
email: string; email: string;
@@ -3477,7 +3477,7 @@ export type Event =
| MoveSecretsEvent | MoveSecretsEvent
| DeleteSecretEvent | DeleteSecretEvent
| DeleteSecretBatchEvent | DeleteSecretBatchEvent
| GetWorkspaceKeyEvent | GetProjectKeyEvent
| AuthorizeIntegrationEvent | AuthorizeIntegrationEvent
| UpdateIntegrationAuthEvent | UpdateIntegrationAuthEvent
| UnauthorizeIntegrationEvent | UnauthorizeIntegrationEvent
@@ -3566,9 +3566,9 @@ export type Event =
| GetEnvironmentEvent | GetEnvironmentEvent
| UpdateEnvironmentEvent | UpdateEnvironmentEvent
| DeleteEnvironmentEvent | DeleteEnvironmentEvent
| AddWorkspaceMemberEvent | AddProjectMemberEvent
| AddBatchWorkspaceMemberEvent | AddBatchProjectMemberEvent
| RemoveWorkspaceMemberEvent | RemoveProjectMemberEvent
| CreateFolderEvent | CreateFolderEvent
| UpdateFolderEvent | UpdateFolderEvent
| DeleteFolderEvent | DeleteFolderEvent
@@ -240,7 +240,7 @@ export const registerDepreciatedProjectMembershipRouter = async (server: Fastify
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
...req.auditLogInfo, ...req.auditLogInfo,
event: { event: {
type: EventType.ADD_BATCH_WORKSPACE_MEMBER, type: EventType.ADD_BATCH_PROJECT_MEMBER,
metadata: data.map(({ userId }) => ({ metadata: data.map(({ userId }) => ({
userId: userId || "", userId: userId || "",
email: "" email: ""
@@ -365,7 +365,7 @@ export const registerDepreciatedProjectMembershipRouter = async (server: Fastify
...req.auditLogInfo, ...req.auditLogInfo,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
event: { event: {
type: EventType.REMOVE_WORKSPACE_MEMBER, type: EventType.REMOVE_PROJECT_MEMBER,
metadata: { metadata: {
userId: membership.userId, userId: membership.userId,
email: "" email: ""
+6 -2
View File
@@ -15,9 +15,11 @@ import { registerCertRouter } from "./certificate-router";
import { registerCertificateTemplateRouter } from "./certificate-template-router"; import { registerCertificateTemplateRouter } from "./certificate-template-router";
import { registerDepreciatedProjectEnvRouter } from "./depreciated-project-env-router"; import { registerDepreciatedProjectEnvRouter } from "./depreciated-project-env-router";
import { registerDepreciatedProjectMembershipRouter } from "./depreciated-project-membership-router"; import { registerDepreciatedProjectMembershipRouter } from "./depreciated-project-membership-router";
import { registerDepreciatedProjectRouter } from "./depreciated-project-router";
import { registerDepreciatedSecretTagRouter } from "./depreciated-secret-tag-router"; import { registerDepreciatedSecretTagRouter } from "./depreciated-secret-tag-router";
import { registerEventRouter } from "./event-router"; import { registerEventRouter } from "./event-router";
import { registerExternalGroupOrgRoleMappingRouter } from "./external-group-org-role-mapping-router"; import { registerExternalGroupOrgRoleMappingRouter } from "./external-group-org-role-mapping-router";
import { registerGroupProjectRouter } from "./group-project-router";
import { registerIdentityAccessTokenRouter } from "./identity-access-token-router"; import { registerIdentityAccessTokenRouter } from "./identity-access-token-router";
import { registerIdentityAliCloudAuthRouter } from "./identity-alicloud-auth-router"; import { registerIdentityAliCloudAuthRouter } from "./identity-alicloud-auth-router";
import { registerIdentityAwsAuthRouter } from "./identity-aws-iam-auth-router"; import { registerIdentityAwsAuthRouter } from "./identity-aws-iam-auth-router";
@@ -46,10 +48,10 @@ import { registerPkiSubscriberRouter } from "./pki-subscriber-router";
import { registerProjectEnvRouter } from "./project-env-router"; import { registerProjectEnvRouter } from "./project-env-router";
import { registerProjectKeyRouter } from "./project-key-router"; import { registerProjectKeyRouter } from "./project-key-router";
import { registerProjectMembershipRouter } from "./project-membership-router"; import { registerProjectMembershipRouter } from "./project-membership-router";
import { registerDepreciatedProjectRouter } from "./depreciated-project-router";
import { registerProjectRouter } from "./project-router"; import { registerProjectRouter } from "./project-router";
import { SECRET_REMINDER_REGISTER_ROUTER_MAP } from "./reminder-routers"; import { SECRET_REMINDER_REGISTER_ROUTER_MAP } from "./reminder-routers";
import { registerSecretFolderRouter } from "./secret-folder-router"; import { registerSecretFolderRouter } from "./secret-folder-router";
import { registerSecretImportRouter } from "./secret-import-router";
import { registerSecretRequestsRouter } from "./secret-requests-router"; import { registerSecretRequestsRouter } from "./secret-requests-router";
import { registerSecretSharingRouter } from "./secret-sharing-router"; import { registerSecretSharingRouter } from "./secret-sharing-router";
import { registerSecretTagRouter } from "./secret-tag-router"; import { registerSecretTagRouter } from "./secret-tag-router";
@@ -60,7 +62,7 @@ import { registerUserEngagementRouter } from "./user-engagement-router";
import { registerUserRouter } from "./user-router"; import { registerUserRouter } from "./user-router";
import { registerWebhookRouter } from "./webhook-router"; import { registerWebhookRouter } from "./webhook-router";
import { registerWorkflowIntegrationRouter } from "./workflow-integration-router"; import { registerWorkflowIntegrationRouter } from "./workflow-integration-router";
import { registerSecretImportRouter } from "./secret-import-router"; import { registerIdentityProjectRouter } from "./identity-project-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" });
@@ -121,6 +123,8 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
await projectRouter.register(registerProjectMembershipRouter); await projectRouter.register(registerProjectMembershipRouter);
await projectRouter.register(registerProjectEnvRouter); await projectRouter.register(registerProjectEnvRouter);
await projectRouter.register(registerSecretTagRouter); await projectRouter.register(registerSecretTagRouter);
await projectRouter.register(registerGroupProjectRouter);
await projectRouter.register(registerIdentityProjectRouter);
}, },
{ prefix: "/projects" } { prefix: "/projects" }
); );
@@ -1,7 +1,8 @@
import { z } from "zod"; import { z } from "zod";
import { import {
OrgMembershipsSchema, OrgMembershipRole,
ProjectMembershipRole,
ProjectMembershipsSchema, ProjectMembershipsSchema,
ProjectUserMembershipRolesSchema, ProjectUserMembershipRolesSchema,
UserEncryptionKeysSchema, UserEncryptionKeysSchema,
@@ -206,50 +207,72 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
rateLimit: writeLimit rateLimit: writeLimit
}, },
schema: { schema: {
hide: false,
tags: [ApiDocsTags.ProjectUsers],
description: "Invite members to project",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
projectId: z.string().trim() projectId: z.string().describe(PROJECT_USERS.INVITE_MEMBER.projectId)
}), }),
body: z.object({ body: z.object({
members: z emails: z
.object({ .string()
orgMembershipId: z.string().trim(), .email()
workspaceEncryptedKey: z.string().trim(),
workspaceEncryptedNonce: z.string().trim()
})
.array() .array()
.min(1) .default([])
.describe(PROJECT_USERS.INVITE_MEMBER.emails)
.refine((val) => val.every((el) => el === el.toLowerCase()), "Email must be lowercase"),
usernames: z
.string()
.array()
.default([])
.describe(PROJECT_USERS.INVITE_MEMBER.usernames)
.refine((val) => val.every((el) => el === el.toLowerCase()), "Username must be lowercase"),
roleSlugs: z.string().array().min(1).optional().describe(PROJECT_USERS.INVITE_MEMBER.roleSlugs)
}), }),
response: { response: {
200: z.object({ 200: z.object({
success: z.boolean(), memberships: ProjectMembershipsSchema.array()
data: OrgMembershipsSchema.array()
}) })
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const data = await server.services.projectMembership.addUsersToProject({ const usernamesAndEmails = [...req.body.emails, ...req.body.usernames];
actorId: req.permission.id, const { projectMemberships: memberships } = await server.services.org.inviteUserToOrganization({
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
projectId: req.params.projectId, actor: req.permission.type,
members: req.body.members inviteeEmails: usernamesAndEmails,
orgId: req.permission.orgId,
organizationRoleSlug: OrgMembershipRole.NoAccess,
projects: [
{
id: req.params.projectId,
projectRoleSlug: req.body.roleSlugs || [ProjectMembershipRole.Member]
}
]
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
projectId: req.params.projectId, projectId: req.params.projectId,
...req.auditLogInfo, ...req.auditLogInfo,
event: { event: {
type: EventType.ADD_BATCH_WORKSPACE_MEMBER, type: EventType.ADD_BATCH_PROJECT_MEMBER,
metadata: data.map(({ userId }) => ({ metadata: memberships.map(({ userId, id }) => ({
userId: userId || "", userId: userId || "",
membershipId: id,
email: "" email: ""
})) }))
} }
}); });
return { data, success: true }; return { memberships };
} }
}); });
@@ -311,23 +334,79 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
roles: req.body.roles roles: req.body.roles
}); });
// await server.services.auditLog.createAuditLog({
// ...req.auditLogInfo,
// projectId: req.params.projectId,
// event: {
// type: EventType.UPDATE_USER_WORKSPACE_ROLE,
// metadata: {
// userId: membership.userId,
// newRole: req.body.role,
// oldRole: membership.role,
// email: ""
// }
// }
// });
return { roles }; return { roles };
} }
}); });
server.route({
method: "DELETE",
url: "/:projectId/memberships",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.ProjectUsers],
description: "Remove members from project",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().describe(PROJECT_USERS.REMOVE_MEMBER.projectId)
}),
body: z.object({
emails: z
.string()
.email()
.array()
.default([])
.describe(PROJECT_USERS.REMOVE_MEMBER.emails)
.refine((val) => val.every((el) => el === el.toLowerCase()), "Email must be lowercase"),
usernames: z
.string()
.array()
.default([])
.describe(PROJECT_USERS.REMOVE_MEMBER.usernames)
.refine((val) => val.every((el) => el === el.toLowerCase()), "Username must be lowercase")
}),
response: {
200: z.object({
memberships: ProjectMembershipsSchema.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const memberships = await server.services.projectMembership.deleteProjectMemberships({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.projectId,
emails: req.body.emails,
usernames: req.body.usernames
});
for (const membership of memberships) {
// eslint-disable-next-line no-await-in-loop
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.params.projectId,
event: {
type: EventType.REMOVE_PROJECT_MEMBER,
metadata: {
userId: membership.userId,
email: ""
}
}
});
}
return { memberships };
}
});
server.route({ server.route({
method: "DELETE", method: "DELETE",
url: "/:projectId/memberships/:membershipId", url: "/:projectId/memberships/:membershipId",
@@ -366,7 +445,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
...req.auditLogInfo, ...req.auditLogInfo,
projectId: req.params.projectId, projectId: req.params.projectId,
event: { event: {
type: EventType.REMOVE_WORKSPACE_MEMBER, type: EventType.REMOVE_PROJECT_MEMBER,
metadata: { metadata: {
userId: membership.userId, userId: membership.userId,
email: "" email: ""
+233 -1
View File
@@ -29,6 +29,7 @@ import { WorkflowIntegration } from "@app/services/workflow-integration/workflow
import { integrationAuthPubSchema, SanitizedProjectSchema } from "../sanitizedSchemas"; import { integrationAuthPubSchema, SanitizedProjectSchema } from "../sanitizedSchemas";
import { sanitizedServiceTokenSchema } from "../v2/service-token-router"; import { sanitizedServiceTokenSchema } from "../v2/service-token-router";
import { slugSchema } from "@app/server/lib/schemas";
const projectWithEnv = SanitizedProjectSchema.merge( const projectWithEnv = SanitizedProjectSchema.merge(
z.object({ z.object({
@@ -117,6 +118,83 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
} }
}); });
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.Projects],
description: "Create a new project",
security: [
{
bearerAuth: []
}
],
body: z.object({
projectName: z.string().trim().describe(PROJECTS.CREATE.projectName),
projectDescription: z.string().trim().optional().describe(PROJECTS.CREATE.projectDescription),
slug: slugSchema({ min: 5, max: 36 }).optional().describe(PROJECTS.CREATE.slug),
kmsKeyId: z.string().optional(),
template: slugSchema({ field: "Template Name", max: 64 })
.optional()
.default(InfisicalProjectTemplate.Default)
.describe(PROJECTS.CREATE.template),
type: z.nativeEnum(ProjectType).default(ProjectType.SecretManager),
shouldCreateDefaultEnvs: z.boolean().optional().default(true)
}),
response: {
200: z.object({
project: projectWithEnv
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const project = await server.services.project.createProject({
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
workspaceName: req.body.projectName,
workspaceDescription: req.body.projectDescription,
slug: req.body.slug,
kmsKeyId: req.body.kmsKeyId,
template: req.body.template,
type: req.body.type,
createDefaultEnvs: req.body.shouldCreateDefaultEnvs
});
await server.services.telemetry.sendPostHogEvents({
event: PostHogEventTypes.ProjectCreated,
distinctId: getTelemetryDistinctId(req),
organizationId: req.permission.orgId,
properties: {
orgId: project.orgId,
name: project.name,
...req.auditLogInfo
}
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
projectId: project.id,
event: {
type: EventType.CREATE_PROJECT,
metadata: {
...req.body,
name: req.body.projectName
}
}
});
return { project };
}
});
server.route({ server.route({
method: "GET", method: "GET",
url: "/", url: "/",
@@ -195,6 +273,47 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
} }
}); });
// TODO(depri): confirm with the team
server.route({
method: "GET",
url: "/slug/:slug",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.Projects],
description: "Get project details by slug",
security: [
{
bearerAuth: []
}
],
params: z.object({
slug: slugSchema({ max: 36 }).describe("The slug of the project to get.")
}),
response: {
200: projectWithEnv
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const project = await server.services.project.getAProject({
filter: {
slug: req.params.slug,
orgId: req.permission.orgId,
type: ProjectFilterType.SLUG
},
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
actor: req.permission.type
});
return project;
}
});
server.route({ server.route({
method: "DELETE", method: "DELETE",
url: "/:projectId", url: "/:projectId",
@@ -246,6 +365,58 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
} }
}); });
/* Delete a project by slug */
server.route({
method: "DELETE",
url: "/slug/:slug",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.Projects],
description: "Delete project",
security: [
{
bearerAuth: []
}
],
params: z.object({
slug: slugSchema({ min: 5, max: 36 }).describe("The slug of the project to delete.")
}),
response: {
200: SanitizedProjectSchema
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const project = await server.services.project.deleteProject({
filter: {
type: ProjectFilterType.SLUG,
slug: req.params.slug,
orgId: req.permission.orgId
},
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
actor: req.permission.type
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
projectId: project.id,
event: {
type: EventType.DELETE_PROJECT,
metadata: project
}
});
return project;
}
});
server.route({ server.route({
method: "PATCH", method: "PATCH",
url: "/:projectId", url: "/:projectId",
@@ -862,7 +1033,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
params: z.object({ params: z.object({
projectId: z.string().trim() projectId: z.string().trim()
}), }),
body: z.discriminatedUnion("integration", [ body: z.discriminatedUnion("integration", [
z.object({ z.object({
integration: z.literal(WorkflowIntegration.SLACK), integration: z.literal(WorkflowIntegration.SLACK),
@@ -1071,4 +1241,66 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
return { message: "Project access request has been send to project admins" }; return { message: "Project access request has been send to project admins" };
} }
}); });
/* Start upgrade of a project */
server.route({
method: "POST",
url: "/:projectId/upgrade",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
projectId: z.string().trim()
}),
body: z.object({
userPrivateKey: z.string().trim()
}),
response: {
200: z.void()
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
await server.services.project.upgradeProject({
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
projectId: req.params.projectId,
userPrivateKey: req.body.userPrivateKey
});
}
});
/* Get upgrade status of project */
server.route({
url: "/:projectId/upgrade/status",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
projectId: z.string().trim()
}),
response: {
200: z.object({
status: z.string().nullable()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const status = await server.services.project.getProjectUpgradeStatus({
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.projectId,
actor: req.permission.type,
actorId: req.permission.id
});
return { status };
}
});
}; };
@@ -0,0 +1,363 @@
import { z } from "zod";
import {
GroupProjectMembershipsSchema,
GroupsSchema,
ProjectMembershipRole,
ProjectUserMembershipRolesSchema,
UsersSchema
} from "@app/db/schemas";
import { EFilterReturnedUsers } from "@app/ee/services/group/group-types";
import { ApiDocsTags, GROUPS, PROJECTS } from "@app/lib/api-docs";
import { ms } from "@app/lib/ms";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
export const registerDepreciatedGroupProjectRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/:projectId/groups/:groupIdOrName",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.ProjectGroups],
description: "Add group to project",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim().describe(PROJECTS.ADD_GROUP_TO_PROJECT.projectId),
groupIdOrName: z.string().trim().describe(PROJECTS.ADD_GROUP_TO_PROJECT.groupIdOrName)
}),
body: z
.object({
role: z
.string()
.trim()
.min(1)
.default(ProjectMembershipRole.NoAccess)
.describe(PROJECTS.ADD_GROUP_TO_PROJECT.role),
roles: z
.array(
z.union([
z.object({
role: z.string(),
isTemporary: z.literal(false).default(false)
}),
z.object({
role: z.string(),
isTemporary: z.literal(true),
temporaryMode: z.nativeEnum(ProjectUserMembershipTemporaryMode),
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
temporaryAccessStartTime: z.string().datetime()
})
])
)
.optional()
})
.refine((data) => data.role || data.roles, {
message: "Either role or roles must be present",
path: ["role", "roles"]
}),
response: {
200: z.object({
groupMembership: GroupProjectMembershipsSchema
})
}
},
handler: async (req) => {
const groupMembership = await server.services.groupProject.addGroupToProject({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
roles: req.body.roles || [{ role: req.body.role }],
projectId: req.params.projectId,
groupIdOrName: req.params.groupIdOrName
});
return { groupMembership };
}
});
server.route({
method: "PATCH",
url: "/:projectId/groups/:groupId",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.ProjectGroups],
description: "Update group in project",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim().describe(PROJECTS.UPDATE_GROUP_IN_PROJECT.projectId),
groupId: z.string().trim().describe(PROJECTS.UPDATE_GROUP_IN_PROJECT.groupId)
}),
body: z.object({
roles: z
.array(
z.union([
z.object({
role: z.string(),
isTemporary: z.literal(false).default(false)
}),
z.object({
role: z.string(),
isTemporary: z.literal(true),
temporaryMode: z.nativeEnum(ProjectUserMembershipTemporaryMode),
temporaryRange: z.string().refine((val) => ms(val) > 0, "Temporary range must be a positive number"),
temporaryAccessStartTime: z.string().datetime()
})
])
)
.min(1)
.describe(PROJECTS.UPDATE_GROUP_IN_PROJECT.roles)
}),
response: {
200: z.object({
roles: ProjectUserMembershipRolesSchema.array()
})
}
},
handler: async (req) => {
const roles = await server.services.groupProject.updateGroupInProject({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.projectId,
groupId: req.params.groupId,
roles: req.body.roles
});
return { roles };
}
});
server.route({
method: "DELETE",
url: "/:projectId/groups/:groupId",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.ProjectGroups],
description: "Remove group from project",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim().describe(PROJECTS.REMOVE_GROUP_FROM_PROJECT.projectId),
groupId: z.string().trim().describe(PROJECTS.REMOVE_GROUP_FROM_PROJECT.groupId)
}),
response: {
200: z.object({
groupMembership: GroupProjectMembershipsSchema
})
}
},
handler: async (req) => {
const groupMembership = await server.services.groupProject.removeGroupFromProject({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
groupId: req.params.groupId,
projectId: req.params.projectId
});
return { groupMembership };
}
});
server.route({
method: "GET",
url: "/:projectId/groups",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.ProjectGroups],
description: "Return list of groups in project",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim().describe(PROJECTS.LIST_GROUPS_IN_PROJECT.projectId)
}),
response: {
200: z.object({
groupMemberships: z
.object({
id: z.string(),
groupId: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
roles: z.array(
z.object({
id: z.string(),
role: z.string(),
customRoleId: z.string().optional().nullable(),
customRoleName: z.string().optional().nullable(),
customRoleSlug: z.string().optional().nullable(),
isTemporary: z.boolean(),
temporaryMode: z.string().optional().nullable(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional()
})
),
group: GroupsSchema.pick({ name: true, id: true, slug: true })
})
.array()
})
}
},
handler: async (req) => {
const groupMemberships = await server.services.groupProject.listGroupsInProject({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.projectId
});
return { groupMemberships };
}
});
server.route({
method: "GET",
url: "/:projectId/groups/:groupId",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.ProjectGroups],
description: "Return project group",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim(),
groupId: z.string().trim()
}),
response: {
200: z.object({
groupMembership: z.object({
id: z.string(),
groupId: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
roles: z.array(
z.object({
id: z.string(),
role: z.string(),
customRoleId: z.string().optional().nullable(),
customRoleName: z.string().optional().nullable(),
customRoleSlug: z.string().optional().nullable(),
isTemporary: z.boolean(),
temporaryMode: z.string().optional().nullable(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional()
})
),
group: GroupsSchema.pick({ name: true, id: true, slug: true })
})
})
}
},
handler: async (req) => {
const groupMembership = await server.services.groupProject.getGroupInProject({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.params
});
return { groupMembership };
}
});
server.route({
method: "GET",
url: "/:projectId/groups/:groupId/users",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.ProjectGroups],
description: "Return project group users",
params: z.object({
projectId: z.string().trim().describe(GROUPS.LIST_USERS.projectId),
groupId: z.string().trim().describe(GROUPS.LIST_USERS.id)
}),
querystring: z.object({
offset: z.coerce.number().min(0).max(100).default(0).describe(GROUPS.LIST_USERS.offset),
limit: z.coerce.number().min(1).max(100).default(10).describe(GROUPS.LIST_USERS.limit),
username: z.string().trim().optional().describe(GROUPS.LIST_USERS.username),
search: z.string().trim().optional().describe(GROUPS.LIST_USERS.search),
filter: z.nativeEnum(EFilterReturnedUsers).optional().describe(GROUPS.LIST_USERS.filterUsers)
}),
response: {
200: z.object({
users: UsersSchema.pick({
email: true,
username: true,
firstName: true,
lastName: true,
id: true
})
.merge(
z.object({
isPartOfGroup: z.boolean(),
joinedGroupAt: z.date().nullable()
})
)
.array(),
totalCount: z.number()
})
}
},
handler: async (req) => {
const { users, totalCount } = await server.services.groupProject.listProjectGroupUsers({
id: req.params.groupId,
projectId: req.params.projectId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.query
});
return { users, totalCount };
}
});
};
@@ -0,0 +1,418 @@
import { z } from "zod";
import {
IdentitiesSchema,
IdentityProjectMembershipsSchema,
ProjectMembershipRole,
ProjectUserMembershipRolesSchema
} from "@app/db/schemas";
import { ApiDocsTags, ORGANIZATIONS, PROJECT_IDENTITIES } from "@app/lib/api-docs";
import { BadRequestError } from "@app/lib/errors";
import { ms } from "@app/lib/ms";
import { OrderByDirection } from "@app/lib/types";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { ProjectIdentityOrderBy } from "@app/services/identity-project/identity-project-types";
import { ProjectUserMembershipTemporaryMode } from "@app/services/project-membership/project-membership-types";
import { SanitizedProjectSchema } from "../sanitizedSchemas";
export const registerDepreciatedIdentityProjectRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/:projectId/identity-memberships/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.ProjectIdentities],
description: "Create project identity membership",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim(),
identityId: z.string().trim()
}),
body: z.object({
// @depreciated
role: z.string().trim().optional().default(ProjectMembershipRole.NoAccess),
roles: z
.array(
z.union([
z.object({
role: z.string().describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
isTemporary: z
.literal(false)
.default(false)
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role)
}),
z.object({
role: z.string().describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
isTemporary: z.literal(true).describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
temporaryMode: z
.nativeEnum(ProjectUserMembershipTemporaryMode)
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
temporaryRange: z
.string()
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role),
temporaryAccessStartTime: z
.string()
.datetime()
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.role)
})
])
)
.describe(PROJECT_IDENTITIES.CREATE_IDENTITY_MEMBERSHIP.roles.description)
.optional()
}),
response: {
200: z.object({
identityMembership: IdentityProjectMembershipsSchema
})
}
},
handler: async (req) => {
const { role, roles } = req.body;
if (!role && !roles) throw new BadRequestError({ message: "You must provide either role or roles field" });
const identityMembership = await server.services.identityProject.createProjectIdentity({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId,
projectId: req.params.projectId,
roles: roles || [{ role }]
});
return { identityMembership };
}
});
server.route({
method: "PATCH",
url: "/:projectId/identity-memberships/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.ProjectIdentities],
description: "Update project identity memberships",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim().describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.projectId),
identityId: z.string().trim().describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.identityId)
}),
body: z.object({
roles: z
.array(
z.union([
z.object({
role: z.string().describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.role),
isTemporary: z
.literal(false)
.default(false)
.describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.isTemporary)
}),
z.object({
role: z.string().describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.role),
isTemporary: z.literal(true).describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.isTemporary),
temporaryMode: z
.nativeEnum(ProjectUserMembershipTemporaryMode)
.describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.temporaryMode),
temporaryRange: z
.string()
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
.describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.temporaryRange),
temporaryAccessStartTime: z
.string()
.datetime()
.describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.temporaryAccessStartTime)
})
])
)
.min(1)
.describe(PROJECT_IDENTITIES.UPDATE_IDENTITY_MEMBERSHIP.roles.description)
}),
response: {
200: z.object({
roles: ProjectUserMembershipRolesSchema.array()
})
}
},
handler: async (req) => {
const roles = await server.services.identityProject.updateProjectIdentity({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId,
projectId: req.params.projectId,
roles: req.body.roles
});
return { roles };
}
});
server.route({
method: "DELETE",
url: "/:projectId/identity-memberships/:identityId",
config: {
rateLimit: writeLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.ProjectIdentities],
description: "Delete project identity memberships",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim().describe(PROJECT_IDENTITIES.DELETE_IDENTITY_MEMBERSHIP.projectId),
identityId: z.string().trim().describe(PROJECT_IDENTITIES.DELETE_IDENTITY_MEMBERSHIP.identityId)
}),
response: {
200: z.object({
identityMembership: IdentityProjectMembershipsSchema
})
}
},
handler: async (req) => {
const identityMembership = await server.services.identityProject.deleteProjectIdentity({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId,
projectId: req.params.projectId
});
return { identityMembership };
}
});
server.route({
method: "GET",
url: "/:projectId/identity-memberships",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.ProjectIdentities],
description: "Return project identity memberships",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim().describe(PROJECT_IDENTITIES.LIST_IDENTITY_MEMBERSHIPS.projectId)
}),
querystring: z.object({
offset: z.coerce
.number()
.min(0)
.default(0)
.describe(PROJECT_IDENTITIES.LIST_IDENTITY_MEMBERSHIPS.offset)
.optional(),
limit: z.coerce
.number()
.min(1)
.max(20000) // TODO: temp limit until combobox added to add identity to project modal, reduce once added
.default(100)
.describe(PROJECT_IDENTITIES.LIST_IDENTITY_MEMBERSHIPS.limit)
.optional(),
orderBy: z
.nativeEnum(ProjectIdentityOrderBy)
.default(ProjectIdentityOrderBy.Name)
.describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.orderBy)
.optional(),
orderDirection: z
.nativeEnum(OrderByDirection)
.default(OrderByDirection.ASC)
.describe(ORGANIZATIONS.LIST_IDENTITY_MEMBERSHIPS.orderDirection)
.optional(),
search: z.string().trim().describe(PROJECT_IDENTITIES.LIST_IDENTITY_MEMBERSHIPS.search).optional()
}),
response: {
200: z.object({
identityMemberships: z
.object({
id: z.string(),
identityId: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
roles: z.array(
z.object({
id: z.string(),
role: z.string(),
customRoleId: z.string().optional().nullable(),
customRoleName: z.string().optional().nullable(),
customRoleSlug: z.string().optional().nullable(),
isTemporary: z.boolean(),
temporaryMode: z.string().optional().nullable(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional()
})
),
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
authMethods: z.array(z.string())
}),
project: SanitizedProjectSchema.pick({ name: true, id: true })
})
.array(),
totalCount: z.number()
})
}
},
handler: async (req) => {
const { identityMemberships, totalCount } = await server.services.identityProject.listProjectIdentities({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.projectId,
limit: req.query.limit,
offset: req.query.offset,
orderBy: req.query.orderBy,
orderDirection: req.query.orderDirection,
search: req.query.search
});
return { identityMemberships, totalCount };
}
});
server.route({
method: "GET",
url: "/:projectId/identity-memberships/:identityId",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.ProjectIdentities],
description: "Return project identity membership",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim().describe(PROJECT_IDENTITIES.GET_IDENTITY_MEMBERSHIP_BY_ID.projectId),
identityId: z.string().trim().describe(PROJECT_IDENTITIES.GET_IDENTITY_MEMBERSHIP_BY_ID.identityId)
}),
response: {
200: z.object({
identityMembership: z.object({
id: z.string(),
identityId: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
roles: z.array(
z.object({
id: z.string(),
role: z.string(),
customRoleId: z.string().optional().nullable(),
customRoleName: z.string().optional().nullable(),
customRoleSlug: z.string().optional().nullable(),
isTemporary: z.boolean(),
temporaryMode: z.string().optional().nullable(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional()
})
),
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
authMethods: z.array(z.string())
}),
project: SanitizedProjectSchema.pick({ name: true, id: true })
})
})
}
},
handler: async (req) => {
const identityMembership = await server.services.identityProject.getProjectIdentityByIdentityId({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.projectId,
identityId: req.params.identityId
});
return { identityMembership };
}
});
server.route({
method: "GET",
url: "/identity-memberships/:identityMembershipId",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
hide: false,
tags: [ApiDocsTags.ProjectIdentities],
params: z.object({
identityMembershipId: z.string().trim()
}),
response: {
200: z.object({
identityMembership: z.object({
id: z.string(),
identityId: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
roles: z.array(
z.object({
id: z.string(),
role: z.string(),
customRoleId: z.string().optional().nullable(),
customRoleName: z.string().optional().nullable(),
customRoleSlug: z.string().optional().nullable(),
isTemporary: z.boolean(),
temporaryMode: z.string().optional().nullable(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional()
})
),
identity: IdentitiesSchema.pick({ name: true, id: true }).extend({
authMethods: z.array(z.string())
}),
project: SanitizedProjectSchema.pick({ name: true, id: true })
})
})
}
},
handler: async (req) => {
const identityMembership = await server.services.identityProject.getProjectIdentityByMembershipId({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
identityMembershipId: req.params.identityMembershipId
});
return { identityMembership };
}
});
};
@@ -7,7 +7,7 @@ import { writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
export const registerProjectMembershipRouter = async (server: FastifyZodProvider) => { export const registerDepreciatedProjectMembershipRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "POST", method: "POST",
url: "/:projectId/memberships", url: "/:projectId/memberships",
@@ -71,7 +71,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
projectId: req.params.projectId, projectId: req.params.projectId,
...req.auditLogInfo, ...req.auditLogInfo,
event: { event: {
type: EventType.ADD_BATCH_WORKSPACE_MEMBER, type: EventType.ADD_BATCH_PROJECT_MEMBER,
metadata: memberships.map(({ userId, id }) => ({ metadata: memberships.map(({ userId, id }) => ({
userId: userId || "", userId: userId || "",
membershipId: id, membershipId: id,
@@ -141,7 +141,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
...req.auditLogInfo, ...req.auditLogInfo,
projectId: req.params.projectId, projectId: req.params.projectId,
event: { event: {
type: EventType.REMOVE_WORKSPACE_MEMBER, type: EventType.REMOVE_PROJECT_MEMBER,
metadata: { metadata: {
userId: membership.userId, userId: membership.userId,
email: "" email: ""
@@ -35,7 +35,8 @@ const projectWithEnv = SanitizedProjectSchema.extend({
kmsSecretManagerKeyId: z.string().nullable().optional() kmsSecretManagerKeyId: z.string().nullable().optional()
}); });
export const registerProjectRouter = async (server: FastifyZodProvider) => { export const registerDepreciatedProjectRouter = async (server: FastifyZodProvider) => {
// depreciated
/* Get project key */ /* Get project key */
server.route({ server.route({
method: "GET", method: "GET",
@@ -72,7 +73,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
...req.auditLogInfo, ...req.auditLogInfo,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
event: { event: {
type: EventType.GET_WORKSPACE_KEY, type: EventType.GET_PROJECT_KEY,
metadata: { metadata: {
keyId: key?.id as string keyId: key?.id as string
} }
@@ -83,68 +84,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
} }
}); });
/* Start upgrade of a project */
server.route({
method: "POST",
url: "/:projectId/upgrade",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
projectId: z.string().trim()
}),
body: z.object({
userPrivateKey: z.string().trim()
}),
response: {
200: z.void()
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
await server.services.project.upgradeProject({
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
projectId: req.params.projectId,
userPrivateKey: req.body.userPrivateKey
});
}
});
/* Get upgrade status of project */
server.route({
url: "/:projectId/upgrade/status",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
projectId: z.string().trim()
}),
response: {
200: z.object({
status: z.string().nullable()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const status = await server.services.project.getProjectUpgradeStatus({
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.projectId,
actor: req.permission.type,
actorId: req.permission.id
});
return { status };
}
});
/* Create new project */ /* Create new project */
server.route({ server.route({
method: "POST", method: "POST",
@@ -224,6 +163,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
}); });
/* Delete a project by slug */ /* Delete a project by slug */
// moved to DELETE /v1/projects/slug/:slug
server.route({ server.route({
method: "DELETE", method: "DELETE",
url: "/:slug", url: "/:slug",
@@ -276,6 +216,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
}); });
/* Get a project by slug */ /* Get a project by slug */
// moved to GET /v1/projects/slug/:slug
server.route({ server.route({
method: "GET", method: "GET",
url: "/:slug", url: "/:slug",
@@ -337,7 +278,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
200: SanitizedProjectSchema 200: SanitizedProjectSchema
} }
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const project = await server.services.project.updateProject({ const project = await server.services.project.updateProject({
+10 -8
View File
@@ -1,13 +1,13 @@
import { registerCaRouter } from "./certificate-authority-router"; import { registerCaRouter } from "./certificate-authority-router";
import { registerGroupProjectRouter } from "./group-project-router"; import { registerDepreciatedGroupProjectRouter } from "./depreciated-group-project-router";
import { registerIdentityOrgRouter } from "./identity-org-router"; import { registerIdentityOrgRouter } from "./identity-org-router";
import { registerIdentityProjectRouter } from "./identity-project-router"; import { registerDepreciatedIdentityProjectRouter } from "./depreciated-identity-project-router";
import { registerMfaRouter } from "./mfa-router"; import { registerMfaRouter } from "./mfa-router";
import { registerOrgRouter } from "./organization-router"; import { registerOrgRouter } from "./organization-router";
import { registerPasswordRouter } from "./password-router"; import { registerPasswordRouter } from "./password-router";
import { registerPkiTemplatesRouter } from "./pki-templates-router"; import { registerPkiTemplatesRouter } from "./pki-templates-router";
import { registerProjectMembershipRouter } from "./project-membership-router"; import { registerDepreciatedProjectMembershipRouter } from "./depreciated-project-membership-router";
import { registerProjectRouter } from "./project-router"; import { registerDepreciatedProjectRouter } from "./depreciated-project-router";
import { registerServiceTokenRouter } from "./service-token-router"; import { registerServiceTokenRouter } from "./service-token-router";
import { registerUserRouter } from "./user-router"; import { registerUserRouter } from "./user-router";
@@ -32,12 +32,14 @@ export const registerV2Routes = async (server: FastifyZodProvider) => {
}, },
{ prefix: "/organizations" } { prefix: "/organizations" }
); );
// moved to v1/projects
await server.register( await server.register(
async (projectServer) => { async (projectServer) => {
await projectServer.register(registerProjectRouter); await projectServer.register(registerDepreciatedProjectRouter);
await projectServer.register(registerIdentityProjectRouter); await projectServer.register(registerDepreciatedIdentityProjectRouter);
await projectServer.register(registerGroupProjectRouter); await projectServer.register(registerDepreciatedGroupProjectRouter);
await projectServer.register(registerProjectMembershipRouter); await projectServer.register(registerDepreciatedProjectMembershipRouter);
}, },
{ prefix: "/workspace" } { prefix: "/workspace" }
); );
-2
View File
@@ -1,6 +1,5 @@
import { registerExternalMigrationRouter } from "./external-migration-router"; import { registerExternalMigrationRouter } from "./external-migration-router";
import { registerLoginRouter } from "./login-router"; import { registerLoginRouter } from "./login-router";
import { registerSecretBlindIndexRouter } from "./secret-blind-index-router";
import { registerSecretRouter } from "./secret-router"; import { registerSecretRouter } from "./secret-router";
import { registerSignupRouter } from "./signup-router"; import { registerSignupRouter } from "./signup-router";
import { registerUserRouter } from "./user-router"; import { registerUserRouter } from "./user-router";
@@ -10,6 +9,5 @@ export const registerV3Routes = async (server: FastifyZodProvider) => {
await server.register(registerLoginRouter, { prefix: "/auth" }); await server.register(registerLoginRouter, { prefix: "/auth" });
await server.register(registerUserRouter, { prefix: "/users" }); await server.register(registerUserRouter, { prefix: "/users" });
await server.register(registerSecretRouter, { prefix: "/secrets" }); await server.register(registerSecretRouter, { prefix: "/secrets" });
await server.register(registerSecretBlindIndexRouter, { prefix: "/workspaces" });
await server.register(registerExternalMigrationRouter, { prefix: "/external-migration" }); await server.register(registerExternalMigrationRouter, { prefix: "/external-migration" });
}; };
@@ -1,109 +0,0 @@
import { z } from "zod";
import { SecretsSchema } from "@app/db/schemas";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerSecretBlindIndexRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:projectId/secrets/blind-index-status",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
projectId: z.string().trim()
}),
response: {
200: z.boolean()
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const count = await server.services.secretBlindIndex.getSecretBlindIndexStatus({
projectId: req.params.projectId,
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId
});
return count === 0;
}
});
server.route({
method: "GET",
url: "/:projectId/secrets",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
projectId: z.string().trim()
}),
response: {
200: z.object({
secrets: SecretsSchema.omit({ secretBlindIndex: true })
.merge(
z.object({
environment: z.string(),
workspace: z.string()
})
)
.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const secrets = await server.services.secretBlindIndex.getProjectSecrets({
projectId: req.params.projectId,
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId
});
return { secrets };
}
});
server.route({
method: "POST",
url: "/:projectId/secrets/names",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
projectId: z.string().trim()
}),
body: z.object({
secretsToUpdate: z
.object({
secretName: z.string().trim(),
secretId: z.string().trim()
})
.array()
}),
response: {
200: z.object({
message: z.string()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
await server.services.secretBlindIndex.updateProjectSecretName({
projectId: req.params.projectId,
secretsToUpdate: req.body.secretsToUpdate,
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actor: req.permission.type,
actorOrgId: req.permission.orgId
});
return { message: "Successfully named workspace secrets" };
}
});
};
+7 -7
View File
@@ -7,10 +7,10 @@ import { TReactQueryOptions } from "@app/types/reactQuery";
import { Actor, AuditLog, TGetAuditLogsFilter } from "./types"; import { Actor, AuditLog, TGetAuditLogsFilter } from "./types";
export const auditLogKeys = { export const auditLogKeys = {
getAuditLogs: (workspaceId: string | null, filters: TGetAuditLogsFilter) => getAuditLogs: (projectId: string | null, filters: TGetAuditLogsFilter) =>
[{ workspaceId, filters }, "audit-logs"] as const, [{ projectId, filters }, "audit-logs"] as const,
getAuditLogActorFilterOpts: (workspaceId: string) => getAuditLogActorFilterOpts: (projectId: string) =>
[{ workspaceId }, "audit-log-actor-filters"] as const [{ projectId }, "audit-log-actor-filters"] as const
}; };
export const useGetAuditLogs = ( export const useGetAuditLogs = (
@@ -56,12 +56,12 @@ export const useGetAuditLogs = (
}); });
}; };
export const useGetAuditLogActorFilterOpts = (workspaceId: string) => { export const useGetAuditLogActorFilterOpts = (projectId: string) => {
return useQuery({ return useQuery({
queryKey: auditLogKeys.getAuditLogActorFilterOpts(workspaceId), queryKey: auditLogKeys.getAuditLogActorFilterOpts(projectId),
queryFn: async () => { queryFn: async () => {
const { data } = await apiRequest.get<{ actors: Actor[] }>( const { data } = await apiRequest.get<{ actors: Actor[] }>(
`/api/v1/workspace/${workspaceId}/audit-logs/filters/actors` `/api/v1/projects/${projectId}/audit-logs/filters/actors`
); );
return data.actors; return data.actors;
} }
+1 -1
View File
@@ -138,7 +138,7 @@ export const useListProjectGroupUsers = ({
}); });
const { data } = await apiRequest.get<{ users: TGroupUser[]; totalCount: number }>( const { data } = await apiRequest.get<{ users: TGroupUser[]; totalCount: number }>(
`/api/v2/workspace/${projectId}/groups/${id}/users`, `/api/v1/projects/${projectId}/groups/${id}/users`,
{ {
params params
} }
+19 -19
View File
@@ -4,23 +4,23 @@ import { apiRequest } from "@app/config/request";
import { CreateTagDTO, DeleteTagDTO, UserWsTags, WsTag } from "./types"; import { CreateTagDTO, DeleteTagDTO, UserWsTags, WsTag } from "./types";
const workspaceTags = { const projectTags = {
getWsTags: (workspaceID: string) => ["workspace-tags", { workspaceID }] as const getWsTags: (projectID: string) => ["project-tags", { projectID }] as const
}; };
const fetchWsTag = async (workspaceID: string) => { const fetchWsTag = async (projectID: string) => {
const { data } = await apiRequest.get<{ workspaceTags: UserWsTags }>( const { data } = await apiRequest.get<{ projectTags: UserWsTags }>(
`/api/v1/workspace/${workspaceID}/tags` `/api/v1/projects/${projectID}/tags`
); );
return data.workspaceTags; return data.projectTags;
}; };
export const useGetWsTags = (workspaceID: string) => { export const useGetWsTags = (projectID: string) => {
return useQuery({ return useQuery({
queryKey: workspaceTags.getWsTags(workspaceID), queryKey: projectTags.getWsTags(projectID),
queryFn: () => fetchWsTag(workspaceID), queryFn: () => fetchWsTag(projectID),
enabled: Boolean(workspaceID) enabled: Boolean(projectID)
}); });
}; };
@@ -28,18 +28,18 @@ export const useCreateWsTag = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation<WsTag, object, CreateTagDTO>({ return useMutation<WsTag, object, CreateTagDTO>({
mutationFn: async ({ workspaceID, tagColor, tagSlug }) => { mutationFn: async ({ projectID, tagColor, tagSlug }) => {
const { data } = await apiRequest.post<{ workspaceTag: WsTag }>( const { data } = await apiRequest.post<{ projectTag: WsTag }>(
`/api/v1/workspace/${workspaceID}/tags`, `/api/v1/projects/${projectID}/tags`,
{ {
color: tagColor || "", color: tagColor || "",
slug: tagSlug slug: tagSlug
} }
); );
return data.workspaceTag; return data.projectTag;
}, },
onSuccess: (tagData) => { onSuccess: (tagData) => {
queryClient.invalidateQueries({ queryKey: workspaceTags.getWsTags(tagData?.projectId) }); queryClient.invalidateQueries({ queryKey: projectTags.getWsTags(tagData?.projectId) });
} }
}); });
}; };
@@ -49,13 +49,13 @@ export const useDeleteWsTag = () => {
return useMutation<WsTag, object, DeleteTagDTO>({ return useMutation<WsTag, object, DeleteTagDTO>({
mutationFn: async ({ tagID, projectId }) => { mutationFn: async ({ tagID, projectId }) => {
const { data } = await apiRequest.delete<{ workspaceTag: WsTag }>( const { data } = await apiRequest.delete<{ projectTag: WsTag }>(
`/api/v1/workspace/${projectId}/tags/${tagID}` `/api/v1/projects/${projectId}/tags/${tagID}`
); );
return data.workspaceTag; return data.projectTag;
}, },
onSuccess: (tagData) => { onSuccess: (tagData) => {
queryClient.invalidateQueries({ queryKey: workspaceTags.getWsTags(tagData?.projectId) }); queryClient.invalidateQueries({ queryKey: projectTags.getWsTags(tagData?.projectId) });
} }
}); });
}; };
+1 -1
View File
@@ -13,7 +13,7 @@ export type WsTag = {
export type WorkspaceTag = { id: string; name: string; slug: string }; export type WorkspaceTag = { id: string; name: string; slug: string };
export type CreateTagDTO = { export type CreateTagDTO = {
workspaceID: string; projectID: string;
tagSlug: string; tagSlug: string;
tagColor: string; tagColor: string;
}; };
@@ -118,13 +118,13 @@ export const useUpdateProjectWorkflowIntegrationConfig = () => {
return useMutation({ return useMutation({
mutationFn: async (dto: TUpdateProjectWorkflowIntegrationConfigDTO) => { mutationFn: async (dto: TUpdateProjectWorkflowIntegrationConfigDTO) => {
const { data } = await apiRequest.put( const { data } = await apiRequest.put(
`/api/v1/workspace/${dto.workspaceId}/workflow-integration`, `/api/v1/projects/${dto.projectId}/workflow-integration`,
dto dto
); );
return data; return data;
}, },
onSuccess: (_, { workspaceId, integration }) => { onSuccess: (_, { projectId: workspaceId, integration }) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: projectKeys.getWorkspaceWorkflowIntegrationConfig(workspaceId, integration) queryKey: projectKeys.getWorkspaceWorkflowIntegrationConfig(workspaceId, integration)
}); });
@@ -138,7 +138,7 @@ export const useDeleteProjectWorkflowIntegration = () => {
return useMutation({ return useMutation({
mutationFn: async (dto: TDeleteProjectWorkflowIntegrationDTO) => { mutationFn: async (dto: TDeleteProjectWorkflowIntegrationDTO) => {
const { data } = await apiRequest.delete( const { data } = await apiRequest.delete(
`/api/v1/workspace/${dto.projectId}/workflow-integration/${dto.integration}/${dto.integrationId}` `/api/v1/projects/${dto.projectId}/workflow-integration/${dto.integration}/${dto.integrationId}`
); );
return data; return data;
@@ -106,7 +106,7 @@ export type ProjectWorkflowIntegrationConfig =
export type TUpdateProjectWorkflowIntegrationConfigDTO = export type TUpdateProjectWorkflowIntegrationConfigDTO =
| { | {
integration: WorkflowIntegrationPlatform.SLACK; integration: WorkflowIntegrationPlatform.SLACK;
workspaceId: string; projectId: string;
integrationId: string; integrationId: string;
isAccessRequestNotificationEnabled: boolean; isAccessRequestNotificationEnabled: boolean;
accessRequestChannels: string; accessRequestChannels: string;
@@ -115,7 +115,7 @@ export type TUpdateProjectWorkflowIntegrationConfigDTO =
} }
| { | {
integration: WorkflowIntegrationPlatform.MICROSOFT_TEAMS; integration: WorkflowIntegrationPlatform.MICROSOFT_TEAMS;
workspaceId: string; projectId: string;
integrationId: string; integrationId: string;
isAccessRequestNotificationEnabled: boolean; isAccessRequestNotificationEnabled: boolean;
isSecretRequestNotificationEnabled: boolean; isSecretRequestNotificationEnabled: boolean;
@@ -25,7 +25,6 @@ export {
useGetWorkspaceIdentityMemberships, useGetWorkspaceIdentityMemberships,
useGetWorkspaceIndexStatus, useGetWorkspaceIndexStatus,
useGetWorkspaceIntegrations, useGetWorkspaceIntegrations,
useGetWorkspaceSecrets,
useGetWorkspaceUserDetails, useGetWorkspaceUserDetails,
useGetWorkspaceUsers, useGetWorkspaceUsers,
useGetWorkspaceWorkflowIntegrationConfig, useGetWorkspaceWorkflowIntegrationConfig,
@@ -41,7 +40,6 @@ export {
useListWorkspaceSshCertificateTemplates, useListWorkspaceSshCertificateTemplates,
useListWorkspaceSshHostGroups, useListWorkspaceSshHostGroups,
useListWorkspaceSshHosts, useListWorkspaceSshHosts,
useNameWorkspaceSecrets,
useSearchProjects, useSearchProjects,
useToggleAutoCapitalization, useToggleAutoCapitalization,
useUpdateIdentityWorkspaceRole, useUpdateIdentityWorkspaceRole,
@@ -15,7 +15,6 @@ import { TIntegration } from "../integrations/types";
import { TPkiAlert } from "../pkiAlerts/types"; import { TPkiAlert } from "../pkiAlerts/types";
import { TPkiCollection } from "../pkiCollections/types"; import { TPkiCollection } from "../pkiCollections/types";
import { TPkiSubscriber } from "../pkiSubscriber/types"; import { TPkiSubscriber } from "../pkiSubscriber/types";
import { EncryptedSecret } from "../secrets/types";
import { TSshCertificate, TSshCertificateAuthority } from "../sshCa/types"; import { TSshCertificate, TSshCertificateAuthority } from "../sshCa/types";
import { TSshCertificateTemplate } from "../sshCertificateTemplates/types"; import { TSshCertificateTemplate } from "../sshCertificateTemplates/types";
import { TSshHost } from "../sshHost/types"; import { TSshHost } from "../sshHost/types";
@@ -32,7 +31,6 @@ import {
CreateWorkspaceDTO, CreateWorkspaceDTO,
DeleteEnvironmentDTO, DeleteEnvironmentDTO,
DeleteWorkspaceDTO, DeleteWorkspaceDTO,
NameWorkspaceSecretsDTO,
ProjectIdentityOrderBy, ProjectIdentityOrderBy,
ProjectType, ProjectType,
TGetUpgradeProjectStatusDTO, TGetUpgradeProjectStatusDTO,
@@ -73,14 +71,6 @@ const fetchProjectUpgradeStatus = async (projectId: string) => {
return data; return data;
}; };
export const fetchWorkspaceSecrets = async (projectId: string) => {
const {
data: { secrets }
} = await apiRequest.get<{ secrets: EncryptedSecret[] }>(`/api/v3/projects/${projectId}/secrets`);
return secrets;
};
export const useUpgradeProject = () => { export const useUpgradeProject = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@@ -129,14 +119,6 @@ export const useGetWorkspaceIndexStatus = (projectId: string) => {
}); });
}; };
export const useGetWorkspaceSecrets = (projectId: string) => {
return useQuery({
queryKey: projectKeys.getWorkspaceSecrets(projectId),
queryFn: () => fetchWorkspaceSecrets(projectId),
enabled: true
});
};
export const useGetWorkspaceById = ( export const useGetWorkspaceById = (
projectId: string, projectId: string,
dto?: { refetchInterval?: number | false } dto?: { refetchInterval?: number | false }
@@ -192,22 +174,6 @@ export const useGetUserWorkspaceMemberships = (orgId: string) =>
enabled: Boolean(orgId) enabled: Boolean(orgId)
}); });
export const useNameWorkspaceSecrets = () => {
const queryClient = useQueryClient();
return useMutation<object, object, NameWorkspaceSecretsDTO>({
mutationFn: async ({ projectId, secretsToUpdate }) =>
apiRequest.post(`/api/v3/projects/${projectId}/secrets/names`, {
secretsToUpdate
}),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({
queryKey: projectKeys.getWorkspaceIndexStatus(variables.projectId)
});
}
});
};
const fetchWorkspaceAuthorization = async (projectId: string) => { const fetchWorkspaceAuthorization = async (projectId: string) => {
const { data } = await apiRequest.get<{ authorizations: IntegrationAuth[] }>( const { data } = await apiRequest.get<{ authorizations: IntegrationAuth[] }>(
`/api/v1/projects/${projectId}/authorizations` `/api/v1/projects/${projectId}/authorizations`
@@ -136,7 +136,7 @@ export const MicrosoftTeamsIntegrationForm = ({ onClose }: Props) => {
} }
await updateProjectMicrosoftTeamsConfig({ await updateProjectMicrosoftTeamsConfig({
workspaceId: currentWorkspace.id, projectId: currentWorkspace.id,
isAccessRequestNotificationEnabled: data.isAccessRequestNotificationEnabled, isAccessRequestNotificationEnabled: data.isAccessRequestNotificationEnabled,
isSecretRequestNotificationEnabled: data.isSecretRequestNotificationEnabled, isSecretRequestNotificationEnabled: data.isSecretRequestNotificationEnabled,
...(data.isAccessRequestNotificationEnabled && { ...(data.isAccessRequestNotificationEnabled && {
@@ -83,7 +83,7 @@ export const SlackIntegrationForm = ({ onClose }: Props) => {
await updateProjectSlackConfig({ await updateProjectSlackConfig({
...data, ...data,
workspaceId: currentWorkspace.id, projectId: currentWorkspace.id,
integration: WorkflowIntegrationPlatform.SLACK, integration: WorkflowIntegrationPlatform.SLACK,
integrationId: data.slackIntegrationId, integrationId: data.slackIntegrationId,
accessRequestChannels: data.accessRequestChannels.filter(Boolean).join(", "), accessRequestChannels: data.accessRequestChannels.filter(Boolean).join(", "),