feat: updated folder, import, approvals and some more to projectid

This commit is contained in:
=
2025-09-12 22:59:57 +05:30
parent fd2b067e14
commit 02dc2e2106
25 changed files with 1380 additions and 267 deletions

View File

@@ -0,0 +1,293 @@
import { nanoid } from "nanoid";
import { z } from "zod";
import { ApproverType, BypasserType } from "@app/ee/services/access-approval-policy/access-approval-policy-types";
import { removeTrailingSlash } from "@app/lib/fn";
import { EnforcementLevel } from "@app/lib/types";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { sapPubSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerDepreciatedSecretApprovalPolicyRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/",
method: "POST",
config: {
rateLimit: writeLimit
},
schema: {
body: z
.object({
workspaceId: z.string(),
name: z.string().optional(),
environment: z.string().optional(),
environments: z.string().array().optional(),
secretPath: z
.string()
.min(1, { message: "Secret path cannot be empty" })
.transform((val) => removeTrailingSlash(val)),
approvers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
z.object({
type: z.literal(ApproverType.User),
id: z.string().optional(),
username: z.string().optional()
})
])
.array()
.min(1, { message: "At least one approver should be provided" })
.max(100, "Cannot have more than 100 approvers"),
bypassers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
z.object({
type: z.literal(BypasserType.User),
id: z.string().optional(),
username: z.string().optional()
})
])
.array()
.max(100, "Cannot have more than 100 bypassers")
.optional(),
approvals: z.number().min(1).default(1),
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
allowedSelfApprovals: z.boolean().default(true)
})
.refine((data) => data.environment || data.environments, "At least one environment should be provided"),
response: {
200: z.object({
approval: sapPubSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const approval = await server.services.secretApprovalPolicy.createSecretApprovalPolicy({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.body.workspaceId,
...req.body,
name: req.body.name ?? `${req.body.environment || req.body.environments?.join(",")}-${nanoid(3)}`,
enforcementLevel: req.body.enforcementLevel
});
return { approval };
}
});
server.route({
url: "/:sapId",
method: "PATCH",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
sapId: z.string()
}),
body: z.object({
name: z.string().optional(),
approvers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), username: z.string().optional() })
])
.array()
.min(1, { message: "At least one approver should be provided" })
.max(100, "Cannot have more than 100 approvers"),
bypassers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
z.object({ type: z.literal(BypasserType.User), id: z.string().optional(), username: z.string().optional() })
])
.array()
.max(100, "Cannot have more than 100 bypassers")
.optional(),
approvals: z.number().min(1).default(1),
secretPath: z
.string()
.trim()
.min(1, { message: "Secret path cannot be empty" })
.optional()
.transform((val) => (val ? removeTrailingSlash(val) : undefined)),
enforcementLevel: z.nativeEnum(EnforcementLevel).optional(),
allowedSelfApprovals: z.boolean().default(true),
environments: z.array(z.string()).optional()
}),
response: {
200: z.object({
approval: sapPubSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const approval = await server.services.secretApprovalPolicy.updateSecretApprovalPolicy({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body,
secretPolicyId: req.params.sapId
});
return { approval };
}
});
server.route({
url: "/:sapId",
method: "DELETE",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
sapId: z.string()
}),
response: {
200: z.object({
approval: sapPubSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const approval = await server.services.secretApprovalPolicy.deleteSecretApprovalPolicy({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
secretPolicyId: req.params.sapId
});
return { approval };
}
});
server.route({
url: "/",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
querystring: z.object({
workspaceId: z.string().trim()
}),
response: {
200: z.object({
approvals: sapPubSchema
.extend({
approvers: z
.object({
id: z.string().nullable().optional(),
type: z.nativeEnum(ApproverType)
})
.array(),
bypassers: z
.object({
id: z.string().nullable().optional(),
type: z.nativeEnum(BypasserType)
})
.array()
})
.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const approvals = await server.services.secretApprovalPolicy.getSecretApprovalPolicyByProjectId({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId
});
return { approvals };
}
});
server.route({
url: "/:sapId",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
sapId: z.string()
}),
response: {
200: z.object({
approval: sapPubSchema.extend({
approvers: z
.object({
id: z.string().nullable().optional(),
type: z.nativeEnum(ApproverType),
username: z.string().nullable().optional()
})
.array(),
bypassers: z
.object({
id: z.string().nullable().optional(),
type: z.nativeEnum(BypasserType),
username: z.string().nullable().optional()
})
.array()
})
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const approval = await server.services.secretApprovalPolicy.getSecretApprovalPolicyById({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.params
});
return { approval };
}
});
server.route({
url: "/board",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
querystring: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().transform(removeTrailingSlash)
}),
response: {
200: z.object({
policy: sapPubSchema
.extend({
userApprovers: z.object({ userId: z.string().nullable().optional() }).array()
})
.optional()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId,
...req.query
});
return { policy };
}
});
};

View File

@@ -29,7 +29,7 @@ import { registerRateLimitRouter } from "./rate-limit-router";
import { registerRelayRouter } from "./relay-router"; import { registerRelayRouter } from "./relay-router";
import { registerSamlRouter } from "./saml-router"; import { registerSamlRouter } from "./saml-router";
import { registerScimRouter } from "./scim-router"; import { registerScimRouter } from "./scim-router";
import { registerSecretApprovalPolicyRouter } from "./secret-approval-policy-router"; import { registerDepreciatedSecretApprovalPolicyRouter } from "./depreciated-secret-approval-policy-router";
import { registerSecretApprovalRequestRouter } from "./secret-approval-request-router"; import { registerSecretApprovalRequestRouter } from "./secret-approval-request-router";
import { registerSecretRotationProviderRouter } from "./secret-rotation-provider-router"; import { registerSecretRotationProviderRouter } from "./secret-rotation-provider-router";
import { registerSecretRotationRouter } from "./secret-rotation-router"; import { registerSecretRotationRouter } from "./secret-rotation-router";
@@ -71,7 +71,7 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
await server.register(registerSnapshotRouter, { prefix: "/secret-snapshot" }); await server.register(registerSnapshotRouter, { prefix: "/secret-snapshot" });
await server.register(registerPITRouter, { prefix: "/pit" }); await server.register(registerPITRouter, { prefix: "/pit" });
await server.register(registerSecretApprovalPolicyRouter, { prefix: "/secret-approvals" }); await server.register(registerDepreciatedSecretApprovalPolicyRouter, { prefix: "/secret-approvals" });
await server.register(registerSecretApprovalRequestRouter, { await server.register(registerSecretApprovalRequestRouter, {
prefix: "/secret-approval-requests" prefix: "/secret-approval-requests"
}); });

View File

@@ -27,7 +27,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
}, },
schema: { schema: {
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), projectId: z.string().trim(),
environment: z.string().trim().optional(), environment: z.string().trim().optional(),
committer: z.string().trim().optional(), committer: z.string().trim().optional(),
search: z.string().trim().optional(), search: z.string().trim().optional(),
@@ -80,7 +80,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
...req.query, ...req.query,
projectId: req.query.workspaceId projectId: req.query.projectId
}); });
return { approvals, totalCount }; return { approvals, totalCount };
} }
@@ -94,7 +94,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
}, },
schema: { schema: {
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), projectId: z.string().trim(),
policyId: z.string().trim().optional() policyId: z.string().trim().optional()
}), }),
response: { response: {
@@ -113,7 +113,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId, projectId: req.query.projectId,
policyId: req.query.policyId policyId: req.query.policyId
}); });
return { approvals }; return { approvals };

View File

