diff --git a/backend/src/ee/services/license/license-fns.ts b/backend/src/ee/services/license/license-fns.ts index b7ae6f7ee..cff6ee181 100644 --- a/backend/src/ee/services/license/license-fns.ts +++ b/backend/src/ee/services/license/license-fns.ts @@ -17,44 +17,44 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({ environmentsUsed: 0, identityLimit: null, identitiesUsed: 0, - dynamicSecret: false, + dynamicSecret: true, secretVersioning: true, - pitRecovery: false, - ipAllowlisting: false, - rbac: false, - githubOrgSync: false, - customRateLimits: false, - customAlerts: false, - secretAccessInsights: false, - auditLogs: false, + pitRecovery: true, + ipAllowlisting: true, + rbac: true, + githubOrgSync: true, + customRateLimits: true, + customAlerts: true, + secretAccessInsights: true, + auditLogs: true, auditLogsRetentionDays: 0, - auditLogStreams: false, + auditLogStreams: true, auditLogStreamLimit: 3, - samlSSO: false, - hsm: false, - oidcSSO: false, - scim: false, - ldap: false, - groups: false, + samlSSO: true, + hsm: true, + oidcSSO: true, + scim: true, + ldap: true, + groups: true, status: null, trial_end: null, has_used_trial: true, - secretApproval: false, - secretRotation: false, - caCrl: false, - instanceUserManagement: false, - externalKms: false, + secretApproval: true, + secretRotation: true, + caCrl: true, + instanceUserManagement: true, + externalKms: true, rateLimits: { readLimit: 60, writeLimit: 200, secretsLimit: 40 }, - pkiEst: false, - enforceMfa: false, - projectTemplates: false, - kmip: false, - gateway: false, - sshHostGroups: false + pkiEst: true, + enforceMfa: true, + projectTemplates: true, + kmip: true, + gateway: true, + sshHostGroups: true }); export const setupLicenseRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => { diff --git a/backend/src/services/folder-checkpoint/folder-checkpoint-dal.ts b/backend/src/services/folder-checkpoint/folder-checkpoint-dal.ts index 5ebb39f5b..94cf1b0d4 100644 --- a/backend/src/services/folder-checkpoint/folder-checkpoint-dal.ts +++ b/backend/src/services/folder-checkpoint/folder-checkpoint-dal.ts @@ -108,8 +108,8 @@ export const folderCheckpointDALFactory = (db: TDbClient) => { `${TableName.FolderCheckpoint}.folderCommitId`, `${TableName.FolderCommit}.id` ) - .where(`${TableName.FolderCommit}.folderId`, commit.folderId) - .where(`${TableName.FolderCommit}.commitId`, "<=", commit.commitId.toString()) + .where(`${TableName.FolderCommit}.folderId`, "=", commit.folderId) + .where(`${TableName.FolderCommit}.commitId`, "<=", commit.commitId) .select(selectAllTableCols(TableName.FolderCheckpoint)) .select( db.ref("actorMetadata").withSchema(TableName.FolderCommit), diff --git a/backend/src/services/folder-commit/folder-commit-dal.ts b/backend/src/services/folder-commit/folder-commit-dal.ts index 5992a4672..616aba72b 100644 --- a/backend/src/services/folder-commit/folder-commit-dal.ts +++ b/backend/src/services/folder-commit/folder-commit-dal.ts @@ -294,12 +294,11 @@ export const folderCommitDALFactory = (db: TDbClient) => { folderId?: string; envId?: string; startCommitId?: string; - endCommitId: string; + endCommitId?: string; tx?: Knex; }): Promise => { try { const docs = await (tx || db.replicaNode())(TableName.FolderCommit) - .where("commitId", "<=", endCommitId) .where((qb) => { if (envId) { void qb.where(`${TableName.FolderCommit}.envId`, "=", envId); @@ -307,6 +306,9 @@ export const folderCommitDALFactory = (db: TDbClient) => { if (startCommitId) { void qb.where("commitId", ">=", startCommitId); } + if (endCommitId) { + void qb.where("commitId", "<=", endCommitId); + } }) .select(selectAllTableCols(TableName.FolderCommit)) .orderBy("commitId", "desc"); @@ -344,6 +346,20 @@ export const folderCommitDALFactory = (db: TDbClient) => { } }; + const findPreviousCommitTo = async (folderId: string, commitId: string, tx?: Knex): Promise => { + try { + const doc = await (tx || db.replicaNode())(TableName.FolderCommit) + .where({ folderId }) + .where("commitId", "<=", commitId) + .select(selectAllTableCols(TableName.FolderCommit)) + .orderBy("commitId", "desc") + .first(); + return doc; + } catch (error) { + throw new DatabaseError({ error, name: "FindPreviousCommitTo" }); + } + }; + return { ...restOfOrm, findByFolderId, @@ -356,6 +372,7 @@ export const folderCommitDALFactory = (db: TDbClient) => { findLatestEnvCommit, getEnvNumberOfCommitsSince, findLatestCommitByFolderIds, - findAllFolderCommitsAfter + findAllFolderCommitsAfter, + findPreviousCommitTo }; }; diff --git a/backend/src/services/folder-commit/folder-commit-queue.ts b/backend/src/services/folder-commit/folder-commit-queue.ts index 06aeaa0c9..d68298337 100644 --- a/backend/src/services/folder-commit/folder-commit-queue.ts +++ b/backend/src/services/folder-commit/folder-commit-queue.ts @@ -1,3 +1,5 @@ +import { Knex } from "knex"; + import { TSecretFolders } from "@app/db/schemas"; import { getConfig } from "@app/lib/config/env"; import { logger } from "@app/lib/logger"; @@ -20,7 +22,7 @@ type TFolderCommitQueueServiceFactoryDep = { >; folderCommitDAL: Pick< TFolderCommitDALFactory, - "findLatestEnvCommit" | "getEnvNumberOfCommitsSince" | "findMultipleLatestCommits" + "findLatestEnvCommit" | "getEnvNumberOfCommitsSince" | "findMultipleLatestCommits" | "findById" >; folderDAL: Pick; }; @@ -42,7 +44,7 @@ export const folderCommitQueueServiceFactory = ({ QueueJobs.CreateFolderTreeCheckpoint, { envId }, { - jobId: envId, + jobId: `${envId}`, backoff: { type: "exponential", delay: 3000 @@ -119,52 +121,66 @@ export const folderCommitQueueServiceFactory = ({ return result; }; + const createFolderTreeCheckpoint = async (envId: string, folderCommitId?: string, tx?: Knex) => { + logger.info("Folder tree checkpoint creation started:", envId); + + const latestTreeCheckpoint = await folderTreeCheckpointDAL.findLatestByEnvId(envId, tx); + + let latestCommit; + if (folderCommitId) { + latestCommit = await folderCommitDAL.findById(folderCommitId, tx); + } else { + latestCommit = await folderCommitDAL.findLatestEnvCommit(envId, tx); + } + if (!latestCommit) { + logger.info(`Latest commit ID not found for envId ${envId}`); + return; + } + const latestCommitId = latestCommit.id; + + if (latestTreeCheckpoint) { + const commitsSinceLastCheckpoint = await folderCommitDAL.getEnvNumberOfCommitsSince( + envId, + latestTreeCheckpoint.folderCommitId, + tx + ); + if (commitsSinceLastCheckpoint < Number(appCfg.PIT_TREE_CHECKPOINT_WINDOW)) { + logger.info( + `Commits since last checkpoint ${commitsSinceLastCheckpoint} is less than ${appCfg.PIT_TREE_CHECKPOINT_WINDOW}` + ); + return; + } + } + + const folders = await folderDAL.findByEnvId(envId, tx); + const sortedFolders = sortFoldersByHierarchy(folders); + const filteredFoldersIds = sortedFolders.filter((folder) => !folder.isReserved).map((folder) => folder.id); + + const folderCommits = await folderCommitDAL.findMultipleLatestCommits(filteredFoldersIds, tx); + const folderTreeCheckpoint = await folderTreeCheckpointDAL.create( + { + folderCommitId: latestCommitId + }, + tx + ); + + await folderTreeCheckpointResourcesDAL.insertMany( + folderCommits.map((folderCommit) => ({ + folderTreeCheckpointId: folderTreeCheckpoint.id, + folderId: folderCommit.folderId, + folderCommitId: folderCommit.id + })), + tx + ); + + logger.info("Folder tree checkpoint created successfully:", folderTreeCheckpoint.id); + }; + queueService.start(QueueName.FolderTreeCheckpoint, async (job) => { try { if (job.name === QueueJobs.CreateFolderTreeCheckpoint) { const { envId } = job.data as { envId: string }; - logger.info("Folder tree checkpoint creation started:", envId, job.id); - - const latestTreeCheckpoint = await folderTreeCheckpointDAL.findLatestByEnvId(envId); - - const latestCommit = await folderCommitDAL.findLatestEnvCommit(envId); - if (!latestCommit) { - logger.info(`Latest commit ID not found for envId ${envId}`); - return; - } - const latestCommitId = latestCommit.id; - - if (latestTreeCheckpoint) { - const commitsSinceLastCheckpoint = await folderCommitDAL.getEnvNumberOfCommitsSince( - envId, - latestTreeCheckpoint.folderCommitId - ); - if (commitsSinceLastCheckpoint < Number(appCfg.PIT_TREE_CHECKPOINT_WINDOW)) { - logger.info( - `Commits since last checkpoint ${commitsSinceLastCheckpoint} is less than ${appCfg.PIT_TREE_CHECKPOINT_WINDOW}` - ); - return; - } - } - - const folders = await folderDAL.findByEnvId(envId); - const sortedFolders = sortFoldersByHierarchy(folders); - const filteredFoldersIds = sortedFolders.filter((folder) => !folder.isReserved).map((folder) => folder.id); - - const folderCommits = await folderCommitDAL.findMultipleLatestCommits(filteredFoldersIds); - const folderTreeCheckpoint = await folderTreeCheckpointDAL.create({ - folderCommitId: latestCommitId - }); - - await folderTreeCheckpointResourcesDAL.insertMany( - folderCommits.map((folderCommit) => ({ - folderTreeCheckpointId: folderTreeCheckpoint.id, - folderId: folderCommit.folderId, - folderCommitId: folderCommit.id - })) - ); - - logger.info("Folder tree checkpoint created successfully:", folderTreeCheckpoint.id); + await createFolderTreeCheckpoint(envId); } } catch (error) { logger.error(error, "Error creating folder tree checkpoint:"); @@ -175,6 +191,7 @@ export const folderCommitQueueServiceFactory = ({ return { scheduleTreeCheckpoint, schedulePeriodicTreeCheckpoint, - cancelScheduledTreeCheckpoint + cancelScheduledTreeCheckpoint, + createFolderTreeCheckpoint }; }; diff --git a/backend/src/services/folder-commit/folder-commit-service.test.ts b/backend/src/services/folder-commit/folder-commit-service.test.ts index c25ead567..bc327da81 100644 --- a/backend/src/services/folder-commit/folder-commit-service.test.ts +++ b/backend/src/services/folder-commit/folder-commit-service.test.ts @@ -131,7 +131,8 @@ describe("folderCommitServiceFactory", () => { }; const mockFolderCommitQueueService = { - scheduleTreeCheckpoint: vi.fn().mockResolvedValue({}) + scheduleTreeCheckpoint: vi.fn().mockResolvedValue({}), + createFolderTreeCheckpoint: vi.fn().mockResolvedValue({}) }; const mockPermissionService = { diff --git a/backend/src/services/folder-commit/folder-commit-service.ts b/backend/src/services/folder-commit/folder-commit-service.ts index 9d598f2ba..87e03f31b 100644 --- a/backend/src/services/folder-commit/folder-commit-service.ts +++ b/backend/src/services/folder-commit/folder-commit-service.ts @@ -116,7 +116,10 @@ type TFolderCommitServiceFactoryDep = { secretVersionV2BridgeDAL: TSecretVersionV2DALFactory; secretV2BridgeDAL: secretV2BridgeDal.TSecretV2BridgeDALFactory; projectDAL: Pick; - folderCommitQueueService?: Pick; + folderCommitQueueService?: Pick< + TFolderCommitQueueServiceFactory, + "scheduleTreeCheckpoint" | "createFolderTreeCheckpoint" + >; permissionService?: TPermissionServiceFactory; }; @@ -226,18 +229,16 @@ export const folderCommitServiceFactory = ({ const checkpointResources = await getFolderResources(folderId, tx); - if (checkpointResources.length > 0) { - const newCheckpoint = await folderCheckpointDAL.create( - { - folderCommitId: latestCommitId - }, - tx - ); - await folderCheckpointResourcesDAL.insertMany( - checkpointResources.map((resource) => ({ folderCheckpointId: newCheckpoint.id, ...resource })), - tx - ); - } + const newCheckpoint = await folderCheckpointDAL.create( + { + folderCommitId: latestCommitId + }, + tx + ); + await folderCheckpointResourcesDAL.insertMany( + checkpointResources.map((resource) => ({ folderCheckpointId: newCheckpoint.id, ...resource })), + tx + ); return latestCommitId; }; @@ -357,10 +358,12 @@ export const folderCommitServiceFactory = ({ const compareFolderStates = async ({ currentCommitId, targetCommitId, + defaultOperation = "create", tx }: { currentCommitId?: string; targetCommitId: string; + defaultOperation?: "create" | "update" | "delete"; tx?: Knex; }) => { const targetCommit = await folderCommitDAL.findById(targetCommitId, tx); @@ -376,7 +379,7 @@ export const folderCommitServiceFactory = ({ type: resource.type, id: resource.id, versionId: resource.versionId, - changeType: "create", + changeType: defaultOperation, commitId: targetCommit.commitId, secretKey: resource.secretKey, secretVersion: resource.secretVersion, @@ -531,6 +534,40 @@ export const folderCommitServiceFactory = ({ throw new NotFoundError({ message: `Folder with ID ${data.folderId} not found` }); } + const newFolders = data.changes.filter( + (change) => change.type === "add" && !change.isUpdate && change.folderVersionId + ); + if (newFolders.length > 0) { + const folderVersions = await folderVersionDAL.find( + { + $in: { + id: newFolders.map((change) => change.folderVersionId).filter(Boolean) as string[] + } + }, + { tx } + ); + await Promise.all( + Object.values(folderVersions).map(async (folderVersion) => { + const subFolderCommit = await folderCommitDAL.create( + { + actorMetadata: metadata, + actorType: data.actor.type, + message: data.message, + folderId: folderVersion.folderId, + envId: folderVersion.envId + }, + tx + ); + await createFolderCheckpoint({ + folderId: folderVersion.folderId, + folderCommitId: subFolderCommit.id, + force: true, + tx + }); + }) + ); + } + const newCommit = await folderCommitDAL.create( { actorMetadata: metadata, @@ -553,8 +590,14 @@ export const folderCommitServiceFactory = ({ tx ); - await createFolderCheckpoint({ folderId: data.folderId, tx }); + await createFolderCheckpoint({ folderId: data.folderId, folderCommitId: newCommit.id, tx }); if (folderCommitQueueService) { + if (!folder.parentId) { + const previousTreeCommit = await folderTreeCheckpointDAL.findLatestByEnvId(folder.envId); + if (!previousTreeCommit) { + await folderCommitQueueService.createFolderTreeCheckpoint(folder.envId, newCommit.id, tx); + } + } await folderCommitQueueService.scheduleTreeCheckpoint(folder.envId); } return newCommit; @@ -1219,30 +1262,31 @@ export const folderCommitServiceFactory = ({ const folderCheckpointCommits = await folderTreeCheckpointResourcesDAL.findByTreeCheckpointId(checkpoint.id, tx); const folderCommits = await folderCommitDAL.findAllCommitsBetween({ envId, - endCommitId: targetCommit.commitId.toString(), startCommitId: checkpoint.commitId.toString(), tx }); // Group commits by folderId and keep only the latest - const folderGroups = new Map(); + const folderGroups = new Map(); if (folderCheckpointCommits && folderCheckpointCommits.length > 0) { for (const commit of folderCheckpointCommits) { - folderGroups.set(commit.folderId, { - createdAt: commit.createdAt, - id: commit.folderCommitId - }); + if (commit.commitId > targetCommit.commitId) { + folderGroups.set(commit.folderId, { + commitId: commit.commitId, + id: commit.folderCommitId + }); + } } } if (folderCommits && folderCommits.length > 0) { for (const commit of folderCommits) { - const { folderId, createdAt, id } = commit; + const { folderId, commitId, id } = commit; const existingCommit = folderGroups.get(folderId); - if (!existingCommit || createdAt.getTime() > existingCommit.createdAt.getTime()) { - folderGroups.set(folderId, { createdAt, id }); + if ((!existingCommit || commitId > existingCommit.commitId) && commitId > targetCommit.commitId) { + folderGroups.set(folderId, { commitId, id }); } } } @@ -1252,16 +1296,27 @@ export const folderCommitServiceFactory = ({ // Process each folder to determine differences await Promise.all( Array.from(folderGroups.entries()).map(async ([folderId, commit]) => { - const latestFolderCommit = await folderCommitDAL.findLatestCommit(folderId, tx); - if (latestFolderCommit && latestFolderCommit.id !== commit.id) { - const diff = await compareFolderStates({ - currentCommitId: latestFolderCommit.id, - targetCommitId: commit.id, + const previousCommit = await folderCommitDAL.findPreviousCommitTo( + folderId, + targetCommit.commitId.toString(), + tx + ); + let diff = []; + if (previousCommit && previousCommit.id !== commit.id) { + diff = await compareFolderStates({ + currentCommitId: commit.id, + targetCommitId: previousCommit.id, tx }); - if (diff?.length > 0) { - folderDiffs.set(folderId, diff); - } + } else { + diff = await compareFolderStates({ + targetCommitId: commit.id, + defaultOperation: "delete", + tx + }); + } + if (diff?.length > 0) { + folderDiffs.set(folderId, diff); } }) ); @@ -1295,92 +1350,115 @@ export const folderCommitServiceFactory = ({ envId: string, actorId: string, actorType: ActorType, - projectId: string, - tx?: Knex + projectId: string ) => { - const targetCommit = await folderCommitDAL.findById(targetCommitId, tx); - if (!targetCommit) { - throw new NotFoundError({ message: `No commit found for commit ID ${targetCommitId}` }); - } - - const checkpoint = await folderTreeCheckpointDAL.findNearestCheckpoint(targetCommitId, envId, tx); - if (!checkpoint) { - throw new NotFoundError({ message: `No checkpoint found for commit ID ${targetCommitId}` }); - } - - const folderCheckpointCommits = await folderTreeCheckpointResourcesDAL.findByTreeCheckpointId(checkpoint.id, tx); - const folderCommits = await folderCommitDAL.findAllCommitsBetween({ - envId, - endCommitId: targetCommit.commitId.toString(), - startCommitId: checkpoint.commitId.toString(), - tx - }); - - // Group commits by folderId and keep only the latest - const folderGroups = new Map(); - - if (folderCheckpointCommits && folderCheckpointCommits.length > 0) { - for (const commit of folderCheckpointCommits) { - folderGroups.set(commit.folderId, { - createdAt: commit.createdAt, - id: commit.folderCommitId - }); + await folderCommitDAL.transaction(async (tx) => { + const targetCommit = await folderCommitDAL.findById(targetCommitId, tx); + if (!targetCommit) { + throw new NotFoundError({ message: `No commit found for commit ID ${targetCommitId}` }); } - } - if (folderCommits && folderCommits.length > 0) { - for (const commit of folderCommits) { - const { folderId, createdAt, id } = commit; - const existingCommit = folderGroups.get(folderId); - - if (!existingCommit || createdAt.getTime() > existingCommit.createdAt.getTime()) { - folderGroups.set(folderId, { createdAt, id }); - } + const checkpoint = await folderTreeCheckpointDAL.findNearestCheckpoint(targetCommitId, envId, tx); + if (!checkpoint) { + throw new NotFoundError({ message: `No checkpoint found for commit ID ${targetCommitId}` }); } - } - const folderDiffs = new Map(); + const folderCheckpointCommits = await folderTreeCheckpointResourcesDAL.findByTreeCheckpointId(checkpoint.id, tx); + const folderCommits = await folderCommitDAL.findAllCommitsBetween({ + envId, + startCommitId: checkpoint.commitId.toString(), + tx + }); - // Process each folder to determine differences - await Promise.all( - Array.from(folderGroups.entries()).map(async ([folderId, commit]) => { - const latestFolderCommit = await folderCommitDAL.findLatestCommit(folderId, tx); - if (latestFolderCommit && latestFolderCommit.id !== commit.id) { - const diff = await compareFolderStates({ - currentCommitId: latestFolderCommit.id, - targetCommitId: commit.id, - tx - }); - if (diff?.length > 0) { - folderDiffs.set(folderId, diff); + // Group commits by folderId and keep only the latest + const folderGroups = new Map(); + + if (folderCheckpointCommits && folderCheckpointCommits.length > 0) { + for (const commit of folderCheckpointCommits) { + if (commit.commitId > targetCommit.commitId) { + folderGroups.set(commit.folderId, { + commitId: commit.commitId, + id: commit.folderCommitId + }); } } - }) - ); - - // Apply changes in hierarchical order - const folderIds = Array.from(folderDiffs.keys()); - const folders = await folderDAL.findFoldersByRootAndIds({ rootId: targetCommit.folderId, folderIds }, tx); - const sortedFolders = sortFoldersByHierarchy(folders); - - for (const folder of sortedFolders) { - const diff = folderDiffs.get(folder.id); - if (diff) { - await applyFolderStateDifferences({ - differences: diff, - actorInfo: { - actorType, - actorId, - message: "Deep rollback" - }, - folderId: folder.id, - projectId, - reconstructNewFolders: true, - reconstructUpToCommit: targetCommit.commitId.toString(), - tx - }); } - } + + if (folderCommits && folderCommits.length > 0) { + for (const commit of folderCommits) { + const { folderId, commitId, id } = commit; + const existingCommit = folderGroups.get(folderId); + + if ((!existingCommit || commitId > existingCommit.commitId) && commitId > targetCommit.commitId) { + folderGroups.set(folderId, { commitId, id }); + } + } + } + + const folderDiffs = new Map(); + + // Process each folder to determine differences + await Promise.all( + Array.from(folderGroups.entries()).map(async ([folderId, { id }]) => { + const previousCommit = await folderCommitDAL.findPreviousCommitTo( + folderId, + targetCommit.commitId.toString(), + tx + ); + if (previousCommit && previousCommit.id !== id) { + const diff = await compareFolderStates({ + currentCommitId: id, + targetCommitId: previousCommit.id, + tx + }); + if (diff?.length > 0) { + folderDiffs.set(folderId, diff); + } + } + }) + ); + + const foldersToDelete = new Set(); + + // Process all DELETE operations to build a complete set of folders to be deleted + for (const [folderId, changes] of folderDiffs.entries()) { + for (const change of changes) { + if (change.changeType === ChangeType.DELETE && change.type === "folder") { + foldersToDelete.add(change.id); + } + } + } + + // Now, remove any folder that is being deleted from the folderDiffs map + // before applying any changes + for (const folderId of foldersToDelete) { + folderDiffs.delete(folderId); + } + + // Apply changes in hierarchical order + const folderIds = Array.from(folderDiffs.keys()); + const folders = await folderDAL.findFoldersByRootAndIds({ rootId: targetCommit.folderId, folderIds }, tx); + const sortedFolders = sortFoldersByHierarchy(folders); + + for (const folder of sortedFolders) { + const diff = folderDiffs.get(folder.id); + if (diff) { + await applyFolderStateDifferences({ + differences: diff, + actorInfo: { + actorType, + actorId, + message: "Deep rollback" + }, + folderId: folder.id, + projectId, + reconstructNewFolders: true, + reconstructUpToCommit: targetCommit.commitId.toString(), + tx + }); + } + } + }); }; const getLatestCommit = async ({ diff --git a/backend/src/services/folder-tree-checkpoint/folder-tree-checkpoint-dal.ts b/backend/src/services/folder-tree-checkpoint/folder-tree-checkpoint-dal.ts index 7589c4b49..a421b86e9 100644 --- a/backend/src/services/folder-tree-checkpoint/folder-tree-checkpoint-dal.ts +++ b/backend/src/services/folder-tree-checkpoint/folder-tree-checkpoint-dal.ts @@ -34,7 +34,7 @@ export const folderTreeCheckpointDALFactory = (db: TDbClient) => { try { const targetCommit = await (tx || db.replicaNode())(TableName.FolderCommit) .where({ id: folderCommitId }) - .select("id", "commitId", "folderId") + .select("id", "commitId", "folderId", "envId") .first(); if (!targetCommit) { @@ -42,13 +42,12 @@ export const folderTreeCheckpointDALFactory = (db: TDbClient) => { } const nearestCheckpoint = await (tx || db.replicaNode())(TableName.FolderTreeCheckpoint) - .join( + .leftJoin( TableName.FolderCommit, `${TableName.FolderTreeCheckpoint}.folderCommitId`, `${TableName.FolderCommit}.id` ) - .where(`${TableName.FolderCommit}.commitId`, "<=", targetCommit.commitId.toString()) - .where(`${TableName.FolderCommit}.envId`, envId) + .where(`${TableName.FolderCommit}.envId`, "=", targetCommit.envId) .select(selectAllTableCols(TableName.FolderTreeCheckpoint)) .select(db.ref("commitId").withSchema(TableName.FolderCommit)) .orderBy(`${TableName.FolderCommit}.commitId`, "desc") diff --git a/frontend/src/pages/secret-manager/CommitDetailsPage/components/RollbackPreviewTab/RollbackPreviewTab.tsx b/frontend/src/pages/secret-manager/CommitDetailsPage/components/RollbackPreviewTab/RollbackPreviewTab.tsx index ecb2056e6..1a8893989 100644 --- a/frontend/src/pages/secret-manager/CommitDetailsPage/components/RollbackPreviewTab/RollbackPreviewTab.tsx +++ b/frontend/src/pages/secret-manager/CommitDetailsPage/components/RollbackPreviewTab/RollbackPreviewTab.tsx @@ -206,32 +206,45 @@ export const RollbackPreviewTab = (): JSX.Element => { {currentFolderChanges.folderPath || currentFolderChanges.folderName} + {currentFolderChanges.changes.length > 0 && ( + + {currentFolderChanges.changes.length} + + )} - + {deepRollback && nestedFolderChanges.length > 0 && ( <>
- Affected Child Folders + + Affected Child Folders +
- {nestedFolderChanges.map((folder) => ( -
setSelectedFolderId(folder.folderId)} - > -
-
- - - {folder.folderPath || folder.folderName} - + {(nestedFolderChanges.map((folder) => ( +
setSelectedFolderId(folder.folderId)} + > +
+
+ + + {folder.folderPath || folder.folderName} + +
+ {folder.changes.length > 0 && ( + + {folder.changes.length} + + )}
-
- ))} + )) + )} )}
@@ -292,16 +305,10 @@ export const RollbackPreviewTab = (): JSX.Element => { description="This will restore all changes to how they appeared at the point in time of this commit. Any modifications made after this commit will be undone." /> - {folderChanges.length > 0 ? ( -
+
{renderSidebar()} {renderMainContent()}
- ) : ( -
-

No changes will be applied with this restore

-
- )}
@@ -346,6 +353,7 @@ export const RollbackPreviewTab = (): JSX.Element => { }} colorSchema="primary" className="px-6 py-2" + isDisabled={message.length === 0 || !rollbackChangesNested || !rollbackChangesNested?.some((folder) => folder.changes.length > 0)} > Restore