mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-11 12:29:26 +00:00
feat(secrets-ui): add secret syncs to referenced secret warning
This commit is contained in:
@@ -166,6 +166,14 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.optional(),
|
.optional(),
|
||||||
|
usedBySecretSyncs: z
|
||||||
|
.object({
|
||||||
|
name: z.string(),
|
||||||
|
destination: z.string(),
|
||||||
|
environment: z.string()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.optional(),
|
||||||
totalFolderCount: z.number().optional(),
|
totalFolderCount: z.number().optional(),
|
||||||
totalDynamicSecretCount: z.number().optional(),
|
totalDynamicSecretCount: z.number().optional(),
|
||||||
totalSecretCount: z.number().optional(),
|
totalSecretCount: z.number().optional(),
|
||||||
@@ -500,6 +508,21 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const usedBySecretSyncs: { name: string; destination: string; environment: string }[] = [];
|
||||||
|
for await (const environment of environments) {
|
||||||
|
const secretSyncs = await server.services.secretSync.listSecretSyncsBySecretPath(
|
||||||
|
{ projectId, secretPath, environment },
|
||||||
|
req.permission
|
||||||
|
);
|
||||||
|
secretSyncs.forEach((sync) => {
|
||||||
|
usedBySecretSyncs.push({
|
||||||
|
name: sync.name,
|
||||||
|
destination: sync.destination,
|
||||||
|
environment
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
folders,
|
folders,
|
||||||
dynamicSecrets,
|
dynamicSecrets,
|
||||||
@@ -512,6 +535,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
totalSecretCount,
|
totalSecretCount,
|
||||||
totalSecretRotationCount,
|
totalSecretRotationCount,
|
||||||
importedByEnvs,
|
importedByEnvs,
|
||||||
|
usedBySecretSyncs,
|
||||||
totalCount:
|
totalCount:
|
||||||
(totalFolderCount ?? 0) +
|
(totalFolderCount ?? 0) +
|
||||||
(totalDynamicSecretCount ?? 0) +
|
(totalDynamicSecretCount ?? 0) +
|
||||||
@@ -605,6 +629,14 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
totalFolderCount: z.number().optional(),
|
totalFolderCount: z.number().optional(),
|
||||||
totalDynamicSecretCount: z.number().optional(),
|
totalDynamicSecretCount: z.number().optional(),
|
||||||
totalSecretCount: z.number().optional(),
|
totalSecretCount: z.number().optional(),
|
||||||
|
usedBySecretSyncs: z
|
||||||
|
.object({
|
||||||
|
name: z.string(),
|
||||||
|
destination: z.string(),
|
||||||
|
environment: z.string()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.optional(),
|
||||||
importedBy: z
|
importedBy: z
|
||||||
.object({
|
.object({
|
||||||
environment: z.object({
|
environment: z.object({
|
||||||
@@ -898,6 +930,16 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
secrets
|
secrets
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const secretSyncs = await server.services.secretSync.listSecretSyncsBySecretPath(
|
||||||
|
{ projectId, secretPath, environment },
|
||||||
|
req.permission
|
||||||
|
);
|
||||||
|
const usedBySecretSyncs = secretSyncs.map((sync) => ({
|
||||||
|
name: sync.name,
|
||||||
|
destination: sync.destination,
|
||||||
|
environment: environment
|
||||||
|
}));
|
||||||
|
|
||||||
if (secrets?.length || secretRotations?.length) {
|
if (secrets?.length || secretRotations?.length) {
|
||||||
const secretCount =
|
const secretCount =
|
||||||
(secrets?.length ?? 0) +
|
(secrets?.length ?? 0) +
|
||||||
@@ -944,6 +986,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
totalSecretCount,
|
totalSecretCount,
|
||||||
totalSecretRotationCount,
|
totalSecretRotationCount,
|
||||||
importedBy,
|
importedBy,
|
||||||
|
usedBySecretSyncs,
|
||||||
totalCount:
|
totalCount:
|
||||||
(totalImportCount ?? 0) +
|
(totalImportCount ?? 0) +
|
||||||
(totalFolderCount ?? 0) +
|
(totalFolderCount ?? 0) +
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import {
|
|||||||
TDeleteSecretSyncDTO,
|
TDeleteSecretSyncDTO,
|
||||||
TFindSecretSyncByIdDTO,
|
TFindSecretSyncByIdDTO,
|
||||||
TFindSecretSyncByNameDTO,
|
TFindSecretSyncByNameDTO,
|
||||||
|
TListSecretSyncsByFolderId,
|
||||||
TListSecretSyncsByProjectId,
|
TListSecretSyncsByProjectId,
|
||||||
TSecretSync,
|
TSecretSync,
|
||||||
TTriggerSecretSyncImportSecretsByIdDTO,
|
TTriggerSecretSyncImportSecretsByIdDTO,
|
||||||
@@ -85,6 +86,34 @@ export const secretSyncServiceFactory = ({
|
|||||||
return secretSyncs as TSecretSync[];
|
return secretSyncs as TSecretSync[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const listSecretSyncsBySecretPath = async (
|
||||||
|
{ projectId, secretPath, environment, destination }: TListSecretSyncsByFolderId,
|
||||||
|
actor: OrgServiceActor
|
||||||
|
) => {
|
||||||
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
|
actor: actor.type,
|
||||||
|
actorId: actor.id,
|
||||||
|
actorAuthMethod: actor.authMethod,
|
||||||
|
actorOrgId: actor.orgId,
|
||||||
|
actionProjectType: ActionProjectType.SecretManager,
|
||||||
|
projectId
|
||||||
|
});
|
||||||
|
|
||||||
|
if (permission.cannot(ProjectPermissionSecretSyncActions.Read, ProjectPermissionSub.SecretSyncs)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
|
||||||
|
if (!folder) return [];
|
||||||
|
|
||||||
|
const secretSyncs = await secretSyncDAL.find({
|
||||||
|
...(destination && { destination }),
|
||||||
|
folderId: folder.id
|
||||||
|
});
|
||||||
|
|
||||||
|
return secretSyncs as TSecretSync[];
|
||||||
|
};
|
||||||
|
|
||||||
const findSecretSyncById = async ({ destination, syncId }: TFindSecretSyncByIdDTO, actor: OrgServiceActor) => {
|
const findSecretSyncById = async ({ destination, syncId }: TFindSecretSyncByIdDTO, actor: OrgServiceActor) => {
|
||||||
const secretSync = await secretSyncDAL.findById(syncId);
|
const secretSync = await secretSyncDAL.findById(syncId);
|
||||||
|
|
||||||
@@ -518,6 +547,7 @@ export const secretSyncServiceFactory = ({
|
|||||||
return {
|
return {
|
||||||
listSecretSyncOptions,
|
listSecretSyncOptions,
|
||||||
listSecretSyncsByProjectId,
|
listSecretSyncsByProjectId,
|
||||||
|
listSecretSyncsBySecretPath,
|
||||||
findSecretSyncById,
|
findSecretSyncById,
|
||||||
findSecretSyncByName,
|
findSecretSyncByName,
|
||||||
createSecretSync,
|
createSecretSync,
|
||||||
|
|||||||
@@ -134,6 +134,13 @@ export type TListSecretSyncsByProjectId = {
|
|||||||
destination?: SecretSync;
|
destination?: SecretSync;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TListSecretSyncsByFolderId = {
|
||||||
|
projectId: string;
|
||||||
|
secretPath: string;
|
||||||
|
environment: string;
|
||||||
|
destination?: SecretSync;
|
||||||
|
};
|
||||||
|
|
||||||
export type TFindSecretSyncByIdDTO = {
|
export type TFindSecretSyncByIdDTO = {
|
||||||
syncId: string;
|
syncId: string;
|
||||||
destination: SecretSync;
|
destination: SecretSync;
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export type DashboardProjectSecretsOverviewResponse = {
|
|||||||
totalUniqueFoldersInPage: number;
|
totalUniqueFoldersInPage: number;
|
||||||
totalUniqueSecretImportsInPage: number;
|
totalUniqueSecretImportsInPage: number;
|
||||||
importedByEnvs?: { environment: string; importedBy: ProjectSecretsImportedBy[] }[];
|
importedByEnvs?: { environment: string; importedBy: ProjectSecretsImportedBy[] }[];
|
||||||
|
usedBySecretSyncs?: { name: string; destination: string; environment: string }[];
|
||||||
totalUniqueSecretRotationsInPage: number;
|
totalUniqueSecretRotationsInPage: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,6 +44,7 @@ export type DashboardProjectSecretsDetailsResponse = {
|
|||||||
totalSecretRotationCount?: number;
|
totalSecretRotationCount?: number;
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
importedBy?: ProjectSecretsImportedBy[];
|
importedBy?: ProjectSecretsImportedBy[];
|
||||||
|
usedBySecretSyncs?: { name: string; destination: string; environment: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProjectSecretsImportedBy = {
|
export type ProjectSecretsImportedBy = {
|
||||||
|
|||||||
@@ -274,7 +274,8 @@ export const OverviewPage = () => {
|
|||||||
totalUniqueSecretImportsInPage,
|
totalUniqueSecretImportsInPage,
|
||||||
totalUniqueDynamicSecretsInPage,
|
totalUniqueDynamicSecretsInPage,
|
||||||
totalUniqueSecretRotationsInPage,
|
totalUniqueSecretRotationsInPage,
|
||||||
importedByEnvs
|
importedByEnvs,
|
||||||
|
usedBySecretSyncs
|
||||||
} = overview ?? {};
|
} = overview ?? {};
|
||||||
|
|
||||||
const secretImportsShaped = secretImports
|
const secretImportsShaped = secretImports
|
||||||
@@ -1045,6 +1046,7 @@ export const OverviewPage = () => {
|
|||||||
selectedEntries={selectedEntries}
|
selectedEntries={selectedEntries}
|
||||||
resetSelectedEntries={resetSelectedEntries}
|
resetSelectedEntries={resetSelectedEntries}
|
||||||
importedByEnvs={importedByEnvs}
|
importedByEnvs={importedByEnvs}
|
||||||
|
usedBySecretSyncs={usedBySecretSyncs}
|
||||||
/>
|
/>
|
||||||
<div className="thin-scrollbar mt-4">
|
<div className="thin-scrollbar mt-4">
|
||||||
<TableContainer
|
<TableContainer
|
||||||
|
|||||||
+10
-1
@@ -38,13 +38,15 @@ type Props = {
|
|||||||
[EntryType.SECRET]: Record<string, Record<string, SecretV3RawSanitized>>;
|
[EntryType.SECRET]: Record<string, Record<string, SecretV3RawSanitized>>;
|
||||||
};
|
};
|
||||||
importedByEnvs?: { environment: string; importedBy: ProjectSecretsImportedBy[] }[];
|
importedByEnvs?: { environment: string; importedBy: ProjectSecretsImportedBy[] }[];
|
||||||
|
usedBySecretSyncs?: { name: string; destination: string; environment: string }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SelectionPanel = ({
|
export const SelectionPanel = ({
|
||||||
secretPath,
|
secretPath,
|
||||||
resetSelectedEntries,
|
resetSelectedEntries,
|
||||||
selectedEntries,
|
selectedEntries,
|
||||||
importedByEnvs
|
importedByEnvs,
|
||||||
|
usedBySecretSyncs
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { permission } = useProjectPermission();
|
const { permission } = useProjectPermission();
|
||||||
|
|
||||||
@@ -156,6 +158,12 @@ export const SelectionPanel = ({
|
|||||||
);
|
);
|
||||||
}, [importedByEnvs, selectedEntries, selectedKeysCount]);
|
}, [importedByEnvs, selectedEntries, selectedKeysCount]);
|
||||||
|
|
||||||
|
const usedBySecretSyncsFiltered = useMemo(() => {
|
||||||
|
if (selectedKeysCount === 0 || !usedBySecretSyncs) return null;
|
||||||
|
const envs = Object.values(selectedEntries.secret).flatMap((entries) => Object.keys(entries));
|
||||||
|
return usedBySecretSyncs.filter((syncItem) => envs.includes(syncItem.environment));
|
||||||
|
}, [selectedEntries, usedBySecretSyncs, selectedKeysCount]);
|
||||||
|
|
||||||
const getDeleteModalTitle = () => {
|
const getDeleteModalTitle = () => {
|
||||||
if (selectedFolderCount > 0 && selectedKeysCount > 0) {
|
if (selectedFolderCount > 0 && selectedKeysCount > 0) {
|
||||||
return "Do you want to delete the selected secrets and folders across environments?";
|
return "Do you want to delete the selected secrets and folders across environments?";
|
||||||
@@ -331,6 +339,7 @@ export const SelectionPanel = ({
|
|||||||
<CollapsibleSecretImports
|
<CollapsibleSecretImports
|
||||||
importedBy={importedBy}
|
importedBy={importedBy}
|
||||||
secretsToDelete={secretsToDeleteKeys}
|
secretsToDelete={secretsToDeleteKeys}
|
||||||
|
usedBySecretSyncs={usedBySecretSyncsFiltered}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -220,6 +220,7 @@ const Page = () => {
|
|||||||
totalSecretCount = 0,
|
totalSecretCount = 0,
|
||||||
totalCount = 0,
|
totalCount = 0,
|
||||||
importedBy,
|
importedBy,
|
||||||
|
usedBySecretSyncs,
|
||||||
totalSecretRotationCount = 0
|
totalSecretRotationCount = 0
|
||||||
} = data ?? {};
|
} = data ?? {};
|
||||||
|
|
||||||
@@ -441,6 +442,7 @@ const Page = () => {
|
|||||||
onClickRollbackMode={() => handlePopUpToggle("snapshots", true)}
|
onClickRollbackMode={() => handlePopUpToggle("snapshots", true)}
|
||||||
protectedBranchPolicyName={boardPolicy?.name}
|
protectedBranchPolicyName={boardPolicy?.name}
|
||||||
importedBy={importedBy}
|
importedBy={importedBy}
|
||||||
|
usedBySecretSyncs={usedBySecretSyncs}
|
||||||
/>
|
/>
|
||||||
<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="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">
|
<div className="flex flex-col" id="dashboard">
|
||||||
@@ -530,6 +532,7 @@ const Page = () => {
|
|||||||
secretPath={secretPath}
|
secretPath={secretPath}
|
||||||
isProtectedBranch={isProtectedBranch}
|
isProtectedBranch={isProtectedBranch}
|
||||||
importedBy={importedBy}
|
importedBy={importedBy}
|
||||||
|
usedBySecretSyncs={usedBySecretSyncs}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{noAccessSecretCount > 0 && <SecretNoAccessListView count={noAccessSecretCount} />}
|
{noAccessSecretCount > 0 && <SecretNoAccessListView count={noAccessSecretCount} />}
|
||||||
|
|||||||
+4
-1
@@ -113,6 +113,7 @@ type Props = {
|
|||||||
onVisibilityToggle: () => void;
|
onVisibilityToggle: () => void;
|
||||||
onToggleRowType: (rowType: RowType) => void;
|
onToggleRowType: (rowType: RowType) => void;
|
||||||
onClickRollbackMode: () => void;
|
onClickRollbackMode: () => void;
|
||||||
|
usedBySecretSyncs?: { environment: string; name: string; destination: string }[];
|
||||||
importedBy?: {
|
importedBy?: {
|
||||||
environment: { name: string; slug: string };
|
environment: { name: string; slug: string };
|
||||||
folders: {
|
folders: {
|
||||||
@@ -139,7 +140,8 @@ export const ActionBar = ({
|
|||||||
onClickRollbackMode,
|
onClickRollbackMode,
|
||||||
onToggleRowType,
|
onToggleRowType,
|
||||||
protectedBranchPolicyName,
|
protectedBranchPolicyName,
|
||||||
importedBy
|
importedBy,
|
||||||
|
usedBySecretSyncs
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([
|
const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([
|
||||||
"addFolder",
|
"addFolder",
|
||||||
@@ -1076,6 +1078,7 @@ export const ActionBar = ({
|
|||||||
<CollapsibleSecretImports
|
<CollapsibleSecretImports
|
||||||
importedBy={importedBy}
|
importedBy={importedBy}
|
||||||
secretsToDelete={Object.values(selectedSecrets).map((s) => s.key)}
|
secretsToDelete={Object.values(selectedSecrets).map((s) => s.key)}
|
||||||
|
usedBySecretSyncs={usedBySecretSyncs}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+60
-7
@@ -1,5 +1,6 @@
|
|||||||
|
/* eslint-disable no-nested-ternary */
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { faFileImport, faKey, faWarning } from "@fortawesome/free-solid-svg-icons";
|
import { faFileImport, faKey, faSync, faWarning } from "@fortawesome/free-solid-svg-icons";
|
||||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||||
|
|
||||||
import { Table, TBody, Td, Th, THead, Tr } from "@app/components/v2";
|
import { Table, TBody, Td, Th, THead, Tr } from "@app/components/v2";
|
||||||
@@ -7,7 +8,8 @@ import { useWorkspace } from "@app/context";
|
|||||||
|
|
||||||
enum ItemType {
|
enum ItemType {
|
||||||
Folder = "Folder",
|
Folder = "Folder",
|
||||||
Secret = "Secret"
|
Secret = "Secret",
|
||||||
|
SecretSync = "SecretSync"
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FlatItem {
|
interface FlatItem {
|
||||||
@@ -28,11 +30,19 @@ interface CollapsibleSecretImportsProps {
|
|||||||
isImported: boolean;
|
isImported: boolean;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
|
usedBySecretSyncs?:
|
||||||
|
| {
|
||||||
|
name: string;
|
||||||
|
destination: string;
|
||||||
|
environment: string;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
secretsToDelete: string[];
|
secretsToDelete: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CollapsibleSecretImports: React.FC<CollapsibleSecretImportsProps> = ({
|
export const CollapsibleSecretImports: React.FC<CollapsibleSecretImportsProps> = ({
|
||||||
importedBy = [],
|
importedBy = [],
|
||||||
|
usedBySecretSyncs = [],
|
||||||
secretsToDelete
|
secretsToDelete
|
||||||
}) => {
|
}) => {
|
||||||
const { currentWorkspace } = useWorkspace();
|
const { currentWorkspace } = useWorkspace();
|
||||||
@@ -51,6 +61,14 @@ export const CollapsibleSecretImports: React.FC<CollapsibleSecretImportsProps> =
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handlePathClick = (item: FlatItem) => {
|
const handlePathClick = (item: FlatItem) => {
|
||||||
|
if (item.type === ItemType.SecretSync) {
|
||||||
|
window.open(
|
||||||
|
`/secret-manager/${currentWorkspace.id}/integrations?selectedTab=secret-syncs`,
|
||||||
|
"_blank"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let pathToNavigate;
|
let pathToNavigate;
|
||||||
if (item.type === ItemType.Folder) {
|
if (item.type === ItemType.Folder) {
|
||||||
pathToNavigate = item.path;
|
pathToNavigate = item.path;
|
||||||
@@ -103,7 +121,24 @@ export const CollapsibleSecretImports: React.FC<CollapsibleSecretImportsProps> =
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add secret sync items
|
||||||
|
usedBySecretSyncs?.forEach((syncItem) => {
|
||||||
|
items.push({
|
||||||
|
type: ItemType.SecretSync,
|
||||||
|
path: syncItem.destination,
|
||||||
|
id: `secret-sync-${syncItem.name}-${syncItem.destination}`,
|
||||||
|
reference: "Secret Sync",
|
||||||
|
environment: { name: syncItem.environment, slug: "" }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return items.sort((a, b) => {
|
return items.sort((a, b) => {
|
||||||
|
if (a.type === ItemType.SecretSync && b.type !== ItemType.SecretSync) return 1;
|
||||||
|
if (a.type !== ItemType.SecretSync && b.type === ItemType.SecretSync) return -1;
|
||||||
|
|
||||||
|
if (a.type === ItemType.SecretSync && b.type === ItemType.SecretSync) {
|
||||||
|
return a.path.localeCompare(b.path);
|
||||||
|
}
|
||||||
const envCompare = a.environment.name.localeCompare(b.environment.name);
|
const envCompare = a.environment.name.localeCompare(b.environment.name);
|
||||||
if (envCompare !== 0) return envCompare;
|
if (envCompare !== 0) return envCompare;
|
||||||
|
|
||||||
@@ -119,7 +154,7 @@ export const CollapsibleSecretImports: React.FC<CollapsibleSecretImportsProps> =
|
|||||||
|
|
||||||
return aPath.localeCompare(bPath);
|
return aPath.localeCompare(bPath);
|
||||||
});
|
});
|
||||||
}, [importedBy]);
|
}, [importedBy, usedBySecretSyncs, secretsToDelete]);
|
||||||
|
|
||||||
const hasImportedItems = importedBy.some((element) => {
|
const hasImportedItems = importedBy.some((element) => {
|
||||||
if (element.folders && element.folders.length > 0) {
|
if (element.folders && element.folders.length > 0) {
|
||||||
@@ -135,7 +170,9 @@ export const CollapsibleSecretImports: React.FC<CollapsibleSecretImportsProps> =
|
|||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!hasImportedItems) {
|
const hasSecretSyncItems = usedBySecretSyncs && usedBySecretSyncs.length > 0;
|
||||||
|
|
||||||
|
if (!hasImportedItems && !hasSecretSyncItems) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,12 +205,28 @@ export const CollapsibleSecretImports: React.FC<CollapsibleSecretImportsProps> =
|
|||||||
key={item.id}
|
key={item.id}
|
||||||
onClick={() => handlePathClick(item)}
|
onClick={() => handlePathClick(item)}
|
||||||
className="cursor-pointer hover:bg-mineshaft-700"
|
className="cursor-pointer hover:bg-mineshaft-700"
|
||||||
title={`Navigate to ${item.path}`}
|
title={
|
||||||
|
item.type === ItemType.SecretSync
|
||||||
|
? "Navigate to Secret Syncs"
|
||||||
|
: `Navigate to ${item.path}`
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Td>
|
<Td>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={item.type === ItemType.Secret ? faKey : faFileImport}
|
icon={
|
||||||
className={`h-4 w-4 ${item.type === ItemType.Secret ? "text-gray-400" : "text-green-700"}`}
|
item.type === ItemType.Secret
|
||||||
|
? faKey
|
||||||
|
: item.type === ItemType.Folder
|
||||||
|
? faFileImport
|
||||||
|
: faSync
|
||||||
|
}
|
||||||
|
className={`h-4 w-4 ${
|
||||||
|
item.type === ItemType.Secret
|
||||||
|
? "text-gray-400"
|
||||||
|
: item.type === ItemType.Folder
|
||||||
|
? "text-green-700"
|
||||||
|
: "text-blue-500"
|
||||||
|
}`}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
</Td>
|
</Td>
|
||||||
|
|||||||
+3
@@ -29,6 +29,7 @@ type Props = {
|
|||||||
tags?: WsTag[];
|
tags?: WsTag[];
|
||||||
isVisible?: boolean;
|
isVisible?: boolean;
|
||||||
isProtectedBranch?: boolean;
|
isProtectedBranch?: boolean;
|
||||||
|
usedBySecretSyncs?: { environment: string; name: string; destination: string }[];
|
||||||
importedBy?: {
|
importedBy?: {
|
||||||
environment: { name: string; slug: string };
|
environment: { name: string; slug: string };
|
||||||
folders: {
|
folders: {
|
||||||
@@ -47,6 +48,7 @@ export const SecretListView = ({
|
|||||||
tags: wsTags = [],
|
tags: wsTags = [],
|
||||||
isVisible,
|
isVisible,
|
||||||
isProtectedBranch = false,
|
isProtectedBranch = false,
|
||||||
|
usedBySecretSyncs,
|
||||||
importedBy
|
importedBy
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
@@ -372,6 +374,7 @@ export const SecretListView = ({
|
|||||||
importedBy.length > 0 && (
|
importedBy.length > 0 && (
|
||||||
<CollapsibleSecretImports
|
<CollapsibleSecretImports
|
||||||
importedBy={importedBy}
|
importedBy={importedBy}
|
||||||
|
usedBySecretSyncs={usedBySecretSyncs}
|
||||||
secretsToDelete={[(popUp.deleteSecret?.data as SecretV3RawSanitized)?.key || ""]}
|
secretsToDelete={[(popUp.deleteSecret?.data as SecretV3RawSanitized)?.key || ""]}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user