@@ -10,6 +10,7 @@ import {
import { registerGatewayV2Router } from "./gateway-router"; import { registerGatewayV2Router } from "./gateway-router";
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router"; import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
import { registerDepreciatedProjectRoleRouter } from "./depreciated-project-role-router"; import { registerDepreciatedProjectRoleRouter } from "./depreciated-project-role-router";
import { registerSecretApprovalPolicyRouter } from "./secret-approval-policy-router";
export const registerV2EERoutes = async (server: FastifyZodProvider) => { export const registerV2EERoutes = async (server: FastifyZodProvider) => {
await server.register( await server.register(
@@ -26,6 +27,8 @@ export const registerV2EERoutes = async (server: FastifyZodProvider) => {
await server.register(registerGatewayV2Router, { prefix: "/gateways" }); await server.register(registerGatewayV2Router, { prefix: "/gateways" });
await server.register(registerSecretApprovalPolicyRouter, { prefix: "/secret-approvals" });
await server.register( await server.register(
async (secretRotationV2Router) => { async (secretRotationV2Router) => {
// register generic secret rotation endpoints // register generic secret rotation endpoints

View File

@@ -19,7 +19,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
schema: { schema: {
body: z body: z
.object({ .object({
workspaceId: z.string(), projectId: z.string(),
name: z.string().optional(), name: z.string().optional(),
environment: z.string().optional(), environment: z.string().optional(),
environments: z.string().array().optional(), environments: z.string().array().optional(),
@@ -69,7 +69,6 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
projectId: req.body.workspaceId,
...req.body, ...req.body,
name: req.body.name ?? `${req.body.environment || req.body.environments?.join(",")}-${nanoid(3)}`, name: req.body.name ?? `${req.body.environment || req.body.environments?.join(",")}-${nanoid(3)}`,
enforcementLevel: req.body.enforcementLevel enforcementLevel: req.body.enforcementLevel
@@ -174,7 +173,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
}, },
schema: { schema: {
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim() projectId: z.string().trim()
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -204,7 +203,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId projectId: req.query.projectId
}); });
return { approvals }; return { approvals };
} }
@@ -263,7 +262,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
}, },
schema: { schema: {
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), projectId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
secretPath: z.string().trim().transform(removeTrailingSlash) secretPath: z.string().trim().transform(removeTrailingSlash)
}), }),
@@ -284,7 +283,6 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId,
...req.query ...req.query
}); });
return { policy }; return { policy };

View File

@@ -901,7 +901,7 @@ export const ENVIRONMENTS = {
export const FOLDERS = { export const FOLDERS = {
LIST: { LIST: {
workspaceId: "The ID of the project to list folders from.", projectId: "The ID of the project to list folders from.",
environment: "The slug of the environment to list folders from.", environment: "The slug of the environment to list folders from.",
path: "The path to list folders from.", path: "The path to list folders from.",
directory: "The directory to list folders from. (Deprecated in favor of path)", directory: "The directory to list folders from. (Deprecated in favor of path)",
@@ -913,7 +913,7 @@ export const FOLDERS = {
folderId: "The ID of the folder to get details." folderId: "The ID of the folder to get details."
}, },
CREATE: { CREATE: {
workspaceId: "The ID of the project to create the folder in.", projectId: "The ID of the project to create the folder in.",
environment: "The slug of the environment to create the folder in.", environment: "The slug of the environment to create the folder in.",
name: "The name of the folder to create.", name: "The name of the folder to create.",
path: "The path of the folder to create.", path: "The path of the folder to create.",
@@ -927,12 +927,12 @@ export const FOLDERS = {
path: "The path of the folder to update.", path: "The path of the folder to update.",
directory: "The new directory of the folder to update. (Deprecated in favor of path)", directory: "The new directory of the folder to update. (Deprecated in favor of path)",
projectSlug: "The slug of the project where the folder is located.", projectSlug: "The slug of the project where the folder is located.",
workspaceId: "The ID of the project where the folder is located.", projectId: "The ID of the project where the folder is located.",
description: "An optional description label for the folder." description: "An optional description label for the folder."
}, },
DELETE: { DELETE: {
folderIdOrName: "The ID or name of the folder to delete.", folderIdOrName: "The ID or name of the folder to delete.",
workspaceId: "The ID of the project to delete the folder from.", projectId: "The ID of the project to delete the folder from.",
environment: "The slug of the environment where the folder is located.", environment: "The slug of the environment where the folder is located.",
directory: "The directory of the folder to delete. (Deprecated in favor of path)", directory: "The directory of the folder to delete. (Deprecated in favor of path)",
path: "The path of the folder to delete." path: "The path of the folder to delete."
@@ -1043,7 +1043,7 @@ export const RAW_SECRETS = {
export const SECRET_IMPORTS = { export const SECRET_IMPORTS = {
LIST: { LIST: {
workspaceId: "The ID of the project to list secret imports from.", projectId: "The ID of the project to list secret imports from.",
environment: "The slug of the environment to list secret imports from.", environment: "The slug of the environment to list secret imports from.",
path: "The path to list secret imports from." path: "The path to list secret imports from."
}, },
@@ -1053,7 +1053,7 @@ export const SECRET_IMPORTS = {
CREATE: { CREATE: {
environment: "The slug of the environment to import into.", environment: "The slug of the environment to import into.",
path: "The path to import into.", path: "The path to import into.",
workspaceId: "The ID of the project you are working in.", projectId: "The ID of the project you are working in.",
isReplication: isReplication:
"When true, secrets from the source will be automatically sent to the destination. If approval policies exist at the destination, the secrets will be sent as approval requests instead of being applied immediately.", "When true, secrets from the source will be automatically sent to the destination. If approval policies exist at the destination, the secrets will be sent as approval requests instead of being applied immediately.",
import: { import: {
@@ -1070,10 +1070,10 @@ export const SECRET_IMPORTS = {
position: "The new position of the secret import. The lowest number will be displayed as the first import." position: "The new position of the secret import. The lowest number will be displayed as the first import."
}, },
path: "The path of the secret import to update.", path: "The path of the secret import to update.",
workspaceId: "The ID of the project where the secret import is located." projectId: "The ID of the project where the secret import is located."
}, },
DELETE: { DELETE: {
workspaceId: "The ID of the project to delete the secret import from.", projectId: "The ID of the project to delete the secret import from.",
secretImportId: "The ID of the secret import to delete.", secretImportId: "The ID of the secret import to delete.",
environment: "The slug of the environment where the secret import is located.", environment: "The slug of the environment where the secret import is located.",
path: "The path of the secret import to delete." path: "The path of the secret import to delete."

View File

@@ -0,0 +1,444 @@
import { z } from "zod";
import { SecretFoldersSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, FOLDERS } from "@app/lib/api-docs";
import { prefixWithSlash, removeTrailingSlash } from "@app/lib/fn";
import { isValidFolderName } from "@app/lib/validator";
import { readLimit, secretsLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { booleanSchema } from "../sanitizedSchemas";
export const registerDepreciatedSecretFolderRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/",
method: "POST",
config: {
rateLimit: secretsLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.Folders],
description: "Create folders",
security: [
{
bearerAuth: []
}
],
body: z.object({
workspaceId: z.string().trim().describe(FOLDERS.CREATE.projectId),
environment: z.string().trim().describe(FOLDERS.CREATE.environment),
name: z
.string()
.trim()
.describe(FOLDERS.CREATE.name)
.refine((name) => isValidFolderName(name), {
message: "Invalid folder name. Only alphanumeric characters, dashes, and underscores are allowed."
}),
path: z
.string()
.trim()
.default("/")
.transform(prefixWithSlash) // Transformations get skipped if path is undefined
.transform(removeTrailingSlash)
.describe(FOLDERS.CREATE.path)
.optional(),
// backward compatibility with cli
directory: z
.string()
.trim()
.default("/")
.transform(prefixWithSlash) // Transformations get skipped if directory is undefined
.transform(removeTrailingSlash)
.describe(FOLDERS.CREATE.directory)
.optional(),
description: z.string().optional().nullable().describe(FOLDERS.CREATE.description)
}),
response: {
200: z.object({
folder: SecretFoldersSchema.extend({
path: z.string()
})
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const path = req.body.path || req.body.directory || "/";
const folder = await server.services.folder.createFolder({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body,
projectId: req.body.workspaceId,
path,
description: req.body.description
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.body.workspaceId,
event: {
type: EventType.CREATE_FOLDER,
metadata: {
environment: req.body.environment,
folderId: folder.id,
folderName: folder.name,
folderPath: path,
...(req.body.description ? { description: req.body.description } : {})
}
}
});
return { folder };
}
});
server.route({
url: "/:folderId",
method: "PATCH",
config: {
rateLimit: secretsLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.Folders],
description: "Update folder",
security: [
{
bearerAuth: []
}
],
params: z.object({
// old way this was name
folderId: z.string().describe(FOLDERS.UPDATE.folderId)
}),
body: z.object({
workspaceId: z.string().trim().describe(FOLDERS.UPDATE.projectId),
environment: z.string().trim().describe(FOLDERS.UPDATE.environment),
name: z
.string()
.trim()
.describe(FOLDERS.UPDATE.name)
.refine((name) => isValidFolderName(name), {
message: "Invalid folder name. Only alphanumeric characters, dashes, and underscores are allowed."
}),
path: z
.string()
.trim()
.default("/")
.transform(prefixWithSlash) // Transformations get skipped if path is undefined
.transform(removeTrailingSlash)
.describe(FOLDERS.UPDATE.path)
.optional(),
// backward compatibility with cli
directory: z
.string()
.trim()
.default("/")
.transform(prefixWithSlash) // Transformations get skipped if directory is undefined
.transform(removeTrailingSlash)
.describe(FOLDERS.UPDATE.directory)
.optional(),
description: z.string().optional().nullable().describe(FOLDERS.UPDATE.description)
}),
response: {
200: z.object({
folder: SecretFoldersSchema.extend({
path: z.string()
})
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const path = req.body.path || req.body.directory || "/";
const { folder, old } = await server.services.folder.updateFolder({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body,
projectId: req.body.workspaceId,
id: req.params.folderId,
path
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.body.workspaceId,
event: {
type: EventType.UPDATE_FOLDER,
metadata: {
environment: req.body.environment,
folderId: folder.id,
folderPath: path,
newFolderName: folder.name,
oldFolderName: old.name
}
}
});
return { folder };
}
});
server.route({
url: "/batch",
method: "PATCH",
config: {
rateLimit: secretsLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.Folders],
description: "Update folders by batch",
security: [
{
bearerAuth: []
}
],
body: z.object({
projectSlug: z.string().trim().describe(FOLDERS.UPDATE.projectSlug),
folders: z
.object({
id: z.string().describe(FOLDERS.UPDATE.folderId),
environment: z.string().trim().describe(FOLDERS.UPDATE.environment),
name: z
.string()
.trim()
.describe(FOLDERS.UPDATE.name)
.refine((name) => isValidFolderName(name), {
message: "Invalid folder name. Only alphanumeric characters, dashes, and underscores are allowed."
}),
path: z
.string()
.trim()
.default("/")
.transform(prefixWithSlash)
.transform(removeTrailingSlash)
.describe(FOLDERS.UPDATE.path),
description: z.string().optional().nullable().describe(FOLDERS.UPDATE.description)
})
.array()
.min(1)
}),
response: {
200: z.object({
folders: SecretFoldersSchema.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { newFolders, oldFolders, projectId } = await server.services.folder.updateManyFolders({
...req.body,
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
await Promise.all(
req.body.folders.map(async (folder, index) => {
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.UPDATE_FOLDER,
metadata: {
environment: oldFolders[index].envId,
folderId: oldFolders[index].id,
folderPath: folder.path,
newFolderName: newFolders[index].name,
oldFolderName: oldFolders[index].name
}
}
});
})
);
return { folders: newFolders };
}
});
// TODO(daniel): Expose this route in api reference and write docs for it.
server.route({
method: "DELETE",
url: "/:folderIdOrName",
config: {
rateLimit: secretsLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.Folders],
description: "Delete a folder",
security: [
{
bearerAuth: []
}
],
params: z.object({
folderIdOrName: z.string().describe(FOLDERS.DELETE.folderIdOrName)
}),
body: z.object({
workspaceId: z.string().trim().describe(FOLDERS.DELETE.projectId),
environment: z.string().trim().describe(FOLDERS.DELETE.environment),
path: z
.string()
.trim()
.default("/")
.transform(prefixWithSlash) // Transformations get skipped if path is undefined
.transform(removeTrailingSlash)
.describe(FOLDERS.DELETE.path)
.optional(),
// keep this here as cli need directory
directory: z
.string()
.trim()
.default("/")
.transform(prefixWithSlash) // Transformations get skipped if directory is undefined
.transform(removeTrailingSlash)
.describe(FOLDERS.DELETE.directory)
.optional()
}),
response: {
200: z.object({
folder: SecretFoldersSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const path = req.body.path || req.body.directory || "/";
const folder = await server.services.folder.deleteFolder({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body,
projectId: req.body.workspaceId,
idOrName: req.params.folderIdOrName,
path
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.body.workspaceId,
event: {
type: EventType.DELETE_FOLDER,
metadata: {
environment: req.body.environment,
folderId: folder.id,
folderPath: path,
folderName: folder.name
}
}
});
return { folder };
}
});
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.Folders],
description: "Get folders",
security: [
{
bearerAuth: []
}
],
querystring: z.object({
workspaceId: z.string().trim().describe(FOLDERS.LIST.projectId),
environment: z.string().trim().describe(FOLDERS.LIST.environment),
lastSecretModified: z.string().datetime().trim().optional().describe(FOLDERS.LIST.lastSecretModified),
path: z
.string()
.trim()
.transform(prefixWithSlash) // Transformations get skipped if path is undefined
.transform(removeTrailingSlash)
.describe(FOLDERS.LIST.path)
.optional(),
// backward compatibility with cli
directory: z
.string()
.trim()
.transform(prefixWithSlash) // Transformations get skipped if directory is undefined
.transform(removeTrailingSlash)
.describe(FOLDERS.LIST.directory)
.optional(),
recursive: booleanSchema.default(false).describe(FOLDERS.LIST.recursive)
}),
response: {
200: z.object({
folders: SecretFoldersSchema.extend({
relativePath: z.string().optional()
}).array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const path = req.query.path || req.query.directory || "/";
const folders = await server.services.folder.getFolders({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.query,
projectId: req.query.workspaceId,
path
});
return { folders };
}
});
server.route({
method: "GET",
url: "/:id",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.Folders],
description: "Get folder by id",
security: [
{
bearerAuth: []
}
],
params: z.object({
id: z.string().trim().describe(FOLDERS.GET_BY_ID.folderId)
}),
response: {
200: z.object({
folder: SecretFoldersSchema.extend({
environment: z.object({
envId: z.string(),
envName: z.string(),
envSlug: z.string()
}),
path: z.string(),
projectId: z.string()
})
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const folder = await server.services.folder.getFolderById({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.id
});
return { folder };
}
});
};

View File

@@ -0,0 +1,472 @@
import { z } from "zod";
import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, SECRET_IMPORTS } from "@app/lib/api-docs";
import { removeTrailingSlash } from "@app/lib/fn";
import { readLimit, secretsLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { secretRawSchema } from "../sanitizedSchemas";
export const registerDepreciatedSecretImportRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/",
config: {
rateLimit: secretsLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretImports],
description: "Create secret imports",
security: [
{
bearerAuth: []
}
],
body: z.object({
workspaceId: z.string().trim().describe(SECRET_IMPORTS.CREATE.projectId),
environment: z.string().trim().describe(SECRET_IMPORTS.CREATE.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.CREATE.path),
import: z.object({
environment: z.string().trim().describe(SECRET_IMPORTS.CREATE.import.environment),
path: z.string().trim().transform(removeTrailingSlash).describe(SECRET_IMPORTS.CREATE.import.path)
}),
isReplication: z.boolean().default(false).describe(SECRET_IMPORTS.CREATE.isReplication)
}),
response: {
200: z.object({
message: z.string(),
secretImport: SecretImportsSchema.omit({ importEnv: true }).merge(
z.object({
importEnv: z.object({ name: z.string(), slug: z.string(), id: z.string() })
})
)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const secretImport = await server.services.secretImport.createImport({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.body,
projectId: req.body.workspaceId,
data: req.body.import
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.body.workspaceId,
event: {
type: EventType.CREATE_SECRET_IMPORT,
metadata: {
secretImportId: secretImport.id,
folderId: secretImport.folderId,
importFromSecretPath: secretImport.importPath,
importFromEnvironment: secretImport.importEnv.slug,
importToEnvironment: req.body.environment,
importToSecretPath: req.body.path
}
}
});
return { message: "Successfully created secret import", secretImport };
}
});
server.route({
method: "PATCH",
url: "/:secretImportId",
config: {
rateLimit: secretsLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretImports],
description: "Update secret imports",
security: [
{
bearerAuth: []
}
],
params: z.object({
secretImportId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.secretImportId)
}),
body: z.object({
workspaceId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.projectId),
environment: z.string().trim().describe(SECRET_IMPORTS.UPDATE.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.UPDATE.path),
import: z.object({
environment: z.string().trim().optional().describe(SECRET_IMPORTS.UPDATE.import.environment),
path: z
.string()
.trim()
.optional()
.transform((val) => (val ? removeTrailingSlash(val) : val))
.describe(SECRET_IMPORTS.UPDATE.import.path),
position: z.number().optional().describe(SECRET_IMPORTS.UPDATE.import.position)
})
}),
response: {
200: z.object({
message: z.string(),
secretImport: SecretImportsSchema.omit({ importEnv: true }).merge(
z.object({
importEnv: z.object({ name: z.string(), slug: z.string(), id: z.string() })
})
)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const secretImport = await server.services.secretImport.updateImport({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.secretImportId,
...req.body,
projectId: req.body.workspaceId,
data: req.body.import
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.body.workspaceId,
event: {
type: EventType.UPDATE_SECRET_IMPORT,
metadata: {
secretImportId: secretImport.id,
folderId: secretImport.folderId,
position: secretImport.position,
importToEnvironment: req.body.environment,
importToSecretPath: req.body.path
}
}
});
return { message: "Successfully updated secret import", secretImport };
}
});
server.route({
method: "DELETE",
url: "/:secretImportId",
config: {
rateLimit: secretsLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretImports],
description: "Delete secret imports",
security: [
{
bearerAuth: []
}
],
params: z.object({
secretImportId: z.string().trim().describe(SECRET_IMPORTS.DELETE.secretImportId)
}),
body: z.object({
workspaceId: z.string().trim().describe(SECRET_IMPORTS.DELETE.projectId),
environment: z.string().trim().describe(SECRET_IMPORTS.DELETE.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.DELETE.path)
}),
response: {
200: z.object({
message: z.string(),
secretImport: SecretImportsSchema.omit({ importEnv: true }).merge(
z.object({
importEnv: z.object({ name: z.string(), slug: z.string(), id: z.string() })
})
)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const secretImport = await server.services.secretImport.deleteImport({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.secretImportId,
...req.body,
projectId: req.body.workspaceId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.body.workspaceId,
event: {
type: EventType.DELETE_SECRET_IMPORT,
metadata: {
secretImportId: secretImport.id,
folderId: secretImport.folderId,
importFromEnvironment: secretImport.importEnv.slug,
importFromSecretPath: secretImport.importPath,
importToEnvironment: req.body.environment,
importToSecretPath: req.body.path
}
}
});
return { message: "Successfully deleted secret import", secretImport };
}
});
server.route({
method: "POST",
url: "/:secretImportId/replication-resync",
config: {
rateLimit: secretsLimit
},
schema: {
description: "Resync secret replication of secret imports",
security: [
{
bearerAuth: []
}
],
params: z.object({
secretImportId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.secretImportId)
}),
body: z.object({
workspaceId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.projectId),
environment: z.string().trim().describe(SECRET_IMPORTS.UPDATE.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.UPDATE.path)
}),
response: {
200: z.object({
message: z.string()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { message } = await server.services.secretImport.resyncSecretImportReplication({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.secretImportId,
...req.body,
projectId: req.body.workspaceId
});
return { message };
}
});
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretImports],
description: "Get secret imports",
security: [
{
bearerAuth: []
}
],
querystring: z.object({
workspaceId: z.string().trim().describe(SECRET_IMPORTS.LIST.projectId),
environment: z.string().trim().describe(SECRET_IMPORTS.LIST.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.LIST.path)
}),
response: {
200: z.object({
message: z.string(),
secretImports: SecretImportsSchema.omit({ importEnv: true })
.extend({
importEnv: z.object({ name: z.string(), slug: z.string(), id: z.string() })
})
.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const secretImports = await server.services.secretImport.getImports({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.query,
projectId: req.query.workspaceId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.query.workspaceId,
event: {
type: EventType.GET_SECRET_IMPORTS,
metadata: {
environment: req.query.environment,
folderId: secretImports?.[0]?.folderId,
numberOfImports: secretImports.length
}
}
});
return { message: "Successfully fetched secret imports", secretImports };
}
});
server.route({
url: "/:secretImportId",
method: "GET",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretImports],
description: "Get single secret import",
security: [
{
bearerAuth: []
}
],
params: z.object({
secretImportId: z.string().trim().describe(SECRET_IMPORTS.GET.secretImportId)
}),
response: {
200: z.object({
secretImport: SecretImportsSchema.omit({ importEnv: true }).extend({
environment: z.object({
id: z.string(),
name: z.string(),
slug: z.string()
}),
projectId: z.string(),
importEnv: z.object({ name: z.string(), slug: z.string(), id: z.string() }),
secretPath: z.string()
})
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const secretImport = await server.services.secretImport.getImportById({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
id: req.params.secretImportId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: secretImport.projectId,
event: {
type: EventType.GET_SECRET_IMPORT,
metadata: {
secretImportId: secretImport.id,
folderId: secretImport.folderId
}
}
});
return { secretImport };
}
});
server.route({
url: "/secrets",
method: "GET",
config: {
rateLimit: secretsLimit
},
schema: {
querystring: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash)
}),
response: {
200: z.object({
secrets: z
.object({
secretPath: z.string(),
environment: z.string(),
environmentInfo: z.object({
id: z.string(),
name: z.string(),
slug: z.string()
}),
folderId: z.string().optional(),
secrets: SecretsSchema.omit({ secretBlindIndex: true }).array()
})
.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const importedSecrets = await server.services.secretImport.getSecretsFromImports({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.query,
projectId: req.query.workspaceId
});
return { secrets: importedSecrets };
}
});
server.route({
url: "/secrets/raw",
method: "GET",
config: {
rateLimit: secretsLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretImports],
querystring: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash)
}),
response: {
200: z.object({
secrets: z
.object({
secretPath: z.string(),
environment: z.string(),
environmentInfo: z.object({
id: z.string(),
name: z.string(),
slug: z.string()
}),
folderId: z.string().optional(),
secrets: secretRawSchema.array()
})
.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const importedSecrets = await server.services.secretImport.getRawSecretsFromImports({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.query,
projectId: req.query.workspaceId
});
return { secrets: importedSecrets };
}
});
};

View File

@@ -50,8 +50,8 @@ import { registerProjectKeyRouter } from "./project-key-router";
import { registerProjectMembershipRouter } from "./project-membership-router"; import { registerProjectMembershipRouter } from "./project-membership-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 { registerDepreciatedSecretFolderRouter } from "./depreciated-secret-folder-router";
import { registerSecretImportRouter } from "./secret-import-router"; import { registerDepreciatedSecretImportRouter } from "./depreciated-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";
@@ -93,8 +93,8 @@ export const registerV1Routes = async (server: FastifyZodProvider) => {
await server.register(registerNotificationRouter, { prefix: "/notifications" }); await server.register(registerNotificationRouter, { prefix: "/notifications" });
await server.register(registerInviteOrgRouter, { prefix: "/invite-org" }); await server.register(registerInviteOrgRouter, { prefix: "/invite-org" });
await server.register(registerUserActionRouter, { prefix: "/user-action" }); await server.register(registerUserActionRouter, { prefix: "/user-action" });
await server.register(registerSecretImportRouter, { prefix: "/secret-imports" }); await server.register(registerDepreciatedSecretImportRouter, { prefix: "/secret-imports" });
await server.register(registerSecretFolderRouter, { prefix: "/folders" }); await server.register(registerDepreciatedSecretFolderRouter, { prefix: "/folders" });
await server.register( await server.register(
async (workflowIntegrationRouter) => { async (workflowIntegrationRouter) => {

View File

@@ -39,7 +39,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
body: z body: z
.object({ .object({
type: z.nativeEnum(WebhookType).default(WebhookType.GENERAL), type: z.nativeEnum(WebhookType).default(WebhookType.GENERAL),
workspaceId: z.string().trim(), projectId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
webhookUrl: z.string().url().trim(), webhookUrl: z.string().url().trim(),
webhookSecretKey: z.string().trim().optional(), webhookSecretKey: z.string().trim().optional(),
@@ -67,13 +67,12 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
projectId: req.body.workspaceId,
...req.body ...req.body
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
projectId: req.body.workspaceId, projectId: req.body.projectId,
event: { event: {
type: EventType.CREATE_WEBHOOK, type: EventType.CREATE_WEBHOOK,
metadata: { metadata: {
@@ -216,7 +215,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
schema: { schema: {
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), projectId: z.string().trim(),
environment: z.string().trim().optional(), environment: z.string().trim().optional(),
secretPath: z secretPath: z
.string() .string()
@@ -238,7 +237,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
...req.query, ...req.query,
projectId: req.query.workspaceId projectId: req.query.projectId
}); });
return { message: "Successfully fetched webhook", webhooks }; return { message: "Successfully fetched webhook", webhooks };
} }

View File

@@ -10,6 +10,8 @@ import { registerDepreciatedProjectMembershipRouter } from "./depreciated-projec
import { registerDepreciatedProjectRouter } from "./depreciated-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";
import { registerSecretFolderRouter } from "./secret-folder-router";
import { registerSecretImportRouter } from "./secret-import-router";
export const registerV2Routes = async (server: FastifyZodProvider) => { export const registerV2Routes = async (server: FastifyZodProvider) => {
await server.register(registerMfaRouter, { prefix: "/auth" }); await server.register(registerMfaRouter, { prefix: "/auth" });
@@ -33,6 +35,9 @@ export const registerV2Routes = async (server: FastifyZodProvider) => {
{ prefix: "/organizations" } { prefix: "/organizations" }
); );
await server.register(registerSecretFolderRouter, { prefix: "/folders" });
await server.register(registerSecretImportRouter, { prefix: "/secret-imports" });
// moved to v1/projects // moved to v1/projects
await server.register( await server.register(
async (projectServer) => { async (projectServer) => {

View File

@@ -28,7 +28,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
} }
], ],
body: z.object({ body: z.object({
workspaceId: z.string().trim().describe(FOLDERS.CREATE.workspaceId), projectId: z.string().trim().describe(FOLDERS.CREATE.projectId),
environment: z.string().trim().describe(FOLDERS.CREATE.environment), environment: z.string().trim().describe(FOLDERS.CREATE.environment),
name: z name: z
.string() .string()
@@ -43,17 +43,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
.default("/") .default("/")
.transform(prefixWithSlash) // Transformations get skipped if path is undefined .transform(prefixWithSlash) // Transformations get skipped if path is undefined
.transform(removeTrailingSlash) .transform(removeTrailingSlash)
.describe(FOLDERS.CREATE.path) .describe(FOLDERS.CREATE.path),
.optional(),
// backward compatibility with cli
directory: z
.string()
.trim()
.default("/")
.transform(prefixWithSlash) // Transformations get skipped if directory is undefined
.transform(removeTrailingSlash)
.describe(FOLDERS.CREATE.directory)
.optional(),
description: z.string().optional().nullable().describe(FOLDERS.CREATE.description) description: z.string().optional().nullable().describe(FOLDERS.CREATE.description)
}), }),
response: { response: {
@@ -66,27 +56,24 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const path = req.body.path || req.body.directory || "/";
const folder = await server.services.folder.createFolder({ const folder = await server.services.folder.createFolder({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
...req.body, ...req.body,
projectId: req.body.workspaceId,
path,
description: req.body.description description: req.body.description
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
projectId: req.body.workspaceId, projectId: req.body.projectId,
event: { event: {
type: EventType.CREATE_FOLDER, type: EventType.CREATE_FOLDER,
metadata: { metadata: {
environment: req.body.environment, environment: req.body.environment,
folderId: folder.id, folderId: folder.id,
folderName: folder.name, folderName: folder.name,
folderPath: path, folderPath: req.body.path,
...(req.body.description ? { description: req.body.description } : {}) ...(req.body.description ? { description: req.body.description } : {})
} }
} }
@@ -115,7 +102,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
folderId: z.string().describe(FOLDERS.UPDATE.folderId) folderId: z.string().describe(FOLDERS.UPDATE.folderId)
}), }),
body: z.object({ body: z.object({
workspaceId: z.string().trim().describe(FOLDERS.UPDATE.workspaceId), projectId: z.string().trim().describe(FOLDERS.UPDATE.projectId),
environment: z.string().trim().describe(FOLDERS.UPDATE.environment), environment: z.string().trim().describe(FOLDERS.UPDATE.environment),
name: z name: z
.string() .string()
@@ -130,17 +117,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
.default("/") .default("/")
.transform(prefixWithSlash) // Transformations get skipped if path is undefined .transform(prefixWithSlash) // Transformations get skipped if path is undefined
.transform(removeTrailingSlash) .transform(removeTrailingSlash)
.describe(FOLDERS.UPDATE.path) .describe(FOLDERS.UPDATE.path),
.optional(),
// backward compatibility with cli
directory: z
.string()
.trim()
.default("/")
.transform(prefixWithSlash) // Transformations get skipped if directory is undefined
.transform(removeTrailingSlash)
.describe(FOLDERS.UPDATE.directory)
.optional(),
description: z.string().optional().nullable().describe(FOLDERS.UPDATE.description) description: z.string().optional().nullable().describe(FOLDERS.UPDATE.description)
}), }),
response: { response: {
@@ -153,26 +130,23 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const path = req.body.path || req.body.directory || "/";
const { folder, old } = await server.services.folder.updateFolder({ const { folder, old } = await server.services.folder.updateFolder({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
...req.body, ...req.body,
projectId: req.body.workspaceId, id: req.params.folderId
id: req.params.folderId,
path
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
projectId: req.body.workspaceId, projectId: req.body.projectId,
event: { event: {
type: EventType.UPDATE_FOLDER, type: EventType.UPDATE_FOLDER,
metadata: { metadata: {
environment: req.body.environment, environment: req.body.environment,
folderId: folder.id, folderId: folder.id,
folderPath: path, folderPath: req.body.path,
newFolderName: folder.name, newFolderName: folder.name,
oldFolderName: old.name oldFolderName: old.name
} }
@@ -198,7 +172,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
} }
], ],
body: z.object({ body: z.object({
projectSlug: z.string().trim().describe(FOLDERS.UPDATE.projectSlug), projectId: z.string().trim().describe(FOLDERS.UPDATE.projectId),
folders: z folders: z
.object({ .object({
id: z.string().describe(FOLDERS.UPDATE.folderId), id: z.string().describe(FOLDERS.UPDATE.folderId),
@@ -281,7 +255,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
folderIdOrName: z.string().describe(FOLDERS.DELETE.folderIdOrName) folderIdOrName: z.string().describe(FOLDERS.DELETE.folderIdOrName)
}), }),
body: z.object({ body: z.object({
workspaceId: z.string().trim().describe(FOLDERS.DELETE.workspaceId), projectId: z.string().trim().describe(FOLDERS.DELETE.projectId),
environment: z.string().trim().describe(FOLDERS.DELETE.environment), environment: z.string().trim().describe(FOLDERS.DELETE.environment),
path: z path: z
.string() .string()
@@ -290,16 +264,6 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
.transform(prefixWithSlash) // Transformations get skipped if path is undefined .transform(prefixWithSlash) // Transformations get skipped if path is undefined
.transform(removeTrailingSlash) .transform(removeTrailingSlash)
.describe(FOLDERS.DELETE.path) .describe(FOLDERS.DELETE.path)
.optional(),
// keep this here as cli need directory
directory: z
.string()
.trim()
.default("/")
.transform(prefixWithSlash) // Transformations get skipped if directory is undefined
.transform(removeTrailingSlash)
.describe(FOLDERS.DELETE.directory)
.optional()
}), }),
response: { response: {
200: z.object({ 200: z.object({
@@ -309,26 +273,23 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const path = req.body.path || req.body.directory || "/";
const folder = await server.services.folder.deleteFolder({ const folder = await server.services.folder.deleteFolder({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
...req.body, ...req.body,
projectId: req.body.workspaceId, idOrName: req.params.folderIdOrName
idOrName: req.params.folderIdOrName,
path
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
projectId: req.body.workspaceId, projectId: req.body.projectId,
event: { event: {
type: EventType.DELETE_FOLDER, type: EventType.DELETE_FOLDER,
metadata: { metadata: {
environment: req.body.environment, environment: req.body.environment,
folderId: folder.id, folderId: folder.id,
folderPath: path, folderPath: req.body.path,
folderName: folder.name folderName: folder.name
} }
} }
@@ -353,7 +314,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
} }
], ],
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim().describe(FOLDERS.LIST.workspaceId), projectId: z.string().trim().describe(FOLDERS.LIST.projectId),
environment: z.string().trim().describe(FOLDERS.LIST.environment), environment: z.string().trim().describe(FOLDERS.LIST.environment),
lastSecretModified: z.string().datetime().trim().optional().describe(FOLDERS.LIST.lastSecretModified), lastSecretModified: z.string().datetime().trim().optional().describe(FOLDERS.LIST.lastSecretModified),
path: z path: z
@@ -361,16 +322,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
.trim() .trim()
.transform(prefixWithSlash) // Transformations get skipped if path is undefined .transform(prefixWithSlash) // Transformations get skipped if path is undefined
.transform(removeTrailingSlash) .transform(removeTrailingSlash)
.describe(FOLDERS.LIST.path) .describe(FOLDERS.LIST.path),
.optional(),
// backward compatibility with cli
directory: z
.string()
.trim()
.transform(prefixWithSlash) // Transformations get skipped if directory is undefined
.transform(removeTrailingSlash)
.describe(FOLDERS.LIST.directory)
.optional(),
recursive: booleanSchema.default(false).describe(FOLDERS.LIST.recursive) recursive: booleanSchema.default(false).describe(FOLDERS.LIST.recursive)
}), }),
response: { response: {
@@ -383,15 +335,12 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const path = req.query.path || req.query.directory || "/";
const folders = await server.services.folder.getFolders({ const folders = await server.services.folder.getFolders({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
...req.query, ...req.query
projectId: req.query.workspaceId,
path
}); });
return { folders }; return { folders };
} }

View File

@@ -1,6 +1,6 @@
import { z } from "zod"; import { z } from "zod";
import { SecretImportsSchema, SecretsSchema } from "@app/db/schemas"; import { SecretImportsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, SECRET_IMPORTS } from "@app/lib/api-docs"; import { ApiDocsTags, SECRET_IMPORTS } from "@app/lib/api-docs";
import { removeTrailingSlash } from "@app/lib/fn"; import { removeTrailingSlash } from "@app/lib/fn";
@@ -27,7 +27,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
} }
], ],
body: z.object({ body: z.object({
workspaceId: z.string().trim().describe(SECRET_IMPORTS.CREATE.workspaceId), projectId: z.string().trim().describe(SECRET_IMPORTS.CREATE.projectId),
environment: z.string().trim().describe(SECRET_IMPORTS.CREATE.environment), environment: z.string().trim().describe(SECRET_IMPORTS.CREATE.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.CREATE.path), path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.CREATE.path),
import: z.object({ import: z.object({
@@ -55,13 +55,13 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
...req.body, ...req.body,
projectId: req.body.workspaceId, projectId: req.body.projectId,
data: req.body.import data: req.body.import
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
projectId: req.body.workspaceId, projectId: req.body.projectId,
event: { event: {
type: EventType.CREATE_SECRET_IMPORT, type: EventType.CREATE_SECRET_IMPORT,
metadata: { metadata: {
@@ -97,7 +97,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
secretImportId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.secretImportId) secretImportId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.secretImportId)
}), }),
body: z.object({ body: z.object({
workspaceId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.workspaceId), projectId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.projectId),
environment: z.string().trim().describe(SECRET_IMPORTS.UPDATE.environment), environment: z.string().trim().describe(SECRET_IMPORTS.UPDATE.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.UPDATE.path), path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.UPDATE.path),
import: z.object({ import: z.object({
@@ -131,13 +131,13 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
id: req.params.secretImportId, id: req.params.secretImportId,
...req.body, ...req.body,
projectId: req.body.workspaceId, projectId: req.body.projectId,
data: req.body.import data: req.body.import
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
projectId: req.body.workspaceId, projectId: req.body.projectId,
event: { event: {
type: EventType.UPDATE_SECRET_IMPORT, type: EventType.UPDATE_SECRET_IMPORT,
metadata: { metadata: {
@@ -173,7 +173,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
secretImportId: z.string().trim().describe(SECRET_IMPORTS.DELETE.secretImportId) secretImportId: z.string().trim().describe(SECRET_IMPORTS.DELETE.secretImportId)
}), }),
body: z.object({ body: z.object({
workspaceId: z.string().trim().describe(SECRET_IMPORTS.DELETE.workspaceId), projectId: z.string().trim().describe(SECRET_IMPORTS.DELETE.projectId),
environment: z.string().trim().describe(SECRET_IMPORTS.DELETE.environment), environment: z.string().trim().describe(SECRET_IMPORTS.DELETE.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.DELETE.path) path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.DELETE.path)
}), }),
@@ -197,12 +197,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
id: req.params.secretImportId, id: req.params.secretImportId,
...req.body, ...req.body,
projectId: req.body.workspaceId projectId: req.body.projectId
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
projectId: req.body.workspaceId, projectId: req.body.projectId,
event: { event: {
type: EventType.DELETE_SECRET_IMPORT, type: EventType.DELETE_SECRET_IMPORT,
metadata: { metadata: {
@@ -236,7 +236,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
secretImportId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.secretImportId) secretImportId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.secretImportId)
}), }),
body: z.object({ body: z.object({
workspaceId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.workspaceId), projectId: z.string().trim().describe(SECRET_IMPORTS.UPDATE.projectId),
environment: z.string().trim().describe(SECRET_IMPORTS.UPDATE.environment), environment: z.string().trim().describe(SECRET_IMPORTS.UPDATE.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.UPDATE.path) path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.UPDATE.path)
}), }),
@@ -255,7 +255,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
id: req.params.secretImportId, id: req.params.secretImportId,
...req.body, ...req.body,
projectId: req.body.workspaceId projectId: req.body.projectId
}); });
return { message }; return { message };
@@ -278,7 +278,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
} }
], ],
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim().describe(SECRET_IMPORTS.LIST.workspaceId), projectId: z.string().trim().describe(SECRET_IMPORTS.LIST.projectId),
environment: z.string().trim().describe(SECRET_IMPORTS.LIST.environment), environment: z.string().trim().describe(SECRET_IMPORTS.LIST.environment),
path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.LIST.path) path: z.string().trim().default("/").transform(removeTrailingSlash).describe(SECRET_IMPORTS.LIST.path)
}), }),
@@ -301,12 +301,12 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
...req.query, ...req.query,
projectId: req.query.workspaceId projectId: req.query.projectId
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
projectId: req.query.workspaceId, projectId: req.query.projectId,
event: { event: {
type: EventType.GET_SECRET_IMPORTS, type: EventType.GET_SECRET_IMPORTS,
metadata: { metadata: {
@@ -386,55 +386,11 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
config: { config: {
rateLimit: secretsLimit rateLimit: secretsLimit
}, },
schema: {
querystring: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash)
}),
response: {
200: z.object({
secrets: z
.object({
secretPath: z.string(),
environment: z.string(),
environmentInfo: z.object({
id: z.string(),
name: z.string(),
slug: z.string()
}),
folderId: z.string().optional(),
secrets: SecretsSchema.omit({ secretBlindIndex: true }).array()
})
.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.API_KEY, AuthMode.SERVICE_TOKEN, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const importedSecrets = await server.services.secretImport.getSecretsFromImports({
actorId: req.permission.id,
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
...req.query,
projectId: req.query.workspaceId
});
return { secrets: importedSecrets };
}
});
server.route({
url: "/secrets/raw",
method: "GET",
config: {
rateLimit: secretsLimit
},
schema: { schema: {
hide: false, hide: false,
tags: [ApiDocsTags.SecretImports], tags: [ApiDocsTags.SecretImports],
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), projectId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
path: z.string().trim().default("/").transform(removeTrailingSlash) path: z.string().trim().default("/").transform(removeTrailingSlash)
}), }),
@@ -463,8 +419,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
actor: req.permission.type, actor: req.permission.type,
actorAuthMethod: req.permission.authMethod, actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId, actorOrgId: req.permission.orgId,
...req.query, ...req.query
projectId: req.query.workspaceId
}); });
return { secrets: importedSecrets }; return { secrets: importedSecrets };
} }

View File

@@ -11,7 +11,7 @@ export const useCreateSecretApprovalPolicy = () => {
return useMutation<object, object, TCreateSecretPolicyDTO>({ return useMutation<object, object, TCreateSecretPolicyDTO>({
mutationFn: async ({ mutationFn: async ({
environments, environments,
workspaceId, projectId,
approvals, approvals,
approvers, approvers,
bypassers, bypassers,
@@ -20,9 +20,9 @@ export const useCreateSecretApprovalPolicy = () => {
enforcementLevel, enforcementLevel,
allowedSelfApprovals allowedSelfApprovals
}) => { }) => {
const { data } = await apiRequest.post("/api/v1/secret-approvals", { const { data } = await apiRequest.post("/api/v2/secret-approvals", {
environments, environments,
workspaceId, projectId,
approvals, approvals,
approvers, approvers,
bypassers, bypassers,
@@ -33,9 +33,9 @@ export const useCreateSecretApprovalPolicy = () => {
}); });
return data; return data;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { projectId }) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: secretApprovalKeys.getApprovalPolicies(workspaceId) queryKey: secretApprovalKeys.getApprovalPolicies(projectId)
}); });
} }
}); });
@@ -56,7 +56,7 @@ export const useUpdateSecretApprovalPolicy = () => {
allowedSelfApprovals, allowedSelfApprovals,
environments environments
}) => { }) => {
const { data } = await apiRequest.patch(`/api/v1/secret-approvals/${id}`, { const { data } = await apiRequest.patch(`/api/v2/secret-approvals/${id}`, {
approvals, approvals,
approvers, approvers,
bypassers, bypassers,
@@ -68,9 +68,9 @@ export const useUpdateSecretApprovalPolicy = () => {
}); });
return data; return data;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { projectId }) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: secretApprovalKeys.getApprovalPolicies(workspaceId) queryKey: secretApprovalKeys.getApprovalPolicies(projectId)
}); });
} }
}); });
@@ -81,12 +81,12 @@ export const useDeleteSecretApprovalPolicy = () => {
return useMutation<object, object, TDeleteSecretPolicyDTO>({ return useMutation<object, object, TDeleteSecretPolicyDTO>({
mutationFn: async ({ id }) => { mutationFn: async ({ id }) => {
const { data } = await apiRequest.delete(`/api/v1/secret-approvals/${id}`); const { data } = await apiRequest.delete(`/api/v2/secret-approvals/${id}`);
return data; return data;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { projectId }) => {
queryClient.invalidateQueries({ queryClient.invalidateQueries({
queryKey: secretApprovalKeys.getApprovalPolicies(workspaceId) queryKey: secretApprovalKeys.getApprovalPolicies(projectId)
}); });
} }
}); });

View File

@@ -10,54 +10,53 @@ import {
} from "./types"; } from "./types";
export const secretApprovalKeys = { export const secretApprovalKeys = {
getApprovalPolicies: (workspaceId: string) => getApprovalPolicies: (projectId: string) => [{ projectId }, "secret-approval-policies"] as const,
[{ workspaceId }, "secret-approval-policies"] as const, getApprovalPolicyOfABoard: (projectId: string, environment: string, secretPath: string) => [
getApprovalPolicyOfABoard: (workspaceId: string, environment: string, secretPath: string) => [ { projectId, environment, secretPath },
{ workspaceId, environment, secretPath },
"Secret-approval-policy" "Secret-approval-policy"
] ]
}; };
const fetchApprovalPolicies = async (workspaceId: string) => { const fetchApprovalPolicies = async (projectId: string) => {
const { data } = await apiRequest.get<{ approvals: TSecretApprovalPolicy[] }>( const { data } = await apiRequest.get<{ approvals: TSecretApprovalPolicy[] }>(
"/api/v1/secret-approvals", "/api/v2/secret-approvals",
{ params: { workspaceId } } { params: { projectId } }
); );
return data.approvals; return data.approvals;
}; };
export const useGetSecretApprovalPolicies = ({ export const useGetSecretApprovalPolicies = ({
workspaceId, projectId,
options = {} options = {}
}: TGetSecretApprovalPoliciesDTO & TReactQueryOptions) => }: TGetSecretApprovalPoliciesDTO & TReactQueryOptions) =>
useQuery({ useQuery({
queryKey: secretApprovalKeys.getApprovalPolicies(workspaceId), queryKey: secretApprovalKeys.getApprovalPolicies(projectId),
queryFn: () => fetchApprovalPolicies(workspaceId), queryFn: () => fetchApprovalPolicies(projectId),
...options, ...options,
enabled: Boolean(workspaceId) && (options?.enabled ?? true) enabled: Boolean(projectId) && (options?.enabled ?? true)
}); });
const fetchApprovalPolicyOfABoard = async ( const fetchApprovalPolicyOfABoard = async (
workspaceId: string, projectId: string,
environment: string, environment: string,
secretPath: string secretPath: string
) => { ) => {
const { data } = await apiRequest.get<{ policy: TSecretApprovalPolicy }>( const { data } = await apiRequest.get<{ policy: TSecretApprovalPolicy }>(
"/api/v1/secret-approvals/board", "/api/v2/secret-approvals/board",
{ params: { workspaceId, environment, secretPath } } { params: { projectId, environment, secretPath } }
); );
return data.policy || ""; return data.policy || "";
}; };
export const useGetSecretApprovalPolicyOfABoard = ({ export const useGetSecretApprovalPolicyOfABoard = ({
workspaceId, projectId,
secretPath = "/", secretPath = "/",
environment, environment,
options = {} options = {}
}: TGetSecretApprovalPolicyOfBoardDTO & TReactQueryOptions) => }: TGetSecretApprovalPolicyOfBoardDTO & TReactQueryOptions) =>
useQuery({ useQuery({
queryKey: secretApprovalKeys.getApprovalPolicyOfABoard(workspaceId, environment, secretPath), queryKey: secretApprovalKeys.getApprovalPolicyOfABoard(projectId, environment, secretPath),
queryFn: () => fetchApprovalPolicyOfABoard(workspaceId, environment, secretPath), queryFn: () => fetchApprovalPolicyOfABoard(projectId, environment, secretPath),
...options, ...options,
enabled: Boolean(workspaceId && secretPath && environment) && (options?.enabled ?? true) enabled: Boolean(projectId && secretPath && environment) && (options?.enabled ?? true)
}); });

View File

@@ -3,7 +3,7 @@ import { WorkspaceEnv } from "../workspace/types";
export type TSecretApprovalPolicy = { export type TSecretApprovalPolicy = {
id: string; id: string;
workspace: string; project: string;
name: string; name: string;
environments: WorkspaceEnv[]; environments: WorkspaceEnv[];
secretPath?: string; secretPath?: string;
@@ -36,17 +36,17 @@ export type Bypasser = {
}; };
export type TGetSecretApprovalPoliciesDTO = { export type TGetSecretApprovalPoliciesDTO = {
workspaceId: string; projectId: string;
}; };
export type TGetSecretApprovalPolicyOfBoardDTO = { export type TGetSecretApprovalPolicyOfBoardDTO = {
workspaceId: string; projectId: string;
environment: string; environment: string;
secretPath: string; secretPath: string;
}; };
export type TCreateSecretPolicyDTO = { export type TCreateSecretPolicyDTO = {
workspaceId: string; projectId: string;
name?: string; name?: string;
environments: string[]; environments: string[];
secretPath: string; secretPath: string;
@@ -67,12 +67,12 @@ export type TUpdateSecretPolicyDTO = {
allowedSelfApprovals?: boolean; allowedSelfApprovals?: boolean;
enforcementLevel?: EnforcementLevel; enforcementLevel?: EnforcementLevel;
// for invalidating list // for invalidating list
workspaceId: string; projectId: string;
environments?: string[]; environments?: string[];
}; };
export type TDeleteSecretPolicyDTO = { export type TDeleteSecretPolicyDTO = {
id: string; id: string;
// for invalidating list // for invalidating list
workspaceId: string; projectId: string;
}; };

View File

@@ -36,9 +36,9 @@ export const useUpdateSecretApprovalRequestStatus = () => {
}); });
return data; return data;
}, },
onSuccess: (_, { id, workspaceId }) => { onSuccess: (_, { id, projectId }) => {
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.detail({ id }) }); queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.detail({ id }) });
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ workspaceId }) }); queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ projectId }) });
} }
}); });
}; };
@@ -53,10 +53,10 @@ export const usePerformSecretApprovalRequestMerge = () => {
}); });
return data; return data;
}, },
onSuccess: (_, { id, workspaceId }) => { onSuccess: (_, { id, projectId }) => {
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.detail({ id }) }); queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.detail({ id }) });
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.list({ workspaceId }) }); queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.list({ projectId }) });
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ workspaceId }) }); queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ projectId }) });
} }
}); });
}; };

