From 2f060407abd5fa8cceb1232273803a21a10333be Mon Sep 17 00:00:00 2001 From: = Date: Thu, 27 Mar 2025 00:19:26 +0530 Subject: [PATCH 1/3] feat: completed folder last secret commit feature --- ...50326171707_folder-last-secret-modified.ts | 21 +++++++++++++++++++ backend/src/db/schemas/secret-folders.ts | 3 ++- backend/src/lib/api-docs/constants.ts | 3 ++- .../server/routes/v1/secret-folder-router.ts | 1 + .../secret-folder/secret-folder-service.ts | 19 +++++++++++++++-- .../secret-folder/secret-folder-types.ts | 1 + backend/src/services/secret/secret-queue.ts | 4 ++++ 7 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 backend/src/db/migrations/20250326171707_folder-last-secret-modified.ts diff --git a/backend/src/db/migrations/20250326171707_folder-last-secret-modified.ts b/backend/src/db/migrations/20250326171707_folder-last-secret-modified.ts new file mode 100644 index 000000000..227fe6ef7 --- /dev/null +++ b/backend/src/db/migrations/20250326171707_folder-last-secret-modified.ts @@ -0,0 +1,21 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; + +export async function up(knex: Knex): Promise { + const hasCol = await knex.schema.hasColumn(TableName.SecretFolder, "lastSecretModified"); + if (!hasCol) { + await knex.schema.alterTable(TableName.SecretFolder, (t) => { + t.datetime("lastSecretModified"); + }); + } +} + +export async function down(knex: Knex): Promise { + const hasCol = await knex.schema.hasColumn(TableName.SecretFolder, "lastSecretModified"); + if (hasCol) { + await knex.schema.alterTable(TableName.SecretFolder, (t) => { + t.dropColumn("lastSecretModified"); + }); + } +} diff --git a/backend/src/db/schemas/secret-folders.ts b/backend/src/db/schemas/secret-folders.ts index 33b13b673..09e2fe8c1 100644 --- a/backend/src/db/schemas/secret-folders.ts +++ b/backend/src/db/schemas/secret-folders.ts @@ -16,7 +16,8 @@ export const SecretFoldersSchema = z.object({ envId: z.string().uuid(), parentId: z.string().uuid().nullable().optional(), isReserved: z.boolean().default(false).nullable().optional(), - description: z.string().nullable().optional() + description: z.string().nullable().optional(), + lastSecretModified: z.date().nullable().optional() }); export type TSecretFolders = z.infer; diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index ec855168e..5acc04d05 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -632,7 +632,8 @@ export const FOLDERS = { environment: "The slug of the environment to list folders from.", path: "The path to list folders from.", directory: "The directory to list folders from. (Deprecated in favor of path)", - recursive: "Whether or not to fetch all folders from the specified base path, and all of its subdirectories." + recursive: "Whether or not to fetch all folders from the specified base path, and all of its subdirectories.", + lastSecretModified: "The timestamp filters folders with secrets modified after the specified date." }, GET_BY_ID: { folderId: "The ID of the folder to get details." diff --git a/backend/src/server/routes/v1/secret-folder-router.ts b/backend/src/server/routes/v1/secret-folder-router.ts index 5f8b553f5..b55564d80 100644 --- a/backend/src/server/routes/v1/secret-folder-router.ts +++ b/backend/src/server/routes/v1/secret-folder-router.ts @@ -335,6 +335,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) => querystring: z.object({ workspaceId: z.string().trim().describe(FOLDERS.LIST.workspaceId), environment: z.string().trim().describe(FOLDERS.LIST.environment), + lastSecretModified: z.string().datetime().trim().optional().describe(FOLDERS.LIST.lastSecretModified), path: z .string() .trim() diff --git a/backend/src/services/secret-folder/secret-folder-service.ts b/backend/src/services/secret-folder/secret-folder-service.ts index d4f58a1d4..97cd19755 100644 --- a/backend/src/services/secret-folder/secret-folder-service.ts +++ b/backend/src/services/secret-folder/secret-folder-service.ts @@ -402,7 +402,8 @@ export const secretFolderServiceFactory = ({ orderDirection, limit, offset, - recursive + recursive, + lastSecretModified }: TGetFolderDTO) => { // folder list is allowed to be read by anyone // permission to check does user has access @@ -425,7 +426,16 @@ export const secretFolderServiceFactory = ({ const recursiveFolders = await folderDAL.findByEnvsDeep({ parentIds: [parentFolder.id] }); // remove the parent folder return recursiveFolders - .filter((folder) => folder.id !== parentFolder.id) + .filter((folder) => { + if (lastSecretModified) { + if (!folder.lastSecretModified) return false; + + if (folder.lastSecretModified < new Date(lastSecretModified)) { + return false; + } + } + return folder.id !== parentFolder.id; + }) .map((folder) => ({ ...folder, relativePath: folder.path @@ -445,6 +455,11 @@ export const secretFolderServiceFactory = ({ offset } ); + if (lastSecretModified) { + return folders.filter((el) => + el.lastSecretModified ? el.lastSecretModified >= new Date(lastSecretModified) : false + ); + } return folders; }; diff --git a/backend/src/services/secret-folder/secret-folder-types.ts b/backend/src/services/secret-folder/secret-folder-types.ts index 2a5681b5b..4008676db 100644 --- a/backend/src/services/secret-folder/secret-folder-types.ts +++ b/backend/src/services/secret-folder/secret-folder-types.ts @@ -46,6 +46,7 @@ export type TGetFolderDTO = { limit?: number; offset?: number; recursive?: boolean; + lastSecretModified?: string; } & TProjectPermission; export type TGetFolderByIdDTO = { diff --git a/backend/src/services/secret/secret-queue.ts b/backend/src/services/secret/secret-queue.ts index 76a486b46..60eec9cb1 100644 --- a/backend/src/services/secret/secret-queue.ts +++ b/backend/src/services/secret/secret-queue.ts @@ -646,6 +646,10 @@ export const secretQueueFactory = ({ } ); + const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath); + if (!folder) return; + await folderDAL.updateById(folder.id, { lastSecretModified: new Date() }); + await secretSyncQueue.queueSecretSyncsSyncSecretsByPath({ projectId, environmentSlug: environment, secretPath }); await syncIntegrations({ secretPath, projectId, environment, deDupeQueue, isManual: false }); From 59ab4de24aab80cd9ab5a9a523bea527add686ba Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Tue, 1 Apr 2025 13:44:23 +0000 Subject: [PATCH 2/3] feat: updated api description --- backend/src/lib/api-docs/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 5acc04d05..88eceb181 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -633,7 +633,7 @@ export const FOLDERS = { path: "The path to list folders from.", directory: "The directory to list folders from. (Deprecated in favor of path)", recursive: "Whether or not to fetch all folders from the specified base path, and all of its subdirectories.", - lastSecretModified: "The timestamp filters folders with secrets modified after the specified date." + lastSecretModified: "The ISO 8601 timestamp filters folders with secrets modified after the specified date." }, GET_BY_ID: { folderId: "The ID of the folder to get details." From ea038f26dfbc095ffc690bed3141c8375a157b65 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Tue, 1 Apr 2025 14:07:57 +0000 Subject: [PATCH 3/3] feat: again updated the desc --- backend/src/lib/api-docs/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 88eceb181..ce72b8bfd 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -633,7 +633,7 @@ export const FOLDERS = { path: "The path to list folders from.", directory: "The directory to list folders from. (Deprecated in favor of path)", recursive: "Whether or not to fetch all folders from the specified base path, and all of its subdirectories.", - lastSecretModified: "The ISO 8601 timestamp filters folders with secrets modified after the specified date." + lastSecretModified: "The timestamp used to filter folders with secrets modified after the specified date. The format for this timestamp is ISO 8601 (e.g. 2025-04-01T09:41:45-04:00)" }, GET_BY_ID: { folderId: "The ID of the folder to get details."