Revert "Revert "Revert "feat(PIT): Point In Time Revamp"""

This commit is contained in:
Maidul Islam
2025-06-08 21:20:37 -04:00
committed by GitHub
parent 210ddf506a
commit 9c6b300ad4
114 changed files with 130 additions and 10782 deletions
@@ -14,15 +14,13 @@ type Props = {
selectedPathSegmentIndex: number;
environmentSlug: string;
projectId: string;
disableCopy?: boolean;
};
export const SecretDashboardPathBreadcrumb = ({
secretPathSegments,
selectedPathSegmentIndex,
environmentSlug,
projectId,
disableCopy
projectId
}: Props) => {
const [, isCopying, setIsCopying] = useTimedReset({
initialState: false
@@ -34,7 +32,7 @@ export const SecretDashboardPathBreadcrumb = ({
return (
<div className="flex items-center space-x-3">
{isLastItem && !disableCopy ? (
{isLastItem ? (
<div className="group flex items-center space-x-2">
<span
className={twMerge(
-12
View File
@@ -75,18 +75,6 @@ export const ROUTE_PATHS = Object.freeze({
"/secret-manager/$projectId/secrets/$envSlug",
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/secrets/$envSlug"
),
RollbackPreviewPage: setRoute(
"/secret-manager/$projectId/commits/$environment/$folderId/$commitId/restore",
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/restore"
),
CommitDetailsPage: setRoute(
"/secret-manager/$projectId/commits/$environment/$folderId/$commitId",
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId"
),
CommitsPage: setRoute(
"/secret-manager/$projectId/commits/$environment/$folderId",
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId"
),
OverviewPage: setRoute(
"/secret-manager/$projectId/overview",
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/overview"
@@ -152,11 +152,6 @@ export enum PermissionConditionOperators {
$ELEMENTMATCH = "$elemMatch"
}
export enum ProjectPermissionCommitsActions {
Read = "read",
PerformRollback = "perform-rollback"
}
export type IdentityManagementSubjectFields = {
identityId: string;
};
@@ -234,7 +229,6 @@ export enum ProjectPermissionSub {
Cmek = "cmek",
SecretSyncs = "secret-syncs",
Kmip = "kmip",
Commits = "commits",
SecretScanningDataSources = "secret-scanning-data-sources",
SecretScanningFindings = "secret-scanning-findings",
SecretScanningConfigs = "secret-scanning-configs"
@@ -373,7 +367,6 @@ export type ProjectPermissionSet =
| [ProjectPermissionCmekActions, ProjectPermissionSub.Cmek]
| [ProjectPermissionActions.Edit, ProjectPermissionSub.Kms]
| [ProjectPermissionKmipActions, ProjectPermissionSub.Kmip]
| [ProjectPermissionCommitsActions, ProjectPermissionSub.Commits]
| [
ProjectPermissionSecretScanningDataSourceActions,
ProjectPermissionSub.SecretScanningDataSources
@@ -1,2 +0,0 @@
export { useGetFolderCommitHistory, useGetFolderCommitsCount } from "./queries";
export * from "./types";
@@ -1,294 +0,0 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request";
import { CommitHistoryItem, CommitWithChanges, RollbackPreview } from "./types";
export const commitKeys = {
count: ({
workspaceId,
environment,
directory
}: {
workspaceId: string;
environment: string;
directory?: string;
}) => [{ workspaceId, environment, directory }, "folder-commits-count"] as const,
history: ({
workspaceId,
environment,
directory
}: {
workspaceId: string;
environment: string;
directory?: string;
}) => [{ workspaceId, environment, directory }, "folder-commits"] as const,
details: ({ workspaceId, commitId }: { workspaceId: string; commitId: string }) =>
[{ workspaceId, commitId }, "commit-details"] as const,
rollbackPreview: ({
folderId,
commitId,
envSlug,
projectId,
deepRollback
}: {
folderId: string;
commitId: string;
envSlug: string;
projectId: string;
deepRollback: boolean;
}) => [{ folderId, commitId, envSlug, projectId, deepRollback }, "rollback-preview"] as const
};
const fetchFolderCommitsCount = async ({
workspaceId,
environment,
directory
}: {
workspaceId: string;
environment: string;
directory?: string;
}) => {
const res = await apiRequest.get<{ count: number; folderId: string }>(
"/api/v1/pit/commits/count",
{
params: {
environment,
path: directory,
projectId: workspaceId
}
}
);
return res.data;
};
const fetchFolderCommitHistory = async (
workspaceId: string,
environment: string,
directory: string,
offset: number = 0,
limit: number = 20,
search?: string,
sort: "asc" | "desc" = "desc"
): Promise<{
commits: CommitHistoryItem[];
total: number;
hasMore: boolean;
}> => {
const res = await apiRequest.get<{
commits: CommitHistoryItem[];
total: number;
hasMore: boolean;
}>("/api/v1/pit/commits", {
params: {
environment,
path: directory,
projectId: workspaceId,
offset,
limit,
search,
sort
}
});
return res.data;
};
export const fetchCommitDetails = async (workspaceId: string, commitId: string) => {
const { data } = await apiRequest.get<CommitWithChanges>(
`/api/v1/pit/commits/${commitId}/changes`,
{
params: {
projectId: workspaceId
}
}
);
return data;
};
export const fetchRollbackPreview = async (
folderId: string,
commitId: string,
envSlug: string,
workspaceId: string,
deepRollback: boolean,
secretPath: string
): Promise<RollbackPreview[]> => {
const { data } = await apiRequest.get<RollbackPreview[]>(
`/api/v1/pit/commits/${commitId}/compare`,
{
params: {
folderId,
environment: envSlug,
deepRollback,
secretPath,
projectId: workspaceId
}
}
);
return data;
};
const fetchRollback = async (
folderId: string,
commitId: string,
workspaceId: string,
deepRollback: boolean,
message?: string,
envSlug?: string
) => {
const { data } = await apiRequest.post<{ success: boolean }>(
`/api/v1/pit/commits/${commitId}/rollback`,
{
folderId,
deepRollback,
message,
environment: envSlug,
projectId: workspaceId
}
);
return data;
};
const fetchRevert = async (commitId: string, workspaceId: string) => {
const { data } = await apiRequest.post<{ success: boolean; message: string }>(
`/api/v1/pit/commits/${commitId}/revert`,
{
projectId: workspaceId
}
);
return data;
};
export const useCommitRevert = ({
commitId,
projectId,
environment,
directory
}: {
commitId: string;
projectId: string;
environment: string;
directory: string;
}) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: () => fetchRevert(commitId, projectId),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [
commitKeys.details({ workspaceId: projectId, commitId }),
commitKeys.history({ workspaceId: projectId, environment, directory }),
commitKeys.count({ workspaceId: projectId, environment, directory })
]
});
}
});
};
export const useCommitRollback = ({
workspaceId,
commitId,
folderId,
deepRollback,
environment,
directory,
envSlug
}: {
workspaceId: string;
commitId: string;
folderId: string;
deepRollback: boolean;
environment: string;
directory: string;
envSlug: string;
}) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (message: string) =>
fetchRollback(folderId, commitId, workspaceId, deepRollback, message, envSlug),
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: [
commitKeys.details({ workspaceId, commitId }),
commitKeys.history({ workspaceId, environment, directory }),
commitKeys.count({ workspaceId, environment, directory })
]
});
}
});
};
export const useGetFolderCommitsCount = ({
workspaceId,
environment,
directory,
isPaused
}: {
workspaceId: string;
environment: string;
directory: string;
isPaused?: boolean;
}) =>
useQuery({
enabled: Boolean(workspaceId && environment) && !isPaused,
queryKey: commitKeys.count({ workspaceId, environment, directory }),
queryFn: () => fetchFolderCommitsCount({ workspaceId, environment, directory })
});
export const useGetFolderCommitHistory = ({
workspaceId,
environment,
directory,
offset = 0,
limit = 20,
search,
sort = "desc"
}: {
workspaceId: string;
environment: string;
directory: string;
offset?: number;
limit?: number;
search?: string;
sort?: "asc" | "desc";
}) => {
return useQuery({
queryKey: [
commitKeys.history({ workspaceId, environment, directory }),
offset,
limit,
search,
sort
],
queryFn: () =>
fetchFolderCommitHistory(workspaceId, environment, directory, offset, limit, search, sort),
enabled: Boolean(workspaceId && environment)
});
};
export const useGetCommitDetails = (workspaceId: string, commitId: string) => {
return useQuery({
queryKey: commitKeys.details({ workspaceId, commitId }),
queryFn: () => fetchCommitDetails(workspaceId, commitId),
enabled: Boolean(workspaceId) && Boolean(commitId)
});
};
export const useGetRollbackPreview = (
folderId: string,
commitId: string,
envSlug: string,
projectId: string,
deepRollback: boolean,
secretPath: string
) => {
return useQuery({
queryKey: commitKeys.rollbackPreview({ folderId, commitId, envSlug, projectId, deepRollback }),
queryFn: () =>
fetchRollbackPreview(folderId, commitId, envSlug, projectId, deepRollback, secretPath),
enabled: Boolean(folderId) && Boolean(commitId)
});
};
@@ -1,64 +0,0 @@
import { CommitType, SecretVersions } from "../types";
export type CommitHistoryItem = {
id: string;
commitId: string;
actorMetadata: {
id: string;
name?: string;
};
actorType: string;
message: string;
folderId: string;
envId: string;
createdAt: string;
updatedAt: string;
isLatest: boolean;
};
export type TFolderCommitChanges = {
id: string;
folderCommitId: string;
changeType: CommitType;
isUpdate: boolean;
secretVersionId: string | null;
folderVersionId: string | null;
createdAt: string;
updatedAt: string;
versions: SecretVersions[];
secretKey?: string;
folderName?: string;
secretVersion?: string;
folderVersion?: string;
};
export type FolderReconstructedItem = {
type: string;
id: string;
versionId: string;
folderName?: string;
folderVersion?: number;
secretKey?: string;
secretVersion?: number;
};
export type CommitWithChanges = {
changes: CommitHistoryItem & {
changes: TFolderCommitChanges[];
};
};
export type RollbackChange = {
type: "folder" | "secret";
id: string;
versionId: string;
changeType: "create" | "update" | "delete";
commitId: string;
};
export type RollbackPreview = {
folderId: string;
folderName: string;
folderPath: string;
changes: RollbackChange[];
};
@@ -11,8 +11,7 @@ export enum ApprovalStatus {
export enum CommitType {
DELETE = "delete",
UPDATE = "update",
CREATE = "create",
ADD = "add"
CREATE = "create"
}
export type TSecretApprovalSecChangeData = {
@@ -10,7 +10,6 @@ import {
import { apiRequest } from "@app/config/request";
import { dashboardKeys } from "@app/hooks/api/dashboard/queries";
import { commitKeys } from "../folderCommits/queries";
import { secretSnapshotKeys } from "../secretSnapshots/queries";
import {
TCreateFolderDTO,
@@ -167,9 +166,6 @@ export const useCreateFolder = () => {
queryClient.invalidateQueries({
queryKey: secretSnapshotKeys.count({ workspaceId: projectId, environment, directory: path })
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({ workspaceId: projectId, environment, directory: path })
});
}
});
};
@@ -204,12 +200,6 @@ export const useUpdateFolder = () => {
queryClient.invalidateQueries({
queryKey: secretSnapshotKeys.count({ workspaceId: projectId, environment, directory: path })
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({ workspaceId: projectId, environment, directory: path })
});
queryClient.invalidateQueries({
queryKey: commitKeys.history({ workspaceId: projectId, environment, directory: path })
});
}
});
};
@@ -244,12 +234,6 @@ export const useDeleteFolder = () => {
queryClient.invalidateQueries({
queryKey: secretSnapshotKeys.count({ workspaceId: projectId, environment, directory: path })
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({ workspaceId: projectId, environment, directory: path })
});
queryClient.invalidateQueries({
queryKey: commitKeys.history({ workspaceId: projectId, environment, directory: path })
});
}
});
};
@@ -295,20 +279,6 @@ export const useUpdateFolderBatch = () => {
directory: folder.path
})
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({
workspaceId: projectId,
environment: folder.environment,
directory: folder.path
})
});
queryClient.invalidateQueries({
queryKey: commitKeys.history({
workspaceId: projectId,
environment: folder.environment,
directory: folder.path
})
});
});
}
});
@@ -3,7 +3,6 @@ import { MutationOptions, useMutation, useQueryClient } from "@tanstack/react-qu
import { apiRequest } from "@app/config/request";
import { dashboardKeys } from "@app/hooks/api/dashboard/queries";
import { commitKeys } from "../folderCommits/queries";
import { secretApprovalRequestKeys } from "../secretApprovalRequest/queries";
import { secretSnapshotKeys } from "../secretSnapshots/queries";
import { secretKeys } from "./queries";
@@ -60,12 +59,6 @@ export const useCreateSecretV3 = ({
queryClient.invalidateQueries({
queryKey: secretSnapshotKeys.count({ environment, workspaceId, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.history({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ workspaceId }) });
},
...options
@@ -125,12 +118,6 @@ export const useUpdateSecretV3 = ({
queryClient.invalidateQueries({
queryKey: secretSnapshotKeys.count({ environment, workspaceId, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.history({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ workspaceId }) });
},
...options
@@ -177,12 +164,6 @@ export const useDeleteSecretV3 = ({
queryClient.invalidateQueries({
queryKey: secretSnapshotKeys.count({ environment, workspaceId, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.history({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ workspaceId }) });
},
...options
@@ -219,12 +200,6 @@ export const useCreateSecretBatch = ({
queryClient.invalidateQueries({
queryKey: secretSnapshotKeys.count({ environment, workspaceId, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.history({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ workspaceId }) });
},
...options
@@ -261,12 +236,6 @@ export const useUpdateSecretBatch = ({
queryClient.invalidateQueries({
queryKey: secretSnapshotKeys.count({ environment, workspaceId, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.history({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ workspaceId }) });
},
...options
@@ -305,12 +274,6 @@ export const useDeleteSecretBatch = ({
queryClient.invalidateQueries({
queryKey: secretSnapshotKeys.count({ environment, workspaceId, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.history({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({ queryKey: secretApprovalRequestKeys.count({ workspaceId }) });
},
...options
@@ -384,20 +347,6 @@ export const useMoveSecrets = ({
directory: sourceSecretPath
})
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({
workspaceId: projectId,
environment: sourceEnvironment,
directory: sourceSecretPath
})
});
queryClient.invalidateQueries({
queryKey: commitKeys.history({
workspaceId: projectId,
environment: sourceEnvironment,
directory: sourceSecretPath
})
});
queryClient.invalidateQueries({
queryKey: secretApprovalRequestKeys.count({ workspaceId: projectId })
});
+2 -4
View File
@@ -282,8 +282,7 @@ export const useUpdateProject = () => {
newProjectName,
newProjectDescription,
newSlug,
secretSharing,
showSnapshotsLegacy
secretSharing
}) => {
const { data } = await apiRequest.patch<{ workspace: Workspace }>(
`/api/v1/workspace/${projectID}`,
@@ -291,8 +290,7 @@ export const useUpdateProject = () => {
name: newProjectName,
description: newProjectDescription,
slug: newSlug,
secretSharing,
showSnapshotsLegacy
secretSharing
}
);
return data.workspace;
@@ -39,7 +39,6 @@ export type Workspace = {
roles?: TProjectRole[];
hasDeleteProtection: boolean;
secretSharing: boolean;
showSnapshotsLegacy: boolean;
};
export type WorkspaceEnv = {
@@ -80,7 +79,6 @@ export type UpdateProjectDTO = {
newProjectDescription?: string;
newSlug?: string;
secretSharing?: boolean;
showSnapshotsLegacy?: boolean;
};
export type UpdatePitVersionLimitDTO = { projectSlug: string; pitVersionLimit: number };
@@ -23,7 +23,6 @@ import { useGetProjectTypeFromRoute } from "@app/hooks";
import { ProjectType } from "@app/hooks/api/workspace/types";
import {
EXCLUDED_PERMISSION_SUBS,
isConditionalSubjects,
PROJECT_PERMISSION_OBJECT,
ProjectTypePermissionSubjects,
@@ -67,7 +66,6 @@ const Content = ({ onClose }: ContentProps) => {
subject as ProjectPermissionSub
] && (search ? title.toLowerCase().includes(search.toLowerCase()) : true)
)
.filter(([subject]) => !EXCLUDED_PERMISSION_SUBS.includes(subject as ProjectPermissionSub))
.sort((a, b) => a[1].title.localeCompare(b[1].title))
.map(([subject]) => subject);
@@ -12,7 +12,6 @@ import {
} from "@app/context";
import {
PermissionConditionOperators,
ProjectPermissionCommitsActions,
ProjectPermissionDynamicSecretActions,
ProjectPermissionGroupActions,
ProjectPermissionIdentityActions,
@@ -93,11 +92,6 @@ const SecretSyncPolicyActionSchema = z.object({
[ProjectPermissionSecretSyncActions.RemoveSecrets]: z.boolean().optional()
});
const CommitPolicyActionSchema = z.object({
[ProjectPermissionCommitsActions.Read]: z.boolean().optional(),
[ProjectPermissionCommitsActions.PerformRollback]: z.boolean().optional()
});
const SecretRotationPolicyActionSchema = z.object({
[ProjectPermissionSecretRotationActions.Read]: z.boolean().optional(),
[ProjectPermissionSecretRotationActions.ReadGeneratedCredentials]: z.boolean().optional(),
@@ -291,7 +285,6 @@ export const projectRoleFormSchema = z.object({
})
.array()
.default([]),
[ProjectPermissionSub.Commits]: CommitPolicyActionSchema.array().default([]),
[ProjectPermissionSub.Member]: MemberPolicyActionSchema.array().default([]),
[ProjectPermissionSub.Groups]: GroupPolicyActionSchema.array().default([]),
[ProjectPermissionSub.Role]: GeneralPolicyActionSchema.array().default([]),
@@ -892,17 +885,6 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
return;
}
if (subject === ProjectPermissionSub.Commits) {
const canRead = action.includes(ProjectPermissionCommitsActions.Read);
const canPerformRollback = action.includes(ProjectPermissionCommitsActions.PerformRollback);
if (!formVal[subject]) formVal[subject] = [{}];
if (canRead) formVal[subject]![0][ProjectPermissionCommitsActions.Read] = true;
if (canPerformRollback)
formVal[subject]![0][ProjectPermissionCommitsActions.PerformRollback] = true;
return;
}
if (subject === ProjectPermissionSub.PkiSubscribers) {
if (!formVal[subject]) formVal[subject] = [];
@@ -1050,8 +1032,6 @@ export const formRolePermission2API = (formVal: TFormSchema["permissions"]) => {
return permissions;
};
export const EXCLUDED_PERMISSION_SUBS = [ProjectPermissionSub.SecretRollback];
export type TProjectPermissionObject = {
[K in ProjectPermissionSub]: {
title: string;
@@ -1247,13 +1227,6 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
{ label: "Remove", value: "delete" }
]
},
[ProjectPermissionSub.Commits]: {
title: "Commits",
actions: [
{ label: "View", value: ProjectPermissionCommitsActions.Read },
{ label: "Perform Rollback", value: ProjectPermissionCommitsActions.PerformRollback }
]
},
[ProjectPermissionSub.Tags]: {
title: "Tags",
actions: [
@@ -1545,8 +1518,7 @@ const SecretsManagerPermissionSubjects = (enabled = false) => ({
[ProjectPermissionSub.IpAllowList]: enabled,
[ProjectPermissionSub.SecretRollback]: enabled,
[ProjectPermissionSub.SecretRotation]: enabled,
[ProjectPermissionSub.ServiceTokens]: enabled,
[ProjectPermissionSub.Commits]: enabled
[ProjectPermissionSub.ServiceTokens]: enabled
});
const KmsPermissionSubjects = (enabled = false) => ({
@@ -1926,10 +1898,6 @@ export const RoleTemplates: Record<ProjectType, RoleTemplate[]> = {
{
subject: ProjectPermissionSub.SecretSyncs,
actions: [ProjectPermissionSecretSyncActions.Read]
},
{
subject: ProjectPermissionSub.Commits,
actions: [ProjectPermissionCommitsActions.Read]
}
]
},
@@ -1987,10 +1955,6 @@ export const RoleTemplates: Record<ProjectType, RoleTemplate[]> = {
{
subject: ProjectPermissionSub.SecretSyncs,
actions: Object.values(ProjectPermissionSecretSyncActions)
},
{
subject: ProjectPermissionSub.Commits,
actions: Object.values(ProjectPermissionCommitsActions)
}
]
},
@@ -25,7 +25,6 @@ import { PermissionEmptyState } from "./PermissionEmptyState";
import { PkiSubscriberPermissionConditions } from "./PkiSubscriberPermissionConditions";
import { PkiTemplatePermissionConditions } from "./PkiTemplatePermissionConditions";
import {
EXCLUDED_PERMISSION_SUBS,
formRolePermission2API,
isConditionalSubjects,
PROJECT_PERMISSION_OBJECT,
@@ -177,7 +176,6 @@ export const RolePermissionsSection = ({ roleSlug, isDisabled }: Props) => {
<div className="py-4">
{!isPending && <PermissionEmptyState />}
{(Object.keys(PROJECT_PERMISSION_OBJECT) as ProjectPermissionSub[])
.filter((subject) => !EXCLUDED_PERMISSION_SUBS.includes(subject))
.filter((subject) => ProjectTypePermissionSubjects[currentWorkspace.type][subject])
.map((subject) => (
<GeneralPermissionPolicies
@@ -1,86 +0,0 @@
import { useNavigate, useParams, useSearch } from "@tanstack/react-router";
import { ProjectPermissionCan } from "@app/components/permissions";
import { ROUTE_PATHS } from "@app/const/routes";
import { useWorkspace } from "@app/context";
import {
ProjectPermissionCommitsActions,
ProjectPermissionSub
} from "@app/context/ProjectPermissionContext/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { CommitDetailsTab } from "./components/CommitDetailsTab";
export const CommitDetailsPage = () => {
const envSlug = useParams({
from: ROUTE_PATHS.SecretManager.CommitDetailsPage.id,
select: (el) => el.environment
});
const selectedCommitId = useParams({
from: ROUTE_PATHS.SecretManager.CommitDetailsPage.id,
select: (el) => el.commitId
});
const folderId = useParams({
from: ROUTE_PATHS.SecretManager.CommitDetailsPage.id,
select: (el) => el.folderId
});
const { currentWorkspace } = useWorkspace();
const navigate = useNavigate();
const routerQueryParams: { secretPath?: string } = useSearch({
from: ROUTE_PATHS.SecretManager.CommitDetailsPage.id
});
const secretPath = (routerQueryParams.secretPath as string) || "/";
const handleGoBackToHistory = () => {
navigate({
to: `/${ProjectType.SecretManager}/$projectId/commits/$environment/$folderId` as const,
params: {
projectId: currentWorkspace.id,
folderId,
environment: envSlug
},
search: (query) => ({
...query,
secretPath
})
});
};
const handleGoToRollbackPreview = () => {
navigate({
to: `/${ProjectType.SecretManager}/$projectId/commits/$environment/$folderId/$commitId/restore` as const,
params: {
projectId: currentWorkspace.id,
folderId,
environment: envSlug,
commitId: selectedCommitId
},
search: (query) => ({
...query,
secretPath
})
});
};
return (
<div className="mx-auto flex w-full max-w-7xl justify-center bg-bunker-800 pb-4 pt-2 text-white">
<div className="w-full max-w-[75vw]">
<ProjectPermissionCan
renderGuardBanner
I={ProjectPermissionCommitsActions.Read}
a={ProjectPermissionSub.Commits}
>
<CommitDetailsTab
selectedCommitId={selectedCommitId}
workspaceId={currentWorkspace.id}
goBackToHistory={handleGoBackToHistory}
envSlug={envSlug}
goToRollbackPreview={handleGoToRollbackPreview}
/>
</ProjectPermissionCan>
</div>
</div>
);
};
@@ -1,362 +0,0 @@
import { useEffect, useState } from "react";
import { faAngleDown } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { DropdownMenuItem } from "@radix-ui/react-dropdown-menu";
import { useSearch } from "@tanstack/react-router";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions";
import {
DeleteActionModal,
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
IconButton,
Spinner
} from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import {
ProjectPermissionCommitsActions,
ProjectPermissionSub
} from "@app/context/ProjectPermissionContext/types";
import { usePopUp } from "@app/hooks";
import { CommitWithChanges } from "@app/hooks/api/folderCommits";
import { useCommitRevert, useGetCommitDetails } from "@app/hooks/api/folderCommits/queries";
import { CommitType } from "@app/hooks/api/types";
import { SecretVersionDiffView } from "../SecretVersionDiffView";
import { MergedItem } from "./types";
const formatDisplayDate = (dateString: string): string => {
try {
const date = new Date(dateString);
const options: Intl.DateTimeFormatOptions = {
year: "numeric",
month: "short",
day: "numeric",
hour: "numeric",
minute: "numeric",
hour12: true
};
return new Intl.DateTimeFormat("en-US", options).format(date);
} catch {
return dateString;
}
};
export const CommitDetailsTab = ({
selectedCommitId,
workspaceId,
envSlug,
goBackToHistory,
goToRollbackPreview
}: {
selectedCommitId: string;
workspaceId: string;
envSlug: string;
goBackToHistory: () => void;
goToRollbackPreview: () => void;
}): JSX.Element => {
// State for tracking collapsed items (empty by default means all are expanded)
const [collapsedItems, setCollapsedItems] = useState<Record<string, boolean>>({});
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
"revertChanges"
] as const);
const { data: commitDetails, isLoading } = useGetCommitDetails(workspaceId, selectedCommitId);
const routerQueryParams: { secretPath?: string } = useSearch({
from: ROUTE_PATHS.SecretManager.CommitDetailsPage.id
});
const secretPath = (routerQueryParams.secretPath as string) || "/";
const { mutateAsync: revert } = useCommitRevert({
commitId: selectedCommitId,
projectId: workspaceId,
environment: envSlug,
directory: secretPath
});
useEffect(() => {
setCollapsedItems({});
}, [selectedCommitId]);
const toggleItemCollapsed = (itemId: string): void => {
setCollapsedItems((prev) => ({ ...prev, [itemId]: !prev[itemId] }));
};
const handleRevertChanges = async (): Promise<void> => {
const response = await revert();
if (!response.success) {
createNotification({
type: "error",
text: response.message
});
return;
}
createNotification({
type: "success",
text: response.message
});
handlePopUpClose("revertChanges");
goBackToHistory();
};
// If no commit is selected or data is loading, show appropriate message
if (!selectedCommitId) {
return (
<div className="flex h-64 items-center justify-center">
<p className="text-gray-400">Select a commit to view details</p>
</div>
);
}
if (isLoading) {
return (
<div className="flex h-64 items-center justify-center">
<Spinner size="lg" />
</div>
);
}
if (!commitDetails) {
return (
<div className="flex h-64 items-center justify-center">
<p className="text-gray-400">No details found for this commit</p>
</div>
);
}
// Parse the commit details if it's a string
let parsedCommitDetails: CommitWithChanges;
try {
parsedCommitDetails =
typeof commitDetails === "string" ? JSON.parse(commitDetails) : commitDetails;
} catch (error) {
console.error("Failed to parse commit details:", error);
return (
<div className="flex h-64 items-center justify-center">
<p className="text-gray-400">Error parsing commit details</p>
</div>
);
}
// Get all changes from the commit
const commitChanges = parsedCommitDetails.changes?.changes || [];
// Separate changes by type
const addedChanges = commitChanges.filter((c) => c.changeType === CommitType.ADD && !c.isUpdate);
const updatedChanges = commitChanges.filter((c) => c.changeType === CommitType.ADD && c.isUpdate);
const deletedChanges = commitChanges.filter((c) => c.changeType === CommitType.DELETE);
// Create merged item list from changes only
const changedItems: MergedItem[] = [];
// Add items from added changes
addedChanges.forEach((change) => {
changedItems.push({
id: change.id,
type: change.secretVersionId || change.secretKey ? "secret" : "folder",
versionId: change.secretVersionId || change.id,
folderName: change.folderName,
folderVersion: change.folderVersion,
secretKey: change.secretKey,
secretVersion: change.secretVersion,
isAdded: true,
versions: change.versions,
changeId: change.id
});
});
// Add items from updated changes
updatedChanges.forEach((change) => {
changedItems.push({
id: change.id,
type: change.secretVersionId || change.secretKey ? "secret" : "folder",
versionId: change.secretVersionId || change.id,
folderName: change.folderName,
folderVersion: change.folderVersion,
secretKey: change.secretKey,
secretVersion: change.secretVersion,
isUpdated: true,
versions: change.versions,
changeId: change.id
});
});
// Add deleted items
deletedChanges.forEach((change) => {
changedItems.push({
id: change.id,
type: change.secretVersionId || change.secretKey ? "secret" : "folder",
secretKey: change.secretKey,
folderName: change.folderName,
secretVersion: change.secretVersion,
folderVersion: change.folderVersion,
isDeleted: true,
versions: change.versions,
changeId: change.id
});
});
// Sort items: deleted first, then folders, then secrets, all alphabetically
const sortedChangedItems = [...changedItems].sort((a, b) => {
// First sort deleted items to the top
if (a.isDeleted !== b.isDeleted) {
return a.isDeleted ? -1 : 1;
}
// Then sort by type (folders before secrets)
if (a.type !== b.type) {
return a.type === "folder" ? -1 : 1;
}
// Finally sort alphabetically by name
const aName = a.type === "folder" ? a.folderName || "" : a.secretKey || "";
const bName = b.type === "folder" ? b.folderName || "" : b.secretKey || "";
return aName.localeCompare(bName);
});
// Render an item from the merged list
const renderMergedItem = (item: MergedItem): JSX.Element => {
return (
<div key={item.id} className="mb-2">
<SecretVersionDiffView
item={item}
isCollapsed={collapsedItems[item.id]}
onToggleCollapse={(id) => toggleItemCollapsed(id)}
/>
</div>
);
};
// Format actor display
const actorDisplay =
parsedCommitDetails.changes?.actorMetadata?.name ||
parsedCommitDetails.changes?.actorType ||
"Unknown";
return (
<div className="w-full">
<div>
<div className="flex justify-between pb-2">
<div className="w-5/6">
<div>
<div className="flex items-center">
<h1 className="mr-4 truncate text-3xl font-semibold text-white">
{parsedCommitDetails.changes?.message || "No message"}
</h1>
</div>
</div>
<div className="font-small mb-4 mt-2 flex items-center text-sm">
<p>
<span> Commited by </span>
<b>{actorDisplay}</b>
<span> on </span>
<b>
{formatDisplayDate(
parsedCommitDetails.changes?.createdAt || new Date().toISOString()
)}
</b>
{parsedCommitDetails.changes?.isLatest && (
<span className="ml-1 italic text-gray-400">(Latest)</span>
)}
</p>
</div>
</div>
<div className="flex items-center justify-start">
<ProjectPermissionCan
I={ProjectPermissionCommitsActions.PerformRollback}
a={ProjectPermissionSub.Commits}
>
{(isAllowed) => (
<DropdownMenu>
<DropdownMenuTrigger
asChild
disabled={!isAllowed}
className={`${!isAllowed ? "cursor-not-allowed" : ""}`}
>
<IconButton
ariaLabel="commit-options"
variant="outline_bg"
className="h-10 rounded border border-mineshaft-600 bg-mineshaft-800 px-4 py-2 text-sm font-medium"
>
<p className="mr-2">Restore Options</p>
<FontAwesomeIcon icon={faAngleDown} />
</IconButton>
</DropdownMenuTrigger>
<DropdownMenuContent
align="end"
sideOffset={2}
className="animate-in fade-in-50 zoom-in-95 min-w-[240px] rounded-md bg-mineshaft-800 p-1 shadow-lg"
style={{ marginTop: "0" }}
>
{!parsedCommitDetails.changes.isLatest && (
<DropdownMenuItem
className="group cursor-pointer rounded-md px-3 py-3 transition-colors hover:bg-mineshaft-700"
onClick={() => goToRollbackPreview()}
>
<div className="flex items-center space-x-3">
<div className="flex flex-col">
<span className="text-sm font-medium text-white">
Roll back to this commit
</span>
<span className="max-w-[180px] whitespace-normal break-words text-xs leading-snug text-gray-400">
Return this folder to its exact state at the time of this commit,
discarding all other changes made after it
</span>
</div>
</div>
</DropdownMenuItem>
)}
<DropdownMenuItem
className="group cursor-pointer rounded-md px-3 py-3 transition-colors hover:bg-mineshaft-700"
onClick={() => handlePopUpOpen("revertChanges")}
>
<div className="flex items-center space-x-3">
<div className="flex flex-col">
<span className="text-sm font-medium text-white">Revert changes</span>
<span className="max-w-[180px] whitespace-normal break-words text-xs leading-snug text-gray-400">
Will restore to the previous version of affected resources
</span>
</div>
</div>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)}
</ProjectPermissionCan>
</div>
</div>
<div className="py-2">
<div className="overflow-hidden">
<div className="space-y-2">
{sortedChangedItems.length > 0 ? (
sortedChangedItems.map((item) => renderMergedItem(item))
) : (
<div className="flex h-32 items-center justify-center rounded-lg border border-mineshaft-600 bg-mineshaft-800">
<p className="text-gray-400">No changed items found</p>
</div>
)}
</div>
</div>
</div>
</div>
<DeleteActionModal
isOpen={popUp.revertChanges.isOpen}
deleteKey="revert"
title="Are you sure you want to revert all changes made in this commit?"
subTitle="This will undo all changes made in this commit."
onChange={(isOpen) => handlePopUpToggle("revertChanges", isOpen)}
onDeleteApproved={handleRevertChanges}
buttonText="Yes, revert changes"
/>
</div>
);
};
@@ -1 +0,0 @@
export { CommitDetailsTab } from "./CommitDetailsTab";
@@ -1,14 +0,0 @@
export interface MergedItem {
id: string;
type: "secret" | "folder";
versionId?: string;
folderName?: string;
folderVersion?: string;
secretKey?: string;
secretVersion?: string;
isAdded?: boolean;
isUpdated?: boolean;
isDeleted?: boolean;
versions?: any[];
changeId: string;
}
@@ -1,389 +0,0 @@
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import { useEffect, useState } from "react";
import { faFolder, faInfoCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNavigate, useParams, useSearch } from "@tanstack/react-router";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions";
import {
Button,
DeleteActionModal,
Input,
PageHeader,
Spinner,
Switch,
Tooltip
} from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { useWorkspace } from "@app/context";
import {
ProjectPermissionCommitsActions,
ProjectPermissionSub
} from "@app/context/ProjectPermissionContext/types";
import { usePopUp } from "@app/hooks";
import { useCommitRollback, useGetRollbackPreview } from "@app/hooks/api/folderCommits/queries";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { SecretVersionDiffView } from "../SecretVersionDiffView";
interface Version {
// Common fields
id?: string;
version: number;
createdAt?: string;
updatedAt?: string;
// Secret-specific fields
secretKey?: string;
secretValue?: string;
secretComment?: string;
skipMultilineEncoding?: boolean;
secretReminderRepeatDays?: number | null;
secretReminderNote?: string | null;
secretReminderRecipients?: string[];
tags?: string[];
metadata?: Record<string, unknown>;
// Folder-specific fields
name?: string;
envId?: string;
folderId?: string;
[key: string]: any;
}
interface RollbackChange {
type: "secret" | "folder";
id: string;
versionId: string;
fromVersion?: number;
changeType: "create" | "update" | "delete";
commitId: string;
secretKey?: string;
secretVersion?: number;
folderName?: string;
folderVersion?: number;
versions?: Version[];
createdAt?: string;
}
interface FolderChanges {
folderId: string;
folderName: string;
folderPath: string;
changes: RollbackChange[];
}
export const RollbackPreviewTab = (): JSX.Element => {
const [deepRollback, setDeepRollback] = useState<boolean>(false);
const [message, setMessage] = useState<string>("");
const [selectedFolderId, setSelectedFolderId] = useState<string | null>(null);
const { currentWorkspace } = useWorkspace();
const envSlug = useParams({
from: ROUTE_PATHS.SecretManager.RollbackPreviewPage.id,
select: (el) => el.environment
});
const selectedCommitId = useParams({
from: ROUTE_PATHS.SecretManager.RollbackPreviewPage.id,
select: (el) => el.commitId
});
const folderId = useParams({
from: ROUTE_PATHS.SecretManager.RollbackPreviewPage.id,
select: (el) => el.folderId
});
const navigate = useNavigate();
const routerQueryParams = useSearch({
from: ROUTE_PATHS.SecretManager.RollbackPreviewPage.id
});
const secretPath = (routerQueryParams.secretPath as string) || "/";
const goBackToHistory = () => {
navigate({
to: `/${ProjectType.SecretManager}/$projectId/commits/$environment/$folderId` as const,
params: {
projectId: currentWorkspace.id,
folderId,
environment: envSlug
},
search: (query) => ({
...query,
secretPath
})
});
};
const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([
"rollbackConfirm"
] as const);
const { mutateAsync: rollback } = useCommitRollback({
workspaceId: currentWorkspace.id,
commitId: selectedCommitId,
folderId,
deepRollback,
environment: envSlug,
directory: secretPath,
envSlug
});
const { data: rollbackChangesNested, isLoading } = useGetRollbackPreview(
folderId,
selectedCommitId,
envSlug,
currentWorkspace.id,
deepRollback,
secretPath
);
const handleRollback = async (): Promise<void> => {
try {
await rollback(message);
createNotification({
type: "success",
text: "Rollback completed successfully"
});
handlePopUpClose("rollbackConfirm");
goBackToHistory();
} catch (error) {
createNotification({
type: "error",
text: error instanceof Error ? error.message : "Failed to rollback changes"
});
}
};
const folderChanges: FolderChanges[] = rollbackChangesNested || [];
const currentFolderChanges: FolderChanges = folderChanges.find(
(folder) => folder.folderId === folderId
) || {
folderId: folderId || "",
folderName: "Current Folder",
folderPath: secretPath,
changes: []
};
const nestedFolderChanges: FolderChanges[] = folderChanges.filter(
(folder) => folder.folderId !== folderId
);
useEffect(() => {
// Select the current folder by default
if (folderChanges.length > 0) {
setSelectedFolderId(currentFolderChanges.folderId);
}
}, [folderChanges, currentFolderChanges.folderId]);
if (!selectedCommitId) {
return (
<div className="flex h-64 items-center justify-center">
<p className="text-gray-400">Select a commit to view rollback preview</p>
</div>
);
}
if (isLoading) {
return (
<div className="flex h-64 items-center justify-center">
<Spinner size="lg" />
</div>
);
}
const renderSidebar = (): JSX.Element => {
return (
<div className="h-[70vh] w-72 overflow-y-auto bg-mineshaft-900">
<div
className={`cursor-pointer border-b border-mineshaft-600 ${
selectedFolderId === currentFolderChanges.folderId ? "bg-mineshaft-700" : ""
}`}
onClick={() => setSelectedFolderId(currentFolderChanges.folderId)}
>
<div className="flex items-center justify-between px-4 py-3">
<div className="flex items-center">
<FontAwesomeIcon icon={faFolder} className="mr-2 text-yellow-500" />
<span className="font-medium text-white">
{currentFolderChanges.folderPath || currentFolderChanges.folderName}
</span>
</div>
{currentFolderChanges.changes.length > 0 && (
<span className="ml-2 rounded-full bg-mineshaft-600 px-2 py-0.5 text-xs text-gray-300">
{currentFolderChanges.changes.length}
</span>
)}
</div>
</div>
{deepRollback && nestedFolderChanges.length > 0 && (
<>
<div className="border-b border-mineshaft-600 bg-mineshaft-800 px-4 py-2">
<span className="text-sm font-semibold text-white">Child folders to be restored</span>
</div>
{nestedFolderChanges.map((folder) => (
<div
key={folder.folderId}
className={`cursor-pointer border-b border-mineshaft-600 ${
selectedFolderId === folder.folderId ? "bg-mineshaft-700" : ""
}`}
onClick={() => setSelectedFolderId(folder.folderId)}
>
<div className="flex items-center justify-between px-4 py-2">
<div className="flex items-center">
<FontAwesomeIcon icon={faFolder} className="mr-2 text-yellow-500" size="sm" />
<span className="max-w-[150px] truncate text-sm font-medium text-white">
{folder.folderPath || folder.folderName}
</span>
</div>
{folder.changes.length > 0 && (
<span className="rounded-full bg-mineshaft-600 px-2 py-0.5 text-xs text-gray-300">
{folder.changes.length}
</span>
)}
</div>
</div>
))}
</>
)}
</div>
);
};
const getSelectedFolderChanges = (): RollbackChange[] => {
if (!selectedFolderId) return [];
const folder = folderChanges.find((f) => f.folderId === selectedFolderId);
return folder?.changes || [];
};
const renderMainContent = (): JSX.Element => {
const selectedFolderChanges = getSelectedFolderChanges();
const selectedFolder = folderChanges.find((f) => f.folderId === selectedFolderId);
if (!selectedFolder || selectedFolderChanges.length === 0) {
return (
<div className="flex h-[70vh] w-full items-center justify-center border border-mineshaft-600">
<p className="text-gray-400">No changes in selected folder</p>
</div>
);
}
return (
<div className="h-[70vh] w-full overflow-y-auto border-l border-mineshaft-600">
<div className="space-y-4 p-4">
{selectedFolderChanges.map((change) => (
<div key={change.id} className="mb-4">
<SecretVersionDiffView
item={{
id: change.id,
type: change.type,
isAdded: change.changeType === "create",
isDeleted: change.changeType === "delete",
isUpdated: change.changeType === "update",
versions: change.versions,
isRollback: true,
secretKey: change.secretKey,
folderName: change.folderName
}}
/>
</div>
))}
</div>
</div>
);
};
return (
<div className="mx-auto flex w-full max-w-7xl justify-center bg-bunker-800 pb-4 pt-2 text-white">
<ProjectPermissionCan
renderGuardBanner
I={ProjectPermissionCommitsActions.PerformRollback}
a={ProjectPermissionSub.Commits}
>
<div className="w-full max-w-[75vw]">
<div className="h-full w-full">
<div>
<PageHeader
title={`Restore folder at commit ${selectedCommitId.substring(0, 8)}`}
description={`Will return all changes in this folder to how they appeared at the point of commit ${selectedCommitId.substring(0, 8)}. Any modifications made after this commit will be undone.`}
/>
<div className="flex w-full border border-mineshaft-600">
{renderSidebar()}
{renderMainContent()}
</div>
<div className="border-x border-mineshaft-600 bg-mineshaft-800 px-6 py-3">
<div className="flex items-center justify-end">
<div className="flex items-center">
<Tooltip content="Will rollback all nested folders to their state at the time of this commit">
<FontAwesomeIcon icon={faInfoCircle} className="mr-2 text-mineshaft-400" />
</Tooltip>
<Switch
className="ml-2 bg-mineshaft-400/50 shadow-inner data-[state=checked]:bg-green/50"
thumbClassName="bg-mineshaft-800"
isChecked={deepRollback}
onCheckedChange={setDeepRollback}
id="deep-rollback"
>
Restore All Child Folders
</Switch>
</div>
</div>
</div>
<div className="border-t border-mineshaft-600 py-4">
<div className="flex w-full items-center justify-between gap-2">
<Input
placeholder="Restore Message"
value={message}
onChange={(e) => setMessage(e.target.value)}
className="w-full border-mineshaft-500 bg-mineshaft-700 py-2 text-sm"
maxLength={256}
/>
<Button
onClick={() => {
handlePopUpOpen("rollbackConfirm");
}}
colorSchema="primary"
className="px-6 py-2"
isDisabled={
message.length === 0 ||
!rollbackChangesNested ||
!rollbackChangesNested?.some((folder) => {
return folder.changes.length > 0;
})
}
>
Restore
</Button>
</div>
</div>
</div>
<DeleteActionModal
isOpen={popUp.rollbackConfirm.isOpen}
deleteKey="restore"
title={`Are you sure you want to restore to commit ${selectedCommitId?.substring(0, 8)}?`}
subTitle={`
${
deepRollback
? "This will revert this folder and all child folders to how they appeared at the point in time of this commit."
: "This will revert all changes to how they appeared at the point in time of this commit."
}
Any changes made after this commit will be permanently removed.
`}
onChange={(isOpen) => handlePopUpToggle("rollbackConfirm", isOpen)}
onDeleteApproved={handleRollback}
buttonText="Restore"
/>
</div>{" "}
</div>{" "}
</ProjectPermissionCan>
</div>
);
};
@@ -1 +0,0 @@
export { RollbackPreviewTab } from "./RollbackPreviewTab";
@@ -1,91 +0,0 @@
import { createFileRoute, linkOptions, stripSearchParams } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { SecretDashboardPathBreadcrumb } from "@app/components/navigation/SecretDashboardPathBreadcrumb";
import { BreadcrumbTypes } from "@app/components/v2";
import { RollbackPreviewTab } from "./RollbackPreviewTab";
const RollbackPreviewTabQueryParamsSchema = z.object({
secretPath: z.string().catch("/")
});
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/restore"
)({
component: RollbackPreviewTab,
validateSearch: zodValidator(RollbackPreviewTabQueryParamsSchema),
search: {
middlewares: [stripSearchParams({ secretPath: "/" })]
},
beforeLoad: ({ context, params, search }) => {
const secretPathSegments = search.secretPath.split("/").filter(Boolean);
return {
breadcrumbs: [
...context.breadcrumbs,
{
type: BreadcrumbTypes.Dropdown,
label:
context.project.environments.find((el) => el.slug === params.environment)?.name || "",
dropdownTitle: "Environments",
links: context.project.environments.map((el) => ({
label: el.name,
link: linkOptions({
to: "/secret-manager/$projectId/secrets/$envSlug",
params: {
projectId: params.projectId,
envSlug: el.slug
}
})
}))
},
...secretPathSegments.map((_, index) => ({
type: BreadcrumbTypes.Component,
component: () => (
<SecretDashboardPathBreadcrumb
secretPathSegments={secretPathSegments}
selectedPathSegmentIndex={index}
environmentSlug={params.environment}
projectId={params.projectId}
disableCopy
/>
)
})),
{
label: "Commits",
link: linkOptions({
to: "/secret-manager/$projectId/commits/$environment/$folderId",
params: {
projectId: params.projectId,
environment: params.environment,
folderId: params.folderId
},
search: {
secretPath: search.secretPath
}
})
},
{
label: params.commitId,
link: linkOptions({
to: "/secret-manager/$projectId/commits/$environment/$folderId/$commitId",
params: {
projectId: params.projectId,
environment: params.environment,
folderId: params.folderId,
commitId: params.commitId
},
search: {
secretPath: search.secretPath
}
})
},
{
label: "Restore"
}
]
};
}
});
@@ -1,652 +0,0 @@
/* eslint-disable no-nested-ternary */
import { useCallback, useRef, useState } from "react";
import { faChevronDown, faChevronUp } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
export interface Version {
id?: string;
version: number;
[key: string]: any;
}
type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
type JsonObject = { [key: string]: JsonValue };
type JsonArray = JsonValue[];
export interface DiffViewItem {
type: "secret" | "folder";
isAdded?: boolean;
isDeleted?: boolean;
isUpdated?: boolean;
versions?: Version[];
isRollback?: boolean;
id: string;
secretKey?: string;
folderName?: string;
}
interface SecretVersionDiffViewProps {
item: DiffViewItem;
isCollapsed?: boolean;
onToggleCollapse?: (id: string) => void;
showHeader?: boolean;
customHeader?: JSX.Element;
excludedFieldsHighlight?: string[];
}
const isObject = (obj: JsonValue): obj is JsonObject => {
return obj !== null && typeof obj === "object" && !Array.isArray(obj);
};
const isArray = (obj: JsonValue): obj is JsonArray => {
return Array.isArray(obj);
};
const deepEqual = (a: JsonValue, b: JsonValue): boolean => {
if (a === b) return true;
if (a == null || b == null) return false;
if (typeof a !== typeof b) return false;
if (isArray(a) && isArray(b)) {
if (a.length !== b.length) return false;
return a.every((item: JsonValue, index: number) => deepEqual(item, b[index]));
}
if (isObject(a) && isObject(b)) {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
return keysA.every((key) => keysB.includes(key) && deepEqual(a[key], b[key]));
}
return false;
};
const getDiffPaths = (oldObj: JsonValue, newObj: JsonValue, path: string = ""): Set<string> => {
const diffPaths = new Set<string>();
if (oldObj === newObj) return diffPaths;
if (oldObj == null || newObj == null) {
diffPaths.add(path || "root");
return diffPaths;
}
if (typeof oldObj !== typeof newObj) {
diffPaths.add(path || "root");
return diffPaths;
}
if (isArray(oldObj) && isArray(newObj)) {
return diffPaths;
}
if (isObject(oldObj) && isObject(newObj)) {
const allKeys = new Set([...Object.keys(oldObj), ...Object.keys(newObj)]);
allKeys.forEach((key) => {
const currentPath = path ? `${path}.${key}` : key;
if (path.includes("[")) {
return;
}
if (!(key in oldObj) || !(key in newObj) || !deepEqual(oldObj[key], newObj[key])) {
diffPaths.add(currentPath);
if (
key in oldObj &&
key in newObj &&
(isObject(oldObj[key]) || isArray(oldObj[key])) &&
(isObject(newObj[key]) || isArray(newObj[key]))
) {
const nestedDiffs = getDiffPaths(oldObj[key], newObj[key], currentPath);
nestedDiffs.forEach((p) => diffPaths.add(p));
}
}
});
return diffPaths;
}
if (oldObj !== newObj) {
diffPaths.add(path || "root");
}
return diffPaths;
};
const getNestedValue = (obj: JsonValue, path: string): JsonValue => {
if (!path) return obj;
const parts = path.split(/[.[\]]+/).filter(Boolean);
let current: JsonValue = obj;
parts.forEach((part) => {
if (current == null) {
return;
}
if (isObject(current)) {
current = current[part];
} else if (isArray(current)) {
const index = parseInt(part, 10);
if (!Number.isNaN(index)) {
current = current[index];
}
}
});
return current;
};
const isPathDifferent = (jsonPath: string, diffPaths: Set<string>): boolean => {
if (diffPaths.has(jsonPath)) return true;
const diffPathsArray = Array.from(diffPaths);
return diffPathsArray.some((diffPath) => {
return (
jsonPath.startsWith(`${diffPath}.`) ||
jsonPath.startsWith(`${diffPath}[`) ||
diffPath.startsWith(`${jsonPath}.`) ||
diffPath.startsWith(`${jsonPath}[`)
);
});
};
const isContainerActuallyChanged = (
path: string,
diffPaths: Set<string>,
oldObj: JsonValue,
newObj: JsonValue
): boolean => {
if (diffPaths.has(path)) {
if (oldObj == null || newObj == null) return true;
if (typeof oldObj !== typeof newObj) return true;
if (isArray(oldObj) !== isArray(newObj)) return true;
if (isObject(oldObj) !== isObject(newObj)) return true;
if (!isObject(oldObj) && !isArray(oldObj)) return true;
return false;
}
if (isArray(oldObj) && isArray(newObj)) {
return false;
}
if (isObject(oldObj) && isObject(newObj)) {
return false;
}
return oldObj !== newObj;
};
const renderJsonWithDiffs = (
obj: JsonValue,
diffPaths: Set<string>,
isOldVersion: boolean,
path: string = "",
indentLevel: number = 0,
keyName?: string,
isLastItem: boolean = false,
excludedFieldsHighlight: string[] = [],
oldVersionObj?: JsonValue,
newVersionObj?: JsonValue
): JSX.Element => {
const indent = " ".repeat(indentLevel);
let isDifferent = false;
if (path.includes("[") && oldVersionObj && newVersionObj) {
const arrayMatch = path.match(/^([^[]+)\[(\d+)\]/);
if (arrayMatch) {
const arrayPath = arrayMatch[1];
const itemIndex = parseInt(arrayMatch[2], 10);
const oldArray = getNestedValue(oldVersionObj, arrayPath);
const newArray = getNestedValue(newVersionObj, arrayPath);
if (isArray(oldArray) && isArray(newArray)) {
const currentItem = isOldVersion ? oldArray[itemIndex] : newArray[itemIndex];
if (isOldVersion) {
isDifferent = !newArray.some((newItem: JsonValue) => deepEqual(currentItem, newItem));
} else {
isDifferent = !oldArray.some((oldItem: JsonValue) => deepEqual(currentItem, oldItem));
}
} else {
isDifferent = isPathDifferent(path, diffPaths);
}
} else {
isDifferent = isPathDifferent(path, diffPaths);
}
} else {
isDifferent = isPathDifferent(path, diffPaths);
}
const getLineClass = (different: boolean) => {
if (!different) return "flex";
return isOldVersion ? "flex bg-red-950 text-red-300" : "flex bg-green-950 text-green-300";
};
const getHighlightClass = (different: boolean) => {
if (!different) return "";
return isOldVersion ? "bg-red-900 rounded px-1" : "bg-green-900 rounded px-1";
};
const prefix = isDifferent ? (isOldVersion ? "-" : "+") : " ";
const keyDisplay = keyName ? `"${keyName}": ` : "";
const comma = !isLastItem ? "," : "";
const reactKey = `${path || "root"}-${keyName || "value"}-${indentLevel}-${typeof obj}`;
if (
obj === null ||
typeof obj === "string" ||
typeof obj === "number" ||
typeof obj === "boolean"
) {
let valueDisplay = "";
if (obj === null) valueDisplay = "null";
else if (typeof obj === "string") valueDisplay = `"${obj}"`;
else valueDisplay = String(obj);
return (
<div key={reactKey} className={getLineClass(isDifferent)}>
<div className="w-4 flex-shrink-0">{prefix}</div>
<div>
{indent}
{keyName && <span className={getHighlightClass(isDifferent)}>{keyDisplay}</span>}
<span className={getHighlightClass(isDifferent)}>{valueDisplay}</span>
{comma}
</div>
</div>
);
}
if (isArray(obj) && obj.length === 0) {
return (
<div key={reactKey} className={getLineClass(isDifferent)}>
<div className="w-4 flex-shrink-0">{prefix}</div>
<div>
{indent}
{keyName && <span className={getHighlightClass(isDifferent)}>{keyDisplay}</span>}
<span className={getHighlightClass(isDifferent)}>[]</span>
{comma}
</div>
</div>
);
}
if (isObject(obj) && Object.keys(obj).length === 0) {
return (
<div key={reactKey} className={getLineClass(isDifferent)}>
<div className="w-4 flex-shrink-0">{prefix}</div>
<div>
{indent}
{keyName && <span className={getHighlightClass(isDifferent)}>{keyDisplay}</span>}
<span className={getHighlightClass(isDifferent)}>{"{}"}</span>
{comma}
</div>
</div>
);
}
let isContainerAddedOrRemoved = false;
if (oldVersionObj && newVersionObj) {
const oldValue = getNestedValue(oldVersionObj, path);
const newValue = getNestedValue(newVersionObj, path);
if (oldValue == null || newValue == null) {
isContainerAddedOrRemoved = true;
} else if (typeof oldValue !== typeof newValue) {
isContainerAddedOrRemoved = true;
} else if (isArray(oldValue) !== isArray(newValue)) {
isContainerAddedOrRemoved = true;
} else if (isObject(oldValue) !== isObject(newValue)) {
isContainerAddedOrRemoved = true;
}
} else {
isContainerAddedOrRemoved = isContainerActuallyChanged(
path,
diffPaths,
isOldVersion ? obj : oldVersionObj || null,
isOldVersion ? newVersionObj || null : obj
);
}
if (isArray(obj)) {
return (
<div key={reactKey}>
<div className={getLineClass(isContainerAddedOrRemoved)}>
<div className="w-4 flex-shrink-0">
{isContainerAddedOrRemoved ? (isOldVersion ? "-" : "+") : " "}
</div>
<div>
{indent}
{keyName && (
<span className={isContainerAddedOrRemoved ? getHighlightClass(true) : ""}>
{keyDisplay}
</span>
)}
<span className={isContainerAddedOrRemoved ? getHighlightClass(true) : ""}>[</span>
</div>
</div>
{obj.map((item: JsonValue, index: number) => {
const itemPath = path ? `${path}[${index}]` : `[${index}]`;
const isLast = index === obj.length - 1;
return (
<div key={`${reactKey}-item-${index + 1}`}>
{renderJsonWithDiffs(
item,
diffPaths,
isOldVersion,
itemPath,
indentLevel + 1,
undefined,
isLast,
excludedFieldsHighlight,
oldVersionObj,
newVersionObj
)}
</div>
);
})}
<div className={getLineClass(isContainerAddedOrRemoved)}>
<div className="w-4 flex-shrink-0">
{isContainerAddedOrRemoved ? (isOldVersion ? "-" : "+") : " "}
</div>
<div>
{indent}
<span className={isContainerAddedOrRemoved ? getHighlightClass(true) : ""}>]</span>
{comma}
</div>
</div>
</div>
);
}
if (isObject(obj)) {
const keys = Object.keys(obj);
return (
<div key={reactKey}>
<div className={getLineClass(isContainerAddedOrRemoved)}>
<div className="w-4 flex-shrink-0">
{isContainerAddedOrRemoved ? (isOldVersion ? "-" : "+") : " "}
</div>
<div>
{indent}
{keyName && (
<span className={isContainerAddedOrRemoved ? getHighlightClass(true) : ""}>
{keyDisplay}
</span>
)}
<span className={isContainerAddedOrRemoved ? getHighlightClass(true) : ""}>{"{"}</span>
</div>
</div>
{keys.map((key, index) => {
const keyPath = path ? `${path}.${key}` : key;
const isLast = index === keys.length - 1;
const propKey = `${reactKey}-prop-${key}`;
return (
<div key={propKey}>
{renderJsonWithDiffs(
obj[key],
diffPaths,
isOldVersion,
keyPath,
indentLevel + 1,
key,
isLast,
excludedFieldsHighlight,
oldVersionObj,
newVersionObj
)}
</div>
);
})}
<div className={getLineClass(isContainerAddedOrRemoved)}>
<div className="w-4 flex-shrink-0">
{isContainerAddedOrRemoved ? (isOldVersion ? "-" : "+") : " "}
</div>
<div>
{indent}
<span className={isContainerAddedOrRemoved ? getHighlightClass(true) : ""}>{"}"}</span>
{comma}
</div>
</div>
</div>
);
}
return (
<div key={reactKey} className={getLineClass(isDifferent)}>
<div className="w-4 flex-shrink-0">{prefix}</div>
<div>
{indent}
{keyDisplay}
{String(obj)}
{comma}
</div>
</div>
);
};
const formatAddedJson = (json: JsonValue): JSX.Element => {
const lines = JSON.stringify(json, null, 2).split("\n");
return (
<div className="font-mono text-sm text-green-300">
{lines.map((line, lineIndex) => {
const lineKey = `added-${line.slice(0, 30)}-${lineIndex}`;
return (
<div key={lineKey} className="flex">
<div className="w-4 flex-shrink-0">+</div>
<div>{line}</div>
</div>
);
})}
</div>
);
};
const formatDeletedJson = (json: JsonValue): JSX.Element => {
const lines = JSON.stringify(json, null, 2).split("\n");
return (
<div className="font-mono text-sm text-red-300">
{lines.map((line, lineIndex) => {
const lineKey = `deleted-${line.slice(0, 30)}-${lineIndex}`;
return (
<div key={lineKey} className="flex">
<div className="w-4 flex-shrink-0">-</div>
<div>{line}</div>
</div>
);
})}
</div>
);
};
const cleanVersionForComparison = (version: Version): JsonValue => {
const { id, version: versionNumber, ...cleanVersion } = version;
return cleanVersion;
};
export const SecretVersionDiffView = ({
item,
isCollapsed = false,
onToggleCollapse,
showHeader = true,
customHeader,
excludedFieldsHighlight = ["metadata", "tags"]
}: SecretVersionDiffViewProps) => {
const oldContainerRef = useRef<HTMLDivElement>(null);
const newContainerRef = useRef<HTMLDivElement>(null);
const [internalCollapsed, setInternalCollapsed] = useState(isCollapsed);
const handleToggle = useCallback(() => {
if (onToggleCollapse && item.id) {
onToggleCollapse(item.id);
} else {
setInternalCollapsed((prev) => !prev);
}
}, [onToggleCollapse, item.id]);
const collapsed = onToggleCollapse ? isCollapsed : internalCollapsed;
if (!item.versions || item.versions.length === 0) {
return <div className="px-6 py-3 text-gray-400">No details available</div>;
}
const sortedVersions = [...item.versions].sort((a, b) => b.version - a.version);
let oldVersion = null;
let newVersion = null;
let oldVersionContent = null;
let newVersionContent = null;
let diffPaths = new Set<string>();
if (item.isUpdated && sortedVersions.length >= 2) {
if (item.isRollback) {
[oldVersion, newVersion] = sortedVersions;
} else {
[newVersion, oldVersion] = sortedVersions;
}
const cleanOldVersion = cleanVersionForComparison(oldVersion);
const cleanNewVersion = cleanVersionForComparison(newVersion);
diffPaths = getDiffPaths(cleanOldVersion, cleanNewVersion);
if (diffPaths.size === 0) {
return null;
}
oldVersionContent = (
<div className="font-mono text-sm">
{renderJsonWithDiffs(
cleanOldVersion,
diffPaths,
true,
"",
0,
undefined,
false,
excludedFieldsHighlight,
cleanOldVersion,
cleanNewVersion
)}
</div>
);
newVersionContent = (
<div className="font-mono text-sm">
{renderJsonWithDiffs(
cleanNewVersion,
diffPaths,
false,
"",
0,
undefined,
false,
excludedFieldsHighlight,
cleanOldVersion,
cleanNewVersion
)}
</div>
);
} else if (item.isAdded) {
[newVersion] = sortedVersions;
const cleanNewVersion = cleanVersionForComparison(newVersion);
newVersionContent = formatAddedJson(cleanNewVersion);
} else if (item.isDeleted) {
[oldVersion] = sortedVersions;
const cleanOldVersion = cleanVersionForComparison(oldVersion);
oldVersionContent = formatDeletedJson(cleanOldVersion);
} else {
return null;
}
const renderHeader = () => {
if (customHeader) {
return customHeader;
}
const isSecret = item.type === "secret";
const key = isSecret ? item.secretKey || "Unnamed Secret" : item.folderName || "Unnamed Folder";
let textStyle = "text-white";
let changeBadge = null;
if (item.isDeleted) {
textStyle = "line-through text-red-300";
changeBadge = (
<span className="ml-2 rounded-md bg-mineshaft-600 px-2 py-0.5 text-xs font-medium">
{isSecret ? "Secret" : "Folder"} Deleted
</span>
);
} else if (item.isAdded) {
changeBadge = (
<span className="ml-2 rounded-md bg-mineshaft-600 px-2 py-0.5 text-xs font-medium">
{isSecret ? "Secret" : "Folder"} Added
</span>
);
} else if (item.isUpdated) {
changeBadge = (
<span className="ml-2 rounded-md bg-mineshaft-600 px-2 py-0.5 text-xs font-medium">
{isSecret ? "Secret" : "Folder"} Updated
</span>
);
}
return (
<div
className="flex cursor-pointer items-center justify-between p-4 hover:bg-mineshaft-700"
onClick={handleToggle}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
handleToggle();
e.preventDefault();
}
}}
role="button"
tabIndex={0}
aria-expanded={!collapsed}
>
<div className="flex items-center">
<span className={textStyle}>{key}</span>
{changeBadge}
</div>
<FontAwesomeIcon icon={collapsed ? faChevronDown : faChevronUp} className="text-gray-400" />
</div>
);
};
return (
<div className="overflow-hidden rounded-lg border border-mineshaft-600 bg-mineshaft-800">
{showHeader && renderHeader()}
{!collapsed && (
<div className="border-t border-mineshaft-700 bg-mineshaft-900 px-6 py-4">
<div className="grid grid-cols-2 gap-4">
<div
ref={oldContainerRef}
className="thin-scrollbar max-h-96 overflow-auto whitespace-pre rounded border border-mineshaft-600 bg-mineshaft-900 p-4"
>
{oldVersionContent}
</div>
<div
ref={newContainerRef}
className="thin-scrollbar max-h-96 overflow-auto whitespace-pre rounded border border-mineshaft-600 bg-mineshaft-900 p-4"
>
{newVersionContent}
</div>
</div>
</div>
)}
</div>
);
};
@@ -1 +0,0 @@
export { SecretVersionDiffView } from "./SecretVersionDiffView";
@@ -1 +0,0 @@
export { CommitDetailsPage } from "./CommitDetailsPage";
@@ -1,76 +0,0 @@
import { createFileRoute, linkOptions, stripSearchParams } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { SecretDashboardPathBreadcrumb } from "@app/components/navigation/SecretDashboardPathBreadcrumb";
import { BreadcrumbTypes } from "@app/components/v2";
import { CommitDetailsPage } from "./CommitDetailsPage";
const CommitDetailsPageQueryParamsSchema = z.object({
secretPath: z.string().catch("/")
});
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/"
)({
component: CommitDetailsPage,
validateSearch: zodValidator(CommitDetailsPageQueryParamsSchema),
search: {
middlewares: [stripSearchParams({ secretPath: "/" })]
},
beforeLoad: ({ context, params, search }) => {
const secretPathSegments = search.secretPath.split("/").filter(Boolean);
return {
breadcrumbs: [
...context.breadcrumbs,
{
type: BreadcrumbTypes.Dropdown,
label:
context.project.environments.find((el) => el.slug === params.environment)?.name || "",
dropdownTitle: "Environments",
links: context.project.environments.map((el) => ({
label: el.name,
link: linkOptions({
to: "/secret-manager/$projectId/secrets/$envSlug",
params: {
projectId: params.projectId,
envSlug: el.slug
}
})
}))
},
...secretPathSegments.map((_, index) => ({
type: BreadcrumbTypes.Component,
component: () => (
<SecretDashboardPathBreadcrumb
secretPathSegments={secretPathSegments}
selectedPathSegmentIndex={index}
environmentSlug={params.environment}
projectId={params.projectId}
disableCopy
/>
)
})),
{
label: "Commits",
link: linkOptions({
to: "/secret-manager/$projectId/commits/$environment/$folderId",
params: {
projectId: params.projectId,
environment: params.environment,
folderId: params.folderId
},
search: {
secretPath: search.secretPath
}
})
},
{
label: params.commitId
}
]
};
}
});
@@ -1,70 +0,0 @@
import { useNavigate, useParams, useSearch } from "@tanstack/react-router";
import { ProjectPermissionCan } from "@app/components/permissions";
import { PageHeader } from "@app/components/v2";
import { ROUTE_PATHS } from "@app/const/routes";
import { useWorkspace } from "@app/context";
import {
ProjectPermissionCommitsActions,
ProjectPermissionSub
} from "@app/context/ProjectPermissionContext/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { CommitHistoryTab } from "./components/CommitHistoryTab";
export const CommitsPage = () => {
const envSlug = useParams({
from: ROUTE_PATHS.SecretManager.CommitsPage.id,
select: (el) => el.environment
});
const { currentWorkspace } = useWorkspace();
const navigate = useNavigate();
const folderId = useParams({
from: ROUTE_PATHS.SecretManager.CommitsPage.id,
select: (el) => el.folderId
});
const routerQueryParams: { secretPath?: string } = useSearch({
from: ROUTE_PATHS.SecretManager.CommitsPage.id
});
const secretPath = routerQueryParams?.secretPath || "/";
const handleSelectCommit = (commitId: string) => {
navigate({
to: `/${ProjectType.SecretManager}/$projectId/commits/$environment/$folderId/$commitId` as const,
params: {
projectId: currentWorkspace.id,
folderId,
environment: envSlug,
commitId
},
search: (query) => ({
...query,
secretPath
})
});
};
return (
<div className="mx-auto flex h-full w-full max-w-7xl justify-center bg-bunker-800 py-4 text-white">
<div className="w-full max-w-[75vw]">
<PageHeader
title="Commits"
description="Track, inspect, and restore your secrets and folders with confidence. View the complete history of changes made to your environment, examine specific modifications at each commit point, and preview the exact impact before rolling back to previous states."
/>
<ProjectPermissionCan
renderGuardBanner
I={ProjectPermissionCommitsActions.Read}
a={ProjectPermissionSub.Commits}
>
<CommitHistoryTab
onSelectCommit={handleSelectCommit}
projectId={currentWorkspace.id}
environment={envSlug}
secretPath={secretPath}
/>
</ProjectPermissionCan>
</div>
</div>
);
};
@@ -1,320 +0,0 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import {
faArrowDownWideShort,
faArrowUpWideShort,
faCopy,
faSearch
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { format, formatDistanceToNow } from "date-fns";
import { Button, Input, Spinner } from "@app/components/v2";
import { CopyButton } from "@app/components/v2/CopyButton";
import { useGetFolderCommitHistory } from "@app/hooks/api/folderCommits";
interface CommitActorMetadata {
email?: string;
name?: string;
}
interface Commit {
id: string;
message: string;
createdAt: string;
actorType: string;
actorMetadata?: CommitActorMetadata;
}
const formatTimeAgo = (timestamp: string): string => {
return formatDistanceToNow(new Date(timestamp), { addSuffix: true });
};
/**
* Commit Item component for displaying a single commit
*/
const CommitItem = ({
commit,
onSelectCommit
}: {
commit: Commit;
onSelectCommit: (commitId: string, tab: string) => void;
}) => {
return (
<div className="border-b border-zinc-800 last:border-b-0">
<div className="px-4 py-4 transition-colors duration-200 hover:bg-zinc-800">
<div className="flex flex-col sm:flex-row sm:justify-between">
<div className="w-5/6 flex-1">
<div className="flex items-center">
<Button
variant="link"
className="truncate text-left text-white hover:underline"
isFullWidth
onClick={(e) => {
e.stopPropagation();
onSelectCommit(commit.id, "tab-commit-details");
}}
>
{commit.message}
</Button>
</div>
<p className="text-white-400 mt-2 flex flex-wrap items-center gap-4 text-sm">
<span className="flex items-center text-mineshaft-300">
{commit.actorMetadata?.email || commit.actorMetadata?.name || commit.actorType}
<p className="ml-1 mr-1">committed</p>
<time dateTime={commit.createdAt}>{formatTimeAgo(commit.createdAt)}</time>
</span>
</p>
</div>
<div className="mt-2 flex w-1/6 items-center justify-end sm:mt-0">
<div className="flex items-center space-x-1">
<Button
variant="link"
className="text-white hover:underline"
onClick={(e) => {
e.stopPropagation();
onSelectCommit(commit.id, "tab-commit-details");
}}
>
<code className="text-white-400 px-3 py-1 font-mono text-sm">
{commit.id?.substring(0, 11)}
</code>
</Button>
<CopyButton
value={commit.id}
name={commit.id}
size="sm"
variant="plain"
color="text-mineshaft-400"
icon={faCopy}
/>
</div>
</div>
</div>
</div>
</div>
);
};
/**
* Date Group component for displaying commits grouped by date
*/
const DateGroup = ({
date,
commits,
onSelectCommit
}: {
date: string;
commits: Commit[];
onSelectCommit: (commitId: string, tab: string) => void;
}) => {
return (
<div className="mb-8 last:mb-0 last:pb-2">
<div className="mb-4 flex items-center">
<div className="relative mr-3 flex h-6 w-6 items-center justify-center">
<div className="z-10 h-3 w-3 rounded-full border-2 border-mineshaft-600 bg-bunker-800" />
<div className="absolute left-0 right-0 top-1/2 h-0.5 -translate-y-1/2 bg-mineshaft-600" />
</div>
<h2 className="text-sm text-white">Commits on {date}</h2>
</div>
<div className="relative">
<div className="absolute bottom-0 left-3 top-0 w-0.5 bg-mineshaft-600" />
<div className="ml-10">
{commits.map((commit) => (
<div key={commit.id} className="relative mb-3 pb-1">
<div className="overflow-hidden rounded-md border border-solid border-mineshaft-600">
<CommitItem commit={commit} onSelectCommit={onSelectCommit} />
</div>
</div>
))}
</div>
</div>
</div>
);
};
export const CommitHistoryTab = ({
onSelectCommit,
projectId,
environment,
secretPath
}: {
onSelectCommit: (commitId: string, tab: string) => void;
projectId: string;
environment: string;
secretPath: string;
}) => {
const [searchTerm, setSearchTerm] = useState("");
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState("");
const [sortDirection, setSortDirection] = useState<"asc" | "desc">("desc");
const [offset, setOffset] = useState(0);
const [allCommits, setAllCommits] = useState<Commit[]>([]);
const debounceTimeoutRef = useRef<NodeJS.Timeout>();
const limit = 5;
// Debounce search term
useEffect(() => {
if (debounceTimeoutRef.current) {
clearTimeout(debounceTimeoutRef.current);
}
debounceTimeoutRef.current = setTimeout(() => {
setDebouncedSearchTerm(searchTerm);
}, 500);
return () => {
if (debounceTimeoutRef.current) {
clearTimeout(debounceTimeoutRef.current);
}
};
}, [searchTerm]);
const {
data: response,
isLoading,
isFetching
} = useGetFolderCommitHistory({
workspaceId: projectId,
environment,
directory: secretPath,
offset,
limit,
search: debouncedSearchTerm,
sort: sortDirection
});
const commits = response?.commits || [];
const hasMore = response?.hasMore || false;
// Reset accumulated commits when search or sort changes
useEffect(() => {
setAllCommits([]);
setOffset(0);
}, [debouncedSearchTerm, sortDirection]);
// Accumulate commits instead of replacing them
useEffect(() => {
if (commits.length > 0) {
if (offset === 0) {
// First load or after search/sort change - replace all commits
setAllCommits(commits);
} else {
// Subsequent loads - append new commits
setAllCommits((prev) => [...prev, ...commits]);
}
}
}, [commits, offset]);
const groupedCommits = useMemo(() => {
return allCommits.reduce(
(acc, commit) => {
const date = format(new Date(commit.createdAt), "MMM d, yyyy");
if (!acc[date]) {
acc[date] = [];
}
acc[date].push(commit);
return acc;
},
{} as Record<string, Commit[]>
);
}, [allCommits]);
const handleSort = useCallback(() => {
setSortDirection((prev) => (prev === "desc" ? "asc" : "desc"));
}, []);
const handleSearch = useCallback((value: string) => {
setSearchTerm(value);
}, []);
const loadMoreCommits = useCallback(() => {
if (hasMore && !isFetching) {
setOffset((prev) => prev + limit);
}
}, [hasMore, isFetching, limit]);
return (
<div className="w-full">
<div className="mb-4 flex flex-col sm:flex-row sm:justify-end">
<div className="flex w-full flex-wrap items-center gap-2">
<div className="relative flex-grow">
<Input
placeholder="Search commits..."
className="h-10 w-full rounded-md border-transparent bg-zinc-800 pl-9 pr-3 text-sm text-white placeholder-gray-400 focus:border-gray-600 focus:ring-primary-500/20"
onChange={(e) => handleSearch(e.target.value)}
value={searchTerm}
aria-label="Search commits"
/>
<div className="absolute left-3 top-1/2 -translate-y-1/2 transform text-gray-400">
<FontAwesomeIcon icon={faSearch} aria-hidden="true" />
</div>
</div>
<Button
variant="outline_bg"
size="md"
className="flex h-10 items-center justify-center gap-2 rounded-md bg-zinc-800 px-4 py-2 text-sm text-white transition-colors duration-200 hover:bg-zinc-700 focus:outline-none focus:ring-2 focus:ring-primary-500"
onClick={handleSort}
aria-label={`Sort by date ${sortDirection === "desc" ? "ascending" : "descending"}`}
>
<FontAwesomeIcon
icon={sortDirection === "desc" ? faArrowDownWideShort : faArrowUpWideShort}
aria-hidden="true"
/>
</Button>
</div>
</div>
{isLoading && offset === 0 ? (
<div className="flex h-64 items-center justify-center">
<Spinner size="lg" aria-label="Loading commits" />
</div>
) : (
<div className="space-y-8">
{Object.keys(groupedCommits).length > 0 ? (
<>
{Object.entries(groupedCommits).map(([date, dateCommits]) => (
<DateGroup
key={date}
date={date}
commits={dateCommits}
onSelectCommit={onSelectCommit}
/>
))}
</>
) : (
<div className="text-white-400 flex min-h-40 flex-col items-center justify-center rounded-lg bg-zinc-900 py-8 text-center">
<FontAwesomeIcon
icon={faSearch}
className="text-white-500 mb-3 text-3xl"
aria-hidden="true"
/>
<p>No matching commits found. Try a different search term.</p>
</div>
)}
{hasMore && (
<div className="flex justify-center pb-2">
<Button
variant="outline_bg"
size="md"
className="rounded-md bg-zinc-900 px-6 py-2 text-sm font-medium text-white transition-colors duration-200 hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-primary-500"
onClick={loadMoreCommits}
disabled={isFetching}
aria-label="Load more commits"
>
{isFetching ? (
<>
<Spinner size="sm" className="mr-2" />
Loading...
</>
) : (
"Load more commits"
)}
</Button>
</div>
)}
</div>
)}
</div>
);
};
@@ -1 +0,0 @@
export { CommitHistoryTab } from "./CommitHistoryTab";
@@ -1 +0,0 @@
export { CommitsPage } from "./CommitsPage";
@@ -1,71 +0,0 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import { createFileRoute, linkOptions, stripSearchParams } from "@tanstack/react-router";
import { zodValidator } from "@tanstack/zod-adapter";
import { z } from "zod";
import { SecretDashboardPathBreadcrumb } from "@app/components/navigation/SecretDashboardPathBreadcrumb";
import { BreadcrumbTypes } from "@app/components/v2";
import { CommitsPage } from "./CommitsPage";
const CommitsPageQueryParamsSchema = z.object({
secretPath: z.string().catch("/")
});
export const Route = createFileRoute(
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/"
)({
component: CommitsPage,
validateSearch: zodValidator(CommitsPageQueryParamsSchema),
search: {
middlewares: [stripSearchParams({ secretPath: "/" })]
},
beforeLoad: ({ context, params, search }) => {
const secretPathSegments = search.secretPath.split("/").filter(Boolean);
return {
breadcrumbs: [
...context.breadcrumbs,
{
type: BreadcrumbTypes.Dropdown,
label:
context.project.environments.find((el) => el.slug === params.environment)?.name || "",
dropdownTitle: "Environments",
links: context.project.environments.map((el) => ({
label: el.name,
link: linkOptions({
to: "/secret-manager/$projectId/secrets/$envSlug",
params: {
projectId: params.projectId,
envSlug: el.slug
}
})
}))
},
...secretPathSegments.map((_, index) => ({
type: BreadcrumbTypes.Component,
component: () => (
<SecretDashboardPathBreadcrumb
secretPathSegments={secretPathSegments}
selectedPathSegmentIndex={index}
environmentSlug={params.environment}
projectId={params.projectId}
disableCopy
/>
)
})),
{
label: "Commits",
link: linkOptions({
to: "/secret-manager/$projectId/commits/$environment/$folderId",
params: {
projectId: params.projectId,
environment: params.environment,
folderId: params.folderId
}
})
}
]
};
}
});
@@ -44,7 +44,6 @@ import {
} from "@app/hooks/api";
import { useGetProjectSecretsDetails } from "@app/hooks/api/dashboard";
import { DashboardSecretsOrderBy } from "@app/hooks/api/dashboard/types";
import { useGetFolderCommitsCount } from "@app/hooks/api/folderCommits";
import { OrderByDirection } from "@app/hooks/api/generic/types";
import { ProjectType } from "@app/hooks/api/workspace/types";
import { hasSecretReadValueOrDescribePermission } from "@app/lib/fn/permission";
@@ -278,17 +277,6 @@ const Page = () => {
limit: 10
});
const {
data: { count: folderCommitsCount, folderId } = { count: 0, folderId: "" },
isPending: isFolderCommitsCountLoading,
isFetching: isFolderCommitsCountFetching
} = useGetFolderCommitsCount({
directory: secretPath,
workspaceId,
environment,
isPaused: !canDoReadRollback
});
const {
data: snapshotCount,
isPending: isSnapshotCountLoading,
@@ -300,39 +288,6 @@ const Page = () => {
isPaused: !canDoReadRollback
});
const isPITEnabled = !currentWorkspace?.showSnapshotsLegacy;
const changesCount = useMemo(() => {
return isPITEnabled ? folderCommitsCount : snapshotCount;
}, [folderCommitsCount, snapshotCount]);
const isChangesCountPending = useMemo(() => {
return isPITEnabled ? isFolderCommitsCountLoading || isSnapshotCountLoading : false;
}, [isFolderCommitsCountLoading, isSnapshotCountLoading]);
const isChangesCountFetching = useMemo(() => {
return isPITEnabled ? isFolderCommitsCountFetching || isSnapshotCountFetching : false;
}, [isFolderCommitsCountFetching, isSnapshotCountFetching]);
const handleOnClickRollbackMode = () => {
if (isPITEnabled) {
navigate({
to: `/${ProjectType.SecretManager}/$projectId/commits/$environment/$folderId` as const,
params: {
projectId: workspaceId,
folderId,
environment
},
search: (query) => ({
...query,
secretPath
})
});
} else {
handlePopUpToggle("snapshots", true);
}
};
const noAccessSecretCount = Math.max(
(page * perPage > totalCount ? totalCount % perPage : perPage) -
(imports?.length || 0) -
@@ -493,14 +448,13 @@ const Page = () => {
onVisibilityToggle={handleToggleVisibility}
onSearchChange={handleSearchChange}
onToggleTagFilter={handleTagToggle}
snapshotCount={changesCount || 0}
isSnapshotCountLoading={isChangesCountPending && isChangesCountFetching}
snapshotCount={snapshotCount || 0}
isSnapshotCountLoading={isSnapshotCountLoading && isSnapshotCountFetching}
onToggleRowType={handleToggleRowType}
onClickRollbackMode={handleOnClickRollbackMode}
onClickRollbackMode={() => handlePopUpToggle("snapshots", true)}
protectedBranchPolicyName={boardPolicy?.name}
importedBy={importedBy}
usedBySecretSyncs={usedBySecretSyncs}
isPITEnabled={isPITEnabled}
/>
<div className="thin-scrollbar mt-3 overflow-y-auto overflow-x-hidden rounded-md rounded-b-none bg-mineshaft-800 text-left text-sm text-bunker-300">
<div className="flex flex-col" id="dashboard">
@@ -664,7 +618,7 @@ const Page = () => {
secretPath={secretPath}
secrets={secrets}
folders={folders}
snapshotCount={changesCount}
snapshotCount={snapshotCount}
onGoBack={handleResetSnapshot}
onClickListSnapshot={() => handlePopUpToggle("snapshots", true)}
/>
@@ -56,10 +56,7 @@ import {
useSubscription,
useWorkspace
} from "@app/context";
import {
ProjectPermissionCommitsActions,
ProjectPermissionSecretRotationActions
} from "@app/context/ProjectPermissionContext/types";
import { ProjectPermissionSecretRotationActions } from "@app/context/ProjectPermissionContext/types";
import { usePopUp } from "@app/hooks";
import {
useCreateFolder,
@@ -126,7 +123,6 @@ type Props = {
isImported: boolean;
}[];
}[];
isPITEnabled: boolean;
};
export const ActionBar = ({
@@ -146,7 +142,6 @@ export const ActionBar = ({
onToggleRowType,
protectedBranchPolicyName,
importedBy,
isPITEnabled = false,
usedBySecretSyncs
}: Props) => {
const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([
@@ -782,8 +777,8 @@ export const ActionBar = ({
</div>
<div>
<ProjectPermissionCan
I={isPITEnabled ? ProjectPermissionCommitsActions.Read : ProjectPermissionActions.Read}
a={isPITEnabled ? ProjectPermissionSub.Commits : ProjectPermissionSub.SecretRollback}
I={ProjectPermissionActions.Read}
a={ProjectPermissionSub.SecretRollback}
>
{(isAllowed) => (
<Button
@@ -801,7 +796,7 @@ export const ActionBar = ({
className="h-10"
isDisabled={!isAllowed}
>
{`${snapshotCount} ${isPITEnabled ? "Commit" : "Snapshot"}${snapshotCount === 1 ? "" : "s"}`}
{`${snapshotCount} ${snapshotCount === 1 ? "Snapshot" : "Snapshots"}`}
</Button>
)}
</ProjectPermissionCan>
@@ -3,7 +3,6 @@ import { InfiniteData } from "@tanstack/react-query";
import { formatDistance } from "date-fns";
import { Button, Drawer, DrawerContent } from "@app/components/v2";
import { NoticeBannerV2 } from "@app/components/v2/NoticeBannerV2/NoticeBannerV2";
import { TSecretSnapshot } from "@app/hooks/api/secretSnapshots/types";
type Props = {
@@ -40,20 +39,6 @@ export const PitDrawer = ({
subTitle="Note: This will recover secrets for all environments in this project"
>
<div className="flex flex-col space-y-2">
<NoticeBannerV2 title="Snapshots are being deprecated" className="mb-2">
<p className="my-1 text-sm text-mineshaft-300">
Snapshots are being deprecated in favor of{" "}
<a
target="_blank"
href="https://infisical.com/docs/documentation/platform/pit-recovery"
rel="noopener noreferrer"
className="underline decoration-primary underline-offset-2 hover:text-mineshaft-200"
>
Commits
</a>
. This feature will be officially removed in November 2025.
</p>
</NoticeBannerV2>
{secretSnaphots?.pages?.map((group, i) => (
<Fragment key={`snapshot-item-${i + 1}`}>
{group.map(({ id, createdAt }, index) => (
@@ -9,7 +9,6 @@ import { usePopUp } from "@app/hooks";
import { useCreateSecretV3, useDeleteSecretV3, useUpdateSecretV3 } from "@app/hooks/api";
import { dashboardKeys } from "@app/hooks/api/dashboard/queries";
import { UsedBySecretSyncs } from "@app/hooks/api/dashboard/types";
import { commitKeys } from "@app/hooks/api/folderCommits/queries";
import { secretApprovalRequestKeys } from "@app/hooks/api/secretApprovalRequest/queries";
import { secretKeys } from "@app/hooks/api/secrets/queries";
import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/secrets/types";
@@ -265,12 +264,6 @@ export const SecretListView = ({
queryClient.invalidateQueries({
queryKey: secretSnapshotKeys.count({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.history({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: secretApprovalRequestKeys.count({ workspaceId })
});
@@ -322,12 +315,6 @@ export const SecretListView = ({
queryClient.invalidateQueries({
queryKey: secretSnapshotKeys.count({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.count({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: commitKeys.history({ workspaceId, environment, directory: secretPath })
});
queryClient.invalidateQueries({
queryKey: secretApprovalRequestKeys.count({ workspaceId })
});
@@ -12,7 +12,6 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions";
import { Button, ContentLoader, Input, Tag, Tooltip } from "@app/components/v2";
import { NoticeBannerV2 } from "@app/components/v2/NoticeBannerV2/NoticeBannerV2";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { useGetSnapshotSecrets, usePerformSecretRollback } from "@app/hooks/api";
import { SecretV3RawSanitized, TSecretFolder } from "@app/hooks/api/types";
@@ -61,7 +60,6 @@ export const SnapshotView = ({
const rollingFolder = snapshotData?.folders || [];
const rollingSecrets = snapshotData?.secrets || [];
const isAllowedRollback = false;
const folderDiffView = useMemo(() => {
const folderGroupById = folders.reduce<Record<string, TSecretFolder>>(
@@ -159,20 +157,6 @@ export const SnapshotView = ({
<h6 className="text-2xl">Snapshot</h6>
<Tag colorSchema="green">{new Date(snapshotData?.createdAt || "").toLocaleString()}</Tag>
</div>
<NoticeBannerV2 title="Snapshots are being deprecated" className="my-2">
<p className="my-1 text-sm text-mineshaft-300">
Snapshots are being deprecated in favor of{" "}
<a
target="_blank"
href="https://infisical.com/docs/documentation/platform/pit-recovery"
rel="noopener noreferrer"
className="underline decoration-primary underline-offset-2 hover:text-mineshaft-200"
>
Commits
</a>
. This feature will be officially removed in November 2025.
</p>
</NoticeBannerV2>
<div className="mt-4 flex items-center space-x-2">
<div className="w-2/5">
<Input
@@ -184,19 +168,17 @@ export const SnapshotView = ({
/>
</div>
<div className="flex-grow" />
{isAllowedRollback && (
<div>
<Button
variant="outline_bg"
onClick={onClickListSnapshot}
leftIcon={<FontAwesomeIcon icon={faCodeCommit} />}
className="h-10"
isDisabled={isRollingBack}
>
{snapshotCount} Commits
</Button>
</div>
)}
<div>
<Button
variant="outline_bg"
onClick={onClickListSnapshot}
leftIcon={<FontAwesomeIcon icon={faCodeCommit} />}
className="h-10"
isDisabled={isRollingBack}
>
{snapshotCount} Commits
</Button>
</div>
<div>
<Button
onClick={onGoBack}
@@ -216,7 +198,7 @@ export const SnapshotView = ({
{(isAllowed) => (
<Button
onClick={handleClickRollback}
isDisabled={isRollingBack || !isAllowed || !isAllowedRollback}
isDisabled={isRollingBack || !isAllowed}
isLoading={isRollingBack}
leftIcon={<FontAwesomeIcon icon={faUndo} />}
className="h-10"
@@ -11,7 +11,6 @@ import { EnvironmentSection } from "../EnvironmentSection";
import { PointInTimeVersionLimitSection } from "../PointInTimeVersionLimitSection";
import { RebuildSecretIndicesSection } from "../RebuildSecretIndicesSection/RebuildSecretIndicesSection";
import { SecretSharingSection } from "../SecretSharingSection";
import { SecretSnapshotsLegacySection } from "../SecretSnapshotsLegacySection";
import { SecretTagsSection } from "../SecretTagsSection";
export const ProjectGeneralTab = () => {
@@ -25,7 +24,6 @@ export const ProjectGeneralTab = () => {
{isSecretManager && <SecretTagsSection />}
{isSecretManager && <AutoCapitalizationSection />}
{isSecretManager && <SecretSharingSection />}
{isSecretManager && <SecretSnapshotsLegacySection />}
{isSecretManager && <PointInTimeVersionLimitSection />}
<AuditLogsRetentionSection />
{isSecretManager && <BackfillSecretReferenceSecretion />}
@@ -1,65 +0,0 @@
import { useState } from "react";
import { createNotification } from "@app/components/notifications";
import { ProjectPermissionCan } from "@app/components/permissions";
import { Checkbox } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { useUpdateProject } from "@app/hooks/api/workspace/queries";
export const SecretSnapshotsLegacySection = () => {
const { currentWorkspace } = useWorkspace();
const { mutateAsync: updateProject } = useUpdateProject();
const [isLoading, setIsLoading] = useState(false);
const handleToggle = async (state: boolean) => {
setIsLoading(true);
try {
if (!currentWorkspace?.id) {
setIsLoading(false);
return;
}
await updateProject({
projectID: currentWorkspace.id,
showSnapshotsLegacy: state
});
createNotification({
text: `Successfully ${state ? "enabled" : "disabled"} secret snapshots legacy for this project`,
type: "success"
});
} catch (err) {
console.error(err);
createNotification({
text: "Failed to update secret snapshots legacy for this project",
type: "error"
});
} finally {
setIsLoading(false);
}
};
return (
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<p className="mb-3 text-xl font-semibold">Show Secret Snapshots ( legacy )</p>
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Settings}>
{(isAllowed) => (
<div className="w-max">
<Checkbox
className="data-[state=checked]:bg-primary"
id="showSnapshotsLegacy"
isDisabled={!isAllowed || isLoading}
isChecked={currentWorkspace?.showSnapshotsLegacy ?? false}
onCheckedChange={(state) => handleToggle(state as boolean)}
>
This feature enables your project members to view secret snapshots in the legacy
format.
</Checkbox>
</div>
)}
</ProjectPermissionCan>
</div>
);
};
@@ -1 +0,0 @@
export { SecretSnapshotsLegacySection } from "./SecretSnapshotsLegacySection";
+1 -183
View File
@@ -221,9 +221,6 @@ import { Route as secretManagerIntegrationsGcpSecretManagerOauthCallbackPageRout
import { Route as secretManagerIntegrationsBitbucketOauthCallbackPageRouteImport } from './pages/secret-manager/integrations/BitbucketOauthCallbackPage/route'
import { Route as secretManagerIntegrationsAzureKeyVaultOauthCallbackPageRouteImport } from './pages/secret-manager/integrations/AzureKeyVaultOauthCallbackPage/route'
import { Route as secretManagerIntegrationsAzureAppConfigurationOauthCallbackPageRouteImport } from './pages/secret-manager/integrations/AzureAppConfigurationOauthCallbackPage/route'
import { Route as secretManagerCommitsPageRouteImport } from './pages/secret-manager/CommitsPage/route'
import { Route as secretManagerCommitDetailsPageComponentsRollbackPreviewTabRouteImport } from './pages/secret-manager/CommitDetailsPage/components/RollbackPreviewTab/route'
import { Route as secretManagerCommitDetailsPageRouteImport } from './pages/secret-manager/CommitDetailsPage/route'
// Create Virtual Routes
@@ -295,14 +292,6 @@ const AuthenticateInjectOrgDetailsOrgLayoutCertManagerProjectIdCertManagerLayout
createFileRoute(
'/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/certificate-templates',
)()
const AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdImport =
createFileRoute(
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId',
)()
const AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdImport =
createFileRoute(
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId',
)()
// Create/Update Routes
@@ -1382,15 +1371,6 @@ const certManagerPkiTemplateListPageRouteRoute =
AuthenticateInjectOrgDetailsOrgLayoutCertManagerProjectIdCertManagerLayoutCertificateTemplatesRoute,
} as any)
const AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRoute =
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdImport.update(
{
id: '/commits/$environment/$folderId',
path: '/commits/$environment/$folderId',
getParentRoute: () => secretManagerLayoutRoute,
} as any,
)
const secretScanningSecretScanningDataSourceByIdPageRouteRoute =
secretScanningSecretScanningDataSourceByIdPageRouteImport.update({
id: '/$type/$dataSourceId',
@@ -1925,16 +1905,6 @@ const secretManagerIntegrationsAwsParameterStoreAuthorizePageRouteRoute =
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutIntegrationsRoute,
} as any)
const AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRoute =
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdImport.update(
{
id: '/$commitId',
path: '/$commitId',
getParentRoute: () =>
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRoute,
} as any,
)
const secretManagerIntegrationsVercelOauthCallbackPageRouteRoute =
secretManagerIntegrationsVercelOauthCallbackPageRouteImport.update({
id: '/vercel/oauth2/callback',
@@ -2017,30 +1987,6 @@ const secretManagerIntegrationsAzureAppConfigurationOauthCallbackPageRouteRoute
} as any,
)
const secretManagerCommitsPageRouteRoute =
secretManagerCommitsPageRouteImport.update({
id: '/',
path: '/',
getParentRoute: () =>
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRoute,
} as any)
const secretManagerCommitDetailsPageComponentsRollbackPreviewTabRouteRoute =
secretManagerCommitDetailsPageComponentsRollbackPreviewTabRouteImport.update({
id: '/restore',
path: '/restore',
getParentRoute: () =>
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRoute,
} as any)
const secretManagerCommitDetailsPageRouteRoute =
secretManagerCommitDetailsPageRouteImport.update({
id: '/',
path: '/',
getParentRoute: () =>
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRoute,
} as any)
// Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' {
@@ -3564,20 +3510,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof secretScanningSecretScanningDataSourceByIdPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutSecretScanningProjectIdSecretScanningLayoutDataSourcesImport
}
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId': {
id: '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId'
path: '/commits/$environment/$folderId'
fullPath: '/secret-manager/$projectId/commits/$environment/$folderId'
preLoaderRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdImport
parentRoute: typeof secretManagerLayoutImport
}
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/': {
id: '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/'
path: '/'
fullPath: '/secret-manager/$projectId/commits/$environment/$folderId/'
preLoaderRoute: typeof secretManagerCommitsPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdImport
}
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/azure-app-configuration/oauth2/callback': {
id: '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/azure-app-configuration/oauth2/callback'
path: '/azure-app-configuration/oauth2/callback'
@@ -3648,27 +3580,6 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof secretManagerIntegrationsVercelOauthCallbackPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutIntegrationsImport
}
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId': {
id: '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId'
path: '/$commitId'
fullPath: '/secret-manager/$projectId/commits/$environment/$folderId/$commitId'
preLoaderRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdImport
}
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/': {
id: '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/'
path: '/'
fullPath: '/secret-manager/$projectId/commits/$environment/$folderId/$commitId/'
preLoaderRoute: typeof secretManagerCommitDetailsPageRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdImport
}
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/restore': {
id: '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/restore'
path: '/restore'
fullPath: '/secret-manager/$projectId/commits/$environment/$folderId/$commitId/restore'
preLoaderRoute: typeof secretManagerCommitDetailsPageComponentsRollbackPreviewTabRouteImport
parentRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdImport
}
}
}
@@ -4239,41 +4150,6 @@ const AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLa
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutIntegrationsRouteChildren,
)
interface AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRouteChildren {
secretManagerCommitDetailsPageRouteRoute: typeof secretManagerCommitDetailsPageRouteRoute
secretManagerCommitDetailsPageComponentsRollbackPreviewTabRouteRoute: typeof secretManagerCommitDetailsPageComponentsRollbackPreviewTabRouteRoute
}
const AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRouteChildren: AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRouteChildren =
{
secretManagerCommitDetailsPageRouteRoute:
secretManagerCommitDetailsPageRouteRoute,
secretManagerCommitDetailsPageComponentsRollbackPreviewTabRouteRoute:
secretManagerCommitDetailsPageComponentsRollbackPreviewTabRouteRoute,
}
const AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRouteWithChildren =
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRoute._addFileChildren(
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRouteChildren,
)
interface AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRouteChildren {
secretManagerCommitsPageRouteRoute: typeof secretManagerCommitsPageRouteRoute
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRouteWithChildren
}
const AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRouteChildren: AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRouteChildren =
{
secretManagerCommitsPageRouteRoute: secretManagerCommitsPageRouteRoute,
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRoute:
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRouteWithChildren,
}
const AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRouteWithChildren =
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRoute._addFileChildren(
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRouteChildren,
)
interface secretManagerLayoutRouteChildren {
secretManagerIPAllowlistPageRouteRoute: typeof secretManagerIPAllowlistPageRouteRoute
secretManagerSecretApprovalsPageRouteRoute: typeof secretManagerSecretApprovalsPageRouteRoute
@@ -4287,7 +4163,6 @@ interface secretManagerLayoutRouteChildren {
projectIdentityDetailsByIDPageRouteSecretManagerRoute: typeof projectIdentityDetailsByIDPageRouteSecretManagerRoute
projectMemberDetailsByIDPageRouteSecretManagerRoute: typeof projectMemberDetailsByIDPageRouteSecretManagerRoute
projectRoleDetailsBySlugPageRouteSecretManagerRoute: typeof projectRoleDetailsBySlugPageRouteSecretManagerRoute
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRoute: typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRouteWithChildren
}
const secretManagerLayoutRouteChildren: secretManagerLayoutRouteChildren = {
@@ -4313,8 +4188,6 @@ const secretManagerLayoutRouteChildren: secretManagerLayoutRouteChildren = {
projectMemberDetailsByIDPageRouteSecretManagerRoute,
projectRoleDetailsBySlugPageRouteSecretManagerRoute:
projectRoleDetailsBySlugPageRouteSecretManagerRoute,
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRoute:
AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRouteWithChildren,
}
const secretManagerLayoutRouteWithChildren =
@@ -4865,8 +4738,6 @@ export interface FileRoutesByFullPath {
'/secret-manager/$projectId/integrations/windmill/authorize': typeof secretManagerIntegrationsWindmillAuthorizePageRouteRoute
'/secret-manager/$projectId/integrations/windmill/create': typeof secretManagerIntegrationsWindmillConfigurePageRouteRoute
'/secret-scanning/$projectId/data-sources/$type/$dataSourceId': typeof secretScanningSecretScanningDataSourceByIdPageRouteRoute
'/secret-manager/$projectId/commits/$environment/$folderId': typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRouteWithChildren
'/secret-manager/$projectId/commits/$environment/$folderId/': typeof secretManagerCommitsPageRouteRoute
'/secret-manager/$projectId/integrations/azure-app-configuration/oauth2/callback': typeof secretManagerIntegrationsAzureAppConfigurationOauthCallbackPageRouteRoute
'/secret-manager/$projectId/integrations/azure-key-vault/oauth2/callback': typeof secretManagerIntegrationsAzureKeyVaultOauthCallbackPageRouteRoute
'/secret-manager/$projectId/integrations/bitbucket/oauth2/callback': typeof secretManagerIntegrationsBitbucketOauthCallbackPageRouteRoute
@@ -4877,9 +4748,6 @@ export interface FileRoutesByFullPath {
'/secret-manager/$projectId/integrations/netlify/oauth2/callback': typeof secretManagerIntegrationsNetlifyOauthCallbackPageRouteRoute
'/secret-manager/$projectId/integrations/secret-syncs/$destination/$syncId': typeof secretManagerSecretSyncDetailsByIDPageRouteRoute
'/secret-manager/$projectId/integrations/vercel/oauth2/callback': typeof secretManagerIntegrationsVercelOauthCallbackPageRouteRoute
'/secret-manager/$projectId/commits/$environment/$folderId/$commitId': typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRouteWithChildren
'/secret-manager/$projectId/commits/$environment/$folderId/$commitId/': typeof secretManagerCommitDetailsPageRouteRoute
'/secret-manager/$projectId/commits/$environment/$folderId/$commitId/restore': typeof secretManagerCommitDetailsPageComponentsRollbackPreviewTabRouteRoute
}
export interface FileRoutesByTo {
@@ -5078,7 +4946,6 @@ export interface FileRoutesByTo {
'/secret-manager/$projectId/integrations/windmill/authorize': typeof secretManagerIntegrationsWindmillAuthorizePageRouteRoute
'/secret-manager/$projectId/integrations/windmill/create': typeof secretManagerIntegrationsWindmillConfigurePageRouteRoute
'/secret-scanning/$projectId/data-sources/$type/$dataSourceId': typeof secretScanningSecretScanningDataSourceByIdPageRouteRoute
'/secret-manager/$projectId/commits/$environment/$folderId': typeof secretManagerCommitsPageRouteRoute
'/secret-manager/$projectId/integrations/azure-app-configuration/oauth2/callback': typeof secretManagerIntegrationsAzureAppConfigurationOauthCallbackPageRouteRoute
'/secret-manager/$projectId/integrations/azure-key-vault/oauth2/callback': typeof secretManagerIntegrationsAzureKeyVaultOauthCallbackPageRouteRoute
'/secret-manager/$projectId/integrations/bitbucket/oauth2/callback': typeof secretManagerIntegrationsBitbucketOauthCallbackPageRouteRoute
@@ -5089,8 +4956,6 @@ export interface FileRoutesByTo {
'/secret-manager/$projectId/integrations/netlify/oauth2/callback': typeof secretManagerIntegrationsNetlifyOauthCallbackPageRouteRoute
'/secret-manager/$projectId/integrations/secret-syncs/$destination/$syncId': typeof secretManagerSecretSyncDetailsByIDPageRouteRoute
'/secret-manager/$projectId/integrations/vercel/oauth2/callback': typeof secretManagerIntegrationsVercelOauthCallbackPageRouteRoute
'/secret-manager/$projectId/commits/$environment/$folderId/$commitId': typeof secretManagerCommitDetailsPageRouteRoute
'/secret-manager/$projectId/commits/$environment/$folderId/$commitId/restore': typeof secretManagerCommitDetailsPageComponentsRollbackPreviewTabRouteRoute
}
export interface FileRoutesById {
@@ -5312,8 +5177,6 @@ export interface FileRoutesById {
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/windmill/authorize': typeof secretManagerIntegrationsWindmillAuthorizePageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/windmill/create': typeof secretManagerIntegrationsWindmillConfigurePageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/secret-scanning/$projectId/_secret-scanning-layout/data-sources/$type/$dataSourceId': typeof secretScanningSecretScanningDataSourceByIdPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId': typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdRouteWithChildren
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/': typeof secretManagerCommitsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/azure-app-configuration/oauth2/callback': typeof secretManagerIntegrationsAzureAppConfigurationOauthCallbackPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/oauth2/callback': typeof secretManagerIntegrationsAzureKeyVaultOauthCallbackPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/bitbucket/oauth2/callback': typeof secretManagerIntegrationsBitbucketOauthCallbackPageRouteRoute
@@ -5324,9 +5187,6 @@ export interface FileRoutesById {
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/netlify/oauth2/callback': typeof secretManagerIntegrationsNetlifyOauthCallbackPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/secret-syncs/$destination/$syncId': typeof secretManagerSecretSyncDetailsByIDPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/vercel/oauth2/callback': typeof secretManagerIntegrationsVercelOauthCallbackPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId': typeof AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdSecretManagerLayoutCommitsEnvironmentFolderIdCommitIdRouteWithChildren
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/': typeof secretManagerCommitDetailsPageRouteRoute
'/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/restore': typeof secretManagerCommitDetailsPageComponentsRollbackPreviewTabRouteRoute
}
export interface FileRouteTypes {
@@ -5539,8 +5399,6 @@ export interface FileRouteTypes {
| '/secret-manager/$projectId/integrations/windmill/authorize'
| '/secret-manager/$projectId/integrations/windmill/create'
| '/secret-scanning/$projectId/data-sources/$type/$dataSourceId'
| '/secret-manager/$projectId/commits/$environment/$folderId'
| '/secret-manager/$projectId/commits/$environment/$folderId/'
| '/secret-manager/$projectId/integrations/azure-app-configuration/oauth2/callback'
| '/secret-manager/$projectId/integrations/azure-key-vault/oauth2/callback'
| '/secret-manager/$projectId/integrations/bitbucket/oauth2/callback'
@@ -5551,9 +5409,6 @@ export interface FileRouteTypes {
| '/secret-manager/$projectId/integrations/netlify/oauth2/callback'
| '/secret-manager/$projectId/integrations/secret-syncs/$destination/$syncId'
| '/secret-manager/$projectId/integrations/vercel/oauth2/callback'
| '/secret-manager/$projectId/commits/$environment/$folderId/$commitId'
| '/secret-manager/$projectId/commits/$environment/$folderId/$commitId/'
| '/secret-manager/$projectId/commits/$environment/$folderId/$commitId/restore'
fileRoutesByTo: FileRoutesByTo
to:
| '/'
@@ -5751,7 +5606,6 @@ export interface FileRouteTypes {
| '/secret-manager/$projectId/integrations/windmill/authorize'
| '/secret-manager/$projectId/integrations/windmill/create'
| '/secret-scanning/$projectId/data-sources/$type/$dataSourceId'
| '/secret-manager/$projectId/commits/$environment/$folderId'
| '/secret-manager/$projectId/integrations/azure-app-configuration/oauth2/callback'
| '/secret-manager/$projectId/integrations/azure-key-vault/oauth2/callback'
| '/secret-manager/$projectId/integrations/bitbucket/oauth2/callback'
@@ -5762,8 +5616,6 @@ export interface FileRouteTypes {
| '/secret-manager/$projectId/integrations/netlify/oauth2/callback'
| '/secret-manager/$projectId/integrations/secret-syncs/$destination/$syncId'
| '/secret-manager/$projectId/integrations/vercel/oauth2/callback'
| '/secret-manager/$projectId/commits/$environment/$folderId/$commitId'
| '/secret-manager/$projectId/commits/$environment/$folderId/$commitId/restore'
id:
| '__root__'
| '/'
@@ -5983,8 +5835,6 @@ export interface FileRouteTypes {
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/windmill/authorize'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/windmill/create'
| '/_authenticate/_inject-org-details/_org-layout/secret-scanning/$projectId/_secret-scanning-layout/data-sources/$type/$dataSourceId'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/azure-app-configuration/oauth2/callback'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/azure-key-vault/oauth2/callback'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/bitbucket/oauth2/callback'
@@ -5995,9 +5845,6 @@ export interface FileRouteTypes {
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/netlify/oauth2/callback'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/secret-syncs/$destination/$syncId'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/vercel/oauth2/callback'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/'
| '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/restore'
fileRoutesById: FileRoutesById
}
@@ -6515,8 +6362,7 @@ export const routeTree = rootRoute
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/groups/$groupId",
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/identities/$identityId",
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/members/$membershipId",
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/roles/$roleSlug",
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId"
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/roles/$roleSlug"
]
},
"/_authenticate/_inject-org-details/_org-layout/secret-scanning/$projectId/_secret-scanning-layout": {
@@ -7185,18 +7031,6 @@ export const routeTree = rootRoute
"filePath": "secret-scanning/SecretScanningDataSourceByIdPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/secret-scanning/$projectId/_secret-scanning-layout/data-sources"
},
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId": {
"filePath": "",
"parent": "/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout",
"children": [
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/",
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId"
]
},
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/": {
"filePath": "secret-manager/CommitsPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId"
},
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/azure-app-configuration/oauth2/callback": {
"filePath": "secret-manager/integrations/AzureAppConfigurationOauthCallbackPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations"
@@ -7236,22 +7070,6 @@ export const routeTree = rootRoute
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations/vercel/oauth2/callback": {
"filePath": "secret-manager/integrations/VercelOauthCallbackPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/integrations"
},
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId": {
"filePath": "",
"parent": "/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId",
"children": [
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/",
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/restore"
]
},
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/": {
"filePath": "secret-manager/CommitDetailsPage/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId"
},
"/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId/restore": {
"filePath": "secret-manager/CommitDetailsPage/components/RollbackPreviewTab/route.tsx",
"parent": "/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/commits/$environment/$folderId/$commitId"
}
}
}
-10
View File
@@ -63,16 +63,6 @@ const secretManagerRoutes = route("/secret-manager/$projectId", [
route("/approval", "secret-manager/SecretApprovalsPage/route.tsx"),
route("/secret-rotation", "secret-manager/SecretRotationPage/route.tsx"),
route("/settings", "secret-manager/SettingsPage/route.tsx"),
route("/commits/$environment/$folderId", [
index("secret-manager/CommitsPage/route.tsx"),
route("/$commitId", [
index("secret-manager/CommitDetailsPage/route.tsx"),
route(
"/restore",
"secret-manager/CommitDetailsPage/components/RollbackPreviewTab/route.tsx"
)
])
]),
route("/integrations", [
index("secret-manager/IntegrationsListPage/route.tsx"),
route("/$integrationId", "secret-manager/IntegrationsDetailsByIDPage/route.tsx"),