View File

@@ -14,7 +14,7 @@ import {
export const secretApprovalRequestKeys = { export const secretApprovalRequestKeys = {
list: ({ list: ({
workspaceId, projectId,
environment, environment,
status, status,
committer, committer,
@@ -23,20 +23,20 @@ export const secretApprovalRequestKeys = {
search search
}: TGetSecretApprovalRequestList) => }: TGetSecretApprovalRequestList) =>
[ [
{ workspaceId, environment, status, committer, offset, limit, search }, { projectId, environment, status, committer, offset, limit, search },
"secret-approval-requests" "secret-approval-requests"
] as const, ] as const,
detail: ({ id }: Omit<TGetSecretApprovalRequestDetails, "decryptKey">) => detail: ({ id }: Omit<TGetSecretApprovalRequestDetails, "decryptKey">) =>
[{ id }, "secret-approval-request-detail"] as const, [{ id }, "secret-approval-request-detail"] as const,
count: ({ workspaceId, policyId }: TGetSecretApprovalRequestCount) => [ count: ({ projectId, policyId }: TGetSecretApprovalRequestCount) => [
{ workspaceId }, { projectId },
"secret-approval-request-count", "secret-approval-request-count",
...(policyId ? [policyId] : []) ...(policyId ? [policyId] : [])
] ]
}; };
const fetchSecretApprovalRequestList = async ({ const fetchSecretApprovalRequestList = async ({
workspaceId, projectId,
environment, environment,
committer, committer,
status = "open", status = "open",
@@ -49,7 +49,7 @@ const fetchSecretApprovalRequestList = async ({
totalCount: number; totalCount: number;
}>("/api/v1/secret-approval-requests", { }>("/api/v1/secret-approval-requests", {
params: { params: {
workspaceId, projectId,
environment, environment,
committer, committer,
status, status,
@@ -63,7 +63,7 @@ const fetchSecretApprovalRequestList = async ({
}; };
export const useGetSecretApprovalRequests = ({ export const useGetSecretApprovalRequests = ({
workspaceId, projectId,
environment, environment,
options = {}, options = {},
status, status,
@@ -74,7 +74,7 @@ export const useGetSecretApprovalRequests = ({
}: TGetSecretApprovalRequestList & TReactQueryOptions) => }: TGetSecretApprovalRequestList & TReactQueryOptions) =>
useQuery({ useQuery({
queryKey: secretApprovalRequestKeys.list({ queryKey: secretApprovalRequestKeys.list({
workspaceId, projectId,
environment, environment,
committer, committer,
status, status,
@@ -84,7 +84,7 @@ export const useGetSecretApprovalRequests = ({
}), }),
queryFn: () => queryFn: () =>
fetchSecretApprovalRequestList({ fetchSecretApprovalRequestList({
workspaceId, projectId,
environment, environment,
status, status,
committer, committer,
@@ -92,7 +92,7 @@ export const useGetSecretApprovalRequests = ({
offset, offset,
search search
}), }),
enabled: Boolean(workspaceId) && (options?.enabled ?? true), enabled: Boolean(projectId) && (options?.enabled ?? true),
placeholderData: (previousData) => previousData placeholderData: (previousData) => previousData
}); });
@@ -127,19 +127,19 @@ export const useGetSecretApprovalRequestDetails = ({
}); });
const fetchSecretApprovalRequestCount = async ({ const fetchSecretApprovalRequestCount = async ({
workspaceId, projectId,
policyId policyId
}: TGetSecretApprovalRequestCount) => { }: TGetSecretApprovalRequestCount) => {
const { data } = await apiRequest.get<{ approvals: TSecretApprovalRequestCount }>( const { data } = await apiRequest.get<{ approvals: TSecretApprovalRequestCount }>(
"/api/v1/secret-approval-requests/count", "/api/v1/secret-approval-requests/count",
{ params: { workspaceId, policyId } } { params: { projectId, policyId } }
); );
return data.approvals; return data.approvals;
}; };
export const useGetSecretApprovalRequestCount = ({ export const useGetSecretApprovalRequestCount = ({
workspaceId, projectId,
policyId, policyId,
options = {} options = {}
}: TGetSecretApprovalRequestCount & { }: TGetSecretApprovalRequestCount & {
@@ -154,8 +154,8 @@ export const useGetSecretApprovalRequestCount = ({
>; >;
}) => }) =>
useQuery({ useQuery({
queryKey: secretApprovalRequestKeys.count({ workspaceId, policyId }), queryKey: secretApprovalRequestKeys.count({ projectId, policyId }),
refetchInterval: 15000, refetchInterval: 15000,
queryFn: () => fetchSecretApprovalRequestCount({ workspaceId, policyId }), queryFn: () => fetchSecretApprovalRequestCount({ projectId, policyId }),
enabled: Boolean(workspaceId) && (options?.enabled ?? true) enabled: Boolean(projectId) && (options?.enabled ?? true)
}); });

View File

@@ -55,7 +55,7 @@ export type TSecretApprovalRequest = {
username: string; username: string;
isOrgMembershipActive: boolean; isOrgMembershipActive: boolean;
}[]; }[];
workspace: string; project: string;
environment: string; environment: string;
folderId: string; folderId: string;
secretPath: string; secretPath: string;
@@ -110,7 +110,7 @@ export type TSecretApprovalRequestCount = {
}; };
export type TGetSecretApprovalRequestList = { export type TGetSecretApprovalRequestList = {
workspaceId: string; projectId: string;
environment?: string; environment?: string;
status?: "open" | "close"; status?: "open" | "close";
committer?: string; committer?: string;
@@ -120,7 +120,7 @@ export type TGetSecretApprovalRequestList = {
}; };
export type TGetSecretApprovalRequestCount = { export type TGetSecretApprovalRequestCount = {
workspaceId: string; projectId: string;
policyId?: string; policyId?: string;
}; };
@@ -137,11 +137,11 @@ export type TUpdateSecretApprovalReviewStatusDTO = {
export type TUpdateSecretApprovalRequestStatusDTO = { export type TUpdateSecretApprovalRequestStatusDTO = {
status: "open" | "close"; status: "open" | "close";
id: string; id: string;
workspaceId: string; projectId: string;
}; };
export type TPerformSecretApprovalRequestMerge = { export type TPerformSecretApprovalRequestMerge = {
id: string; id: string;
workspaceId: string; projectId: string;
bypassReason?: string; bypassReason?: string;
}; };

View File

@@ -30,10 +30,10 @@ export const folderQueryKeys = {
["secret-folders", "environment", projectId] as const ["secret-folders", "environment", projectId] as const
}; };
const fetchProjectFolders = async (workspaceId: string, environment: string, path = "/") => { const fetchProjectFolders = async (projectId: string, environment: string, path = "/") => {
const { data } = await apiRequest.get<{ folders: TSecretFolder[] }>("/api/v1/folders", { const { data } = await apiRequest.get<{ folders: TSecretFolder[] }>("/api/v2/folders", {
params: { params: {
workspaceId, projectId,
environment, environment,
path path
} }
@@ -145,9 +145,9 @@ export const useCreateFolder = () => {
return useMutation<object, object, TCreateFolderDTO>({ return useMutation<object, object, TCreateFolderDTO>({
mutationFn: async (dto) => { mutationFn: async (dto) => {
const { data } = await apiRequest.post("/api/v1/folders", { const { data } = await apiRequest.post("/api/v2/folders", {
...dto, ...dto,
workspaceId: dto.projectId projectId: dto.projectId
}); });
return data; return data;
}, },
@@ -179,10 +179,10 @@ export const useUpdateFolder = () => {
return useMutation<object, object, TUpdateFolderDTO>({ return useMutation<object, object, TUpdateFolderDTO>({
mutationFn: async ({ path = "/", folderId, name, environment, projectId, description }) => { mutationFn: async ({ path = "/", folderId, name, environment, projectId, description }) => {
const { data } = await apiRequest.patch(`/api/v1/folders/${folderId}`, { const { data } = await apiRequest.patch(`/api/v2/folders/${folderId}`, {
name, name,
environment, environment,
workspaceId: projectId, projectId,
path, path,
description description
}); });
@@ -219,10 +219,10 @@ export const useDeleteFolder = () => {
return useMutation<object, object, TDeleteFolderDTO>({ return useMutation<object, object, TDeleteFolderDTO>({
mutationFn: async ({ path = "/", folderId, environment, projectId }) => { mutationFn: async ({ path = "/", folderId, environment, projectId }) => {
const { data } = await apiRequest.delete(`/api/v1/folders/${folderId}`, { const { data } = await apiRequest.delete(`/api/v2/folders/${folderId}`, {
data: { data: {
environment, environment,
workspaceId: projectId, projectId,
path path
} }
}); });
@@ -259,7 +259,7 @@ export const useUpdateFolderBatch = () => {
return useMutation<object, object, TUpdateFolderBatchDTO>({ return useMutation<object, object, TUpdateFolderBatchDTO>({
mutationFn: async ({ projectSlug, folders }) => { mutationFn: async ({ projectSlug, folders }) => {
const { data } = await apiRequest.patch("/api/v1/folders/batch", { const { data } = await apiRequest.patch("/api/v2/folders/batch", {
projectSlug, projectSlug,
folders folders
}); });

View File

@@ -16,10 +16,10 @@ export const useCreateSecretImport = () => {
return useMutation<object, object, TCreateSecretImportDTO>({ return useMutation<object, object, TCreateSecretImportDTO>({
mutationFn: async ({ import: secretImport, environment, isReplication, projectId, path }) => { mutationFn: async ({ import: secretImport, environment, isReplication, projectId, path }) => {
const { data } = await apiRequest.post("/api/v1/secret-imports", { const { data } = await apiRequest.post("/api/v2/secret-imports", {
import: secretImport, import: secretImport,
environment, environment,
workspaceId: projectId, projectId,
path, path,
isReplication isReplication
}); });
@@ -44,11 +44,11 @@ export const useUpdateSecretImport = () => {
return useMutation<object, object, TUpdateSecretImportDTO>({ return useMutation<object, object, TUpdateSecretImportDTO>({
mutationFn: async ({ environment, import: secretImports, projectId, path, id }) => { mutationFn: async ({ environment, import: secretImports, projectId, path, id }) => {
const { data } = await apiRequest.patch(`/api/v1/secret-imports/${id}`, { const { data } = await apiRequest.patch(`/api/v2/secret-imports/${id}`, {
import: secretImports, import: secretImports,
environment, environment,
path, path,
workspaceId: projectId projectId
}); });
return data; return data;
}, },
@@ -69,10 +69,10 @@ export const useUpdateSecretImport = () => {
export const useResyncSecretReplication = () => { export const useResyncSecretReplication = () => {
return useMutation<object, object, TResyncSecretReplicationDTO>({ return useMutation<object, object, TResyncSecretReplicationDTO>({
mutationFn: async ({ environment, projectId, path, id }) => { mutationFn: async ({ environment, projectId, path, id }) => {
const { data } = await apiRequest.post(`/api/v1/secret-imports/${id}/replication-resync`, { const { data } = await apiRequest.post(`/api/v2/secret-imports/${id}/replication-resync`, {
environment, environment,
path, path,
workspaceId: projectId projectId
}); });
return data; return data;
} }
@@ -84,9 +84,9 @@ export const useDeleteSecretImport = () => {
return useMutation<object, object, TDeleteSecretImportDTO>({ return useMutation<object, object, TDeleteSecretImportDTO>({
mutationFn: async ({ id, projectId, path, environment }) => { mutationFn: async ({ id, projectId, path, environment }) => {
const { data } = await apiRequest.delete(`/api/v1/secret-imports/${id}`, { const { data } = await apiRequest.delete(`/api/v2/secret-imports/${id}`, {
data: { data: {
workspaceId: projectId, projectId,
path, path,
environment environment
} }

View File

@@ -30,10 +30,10 @@ export const secretImportKeys = {
const fetchSecretImport = async ({ projectId, environment, path = "/" }: TGetSecretImports) => { const fetchSecretImport = async ({ projectId, environment, path = "/" }: TGetSecretImports) => {
const { data } = await apiRequest.get<{ secretImports: TSecretImport[] }>( const { data } = await apiRequest.get<{ secretImports: TSecretImport[] }>(
"/api/v1/secret-imports", "/api/v2/secret-imports",
{ {
params: { params: {
workspaceId: projectId, projectId,
environment, environment,
path path
} }
@@ -65,16 +65,12 @@ export const useGetSecretImports = ({
queryFn: () => fetchSecretImport({ path, projectId, environment }) queryFn: () => fetchSecretImport({ path, projectId, environment })
}); });
const fetchImportedSecrets = async ( const fetchImportedSecrets = async (projectId: string, environment: string, directory?: string) => {
workspaceId: string,
environment: string,
directory?: string
) => {
const { data } = await apiRequest.get<{ secrets: TImportedSecrets[] }>( const { data } = await apiRequest.get<{ secrets: TImportedSecrets[] }>(
"/api/v1/secret-imports/secrets/raw", "/api/v2/secret-imports/secrets",
{ {
params: { params: {
workspaceId, projectId,
environment, environment,
path: directory path: directory
} }
@@ -89,10 +85,10 @@ const fetchImportedFolders = async ({
path path
}: TGetImportedFoldersByEnvDTO) => { }: TGetImportedFoldersByEnvDTO) => {
const { data } = await apiRequest.get<{ secretImports: TSecretImport[] }>( const { data } = await apiRequest.get<{ secretImports: TSecretImport[] }>(
"/api/v1/secret-imports", "/api/v2/secret-imports",
{ {
params: { params: {
workspaceId: projectId, projectId,
environment, environment,
path path
} }

View File

@@ -13,8 +13,8 @@ export const useCreateWebhook = () => {
const { data } = await apiRequest.post("/api/v1/webhooks", dto); const { data } = await apiRequest.post("/api/v1/webhooks", dto);
return data; return data;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { projectId }) => {
queryClient.invalidateQueries({ queryKey: queryKeys.getWebhooks(workspaceId) }); queryClient.invalidateQueries({ queryKey: queryKeys.getWebhooks(projectId) });
} }
}); });
}; };
@@ -27,11 +27,11 @@ export const useTestWebhook = () => {
const { data } = await apiRequest.post(`/api/v1/webhooks/${webhookId}/test`); const { data } = await apiRequest.post(`/api/v1/webhooks/${webhookId}/test`);
return data; return data;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { projectId }) => {
queryClient.invalidateQueries({ queryKey: queryKeys.getWebhooks(workspaceId) }); queryClient.invalidateQueries({ queryKey: queryKeys.getWebhooks(projectId) });
}, },
onError: (_, { workspaceId }) => { onError: (_, { projectId }) => {
queryClient.invalidateQueries({ queryKey: queryKeys.getWebhooks(workspaceId) }); queryClient.invalidateQueries({ queryKey: queryKeys.getWebhooks(projectId) });
} }
}); });
}; };
@@ -46,8 +46,8 @@ export const useUpdateWebhook = () => {
}); });
return data; return data;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { projectId }) => {
queryClient.invalidateQueries({ queryKey: queryKeys.getWebhooks(workspaceId) }); queryClient.invalidateQueries({ queryKey: queryKeys.getWebhooks(projectId) });
} }
}); });
}; };
@@ -60,8 +60,8 @@ export const useDeleteWebhook = () => {
const { data } = await apiRequest.delete(`/api/v1/webhooks/${dto.webhookId}`); const { data } = await apiRequest.delete(`/api/v1/webhooks/${dto.webhookId}`);
return data; return data;
}, },
onSuccess: (_, { workspaceId }) => { onSuccess: (_, { projectId }) => {
queryClient.invalidateQueries({ queryKey: queryKeys.getWebhooks(workspaceId) }); queryClient.invalidateQueries({ queryKey: queryKeys.getWebhooks(projectId) });
} }
}); });
}; };

View File

@@ -23,7 +23,7 @@ export type TWebhook = {
}; };
export type TCreateWebhookDto = { export type TCreateWebhookDto = {
workspaceId: string; projectId: string;
environment: string; environment: string;
webhookUrl: string; webhookUrl: string;
webhookSecretKey?: string; webhookSecretKey?: string;
@@ -33,16 +33,16 @@ export type TCreateWebhookDto = {
export type TUpdateWebhookDto = { export type TUpdateWebhookDto = {
webhookId: string; webhookId: string;
workspaceId: string; projectId: string;
isDisabled?: boolean; isDisabled?: boolean;
}; };
export type TDeleteWebhookDto = { export type TDeleteWebhookDto = {
webhookId: string; webhookId: string;
workspaceId: string; projectId: string;
}; };
export type TTestWebhookDTO = { export type TTestWebhookDTO = {
webhookId: string; webhookId: string;
workspaceId: string; projectId: string;
}; };

View File

@@ -62,7 +62,7 @@ export const WebhooksTab = withProjectPermission(
try { try {
await createWebhook({ await createWebhook({
...data, ...data,
workspaceId projectId: workspaceId
}); });
handlePopUpClose("addWebhook"); handlePopUpClose("addWebhook");
createNotification({ createNotification({
@@ -82,7 +82,7 @@ export const WebhooksTab = withProjectPermission(
try { try {
await updateWebhook({ await updateWebhook({
webhookId, webhookId,
workspaceId, projectId: workspaceId,
isDisabled isDisabled
}); });
createNotification({ createNotification({
@@ -103,7 +103,7 @@ export const WebhooksTab = withProjectPermission(
const webhookId = popUp?.deleteWebhook?.data as string; const webhookId = popUp?.deleteWebhook?.data as string;
await deleteWebhook({ await deleteWebhook({
webhookId, webhookId,
workspaceId projectId: workspaceId
}); });
handlePopUpClose("deleteWebhook"); handlePopUpClose("deleteWebhook");
createNotification({ createNotification({
@@ -123,7 +123,7 @@ export const WebhooksTab = withProjectPermission(
try { try {
await testWebhook({ await testWebhook({
webhookId, webhookId,
workspaceId projectId: workspaceId
}); });
createNotification({ createNotification({
type: "success", type: "success",