mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 08:29:55 +00:00
Revert "Revert "Revert "feat(PIT): Point In Time Revamp"""
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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 })
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
+1
-37
@@ -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>
|
||||
);
|
||||
};
|
||||
-362
@@ -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
@@ -1 +0,0 @@
|
||||
export { CommitDetailsTab } from "./CommitDetailsTab";
|
||||
-14
@@ -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;
|
||||
}
|
||||
-389
@@ -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
@@ -1 +0,0 @@
|
||||
export { RollbackPreviewTab } from "./RollbackPreviewTab";
|
||||
-91
@@ -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"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
||||
-652
@@ -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
@@ -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>
|
||||
);
|
||||
};
|
||||
-320
@@ -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)}
|
||||
/>
|
||||
|
||||
+4
-9
@@ -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>
|
||||
|
||||
-15
@@ -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) => (
|
||||
|
||||
-13
@@ -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
-30
@@ -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"
|
||||
|
||||
-2
@@ -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 />}
|
||||
|
||||
-65
@@ -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
@@ -1 +0,0 @@
|
||||
export { SecretSnapshotsLegacySection } from "./SecretSnapshotsLegacySection";
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
|
||||
Reference in New Issue
Block a user