mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 22:27:48 +00:00
feat(secrets-ui): improve warning message table
This commit is contained in:
@@ -171,7 +171,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
.object({
|
.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
destination: z.string(),
|
destination: z.string(),
|
||||||
environment: z.string()
|
environment: z.string(),
|
||||||
|
id: z.string(),
|
||||||
|
path: z.string()
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.optional(),
|
.optional(),
|
||||||
@@ -509,7 +511,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const usedBySecretSyncs: { name: string; destination: string; environment: string }[] = [];
|
const usedBySecretSyncs: { name: string; destination: string; environment: string; id: string; path: string }[] =
|
||||||
|
[];
|
||||||
for await (const environment of environments) {
|
for await (const environment of environments) {
|
||||||
const secretSyncs = await server.services.secretSync.listSecretSyncsBySecretPath(
|
const secretSyncs = await server.services.secretSync.listSecretSyncsBySecretPath(
|
||||||
{ projectId, secretPath, environment },
|
{ projectId, secretPath, environment },
|
||||||
@@ -519,7 +522,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
usedBySecretSyncs.push({
|
usedBySecretSyncs.push({
|
||||||
name: sync.name,
|
name: sync.name,
|
||||||
destination: sync.destination,
|
destination: sync.destination,
|
||||||
environment
|
environment,
|
||||||
|
id: sync.id,
|
||||||
|
path: sync.folder?.path || "/"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -640,7 +645,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
.object({
|
.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
destination: z.string(),
|
destination: z.string(),
|
||||||
environment: z.string()
|
environment: z.string(),
|
||||||
|
id: z.string(),
|
||||||
|
path: z.string()
|
||||||
})
|
})
|
||||||
.array()
|
.array()
|
||||||
.optional(),
|
.optional(),
|
||||||
@@ -945,7 +952,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
|||||||
const usedBySecretSyncs = secretSyncs.map((sync) => ({
|
const usedBySecretSyncs = secretSyncs.map((sync) => ({
|
||||||
name: sync.name,
|
name: sync.name,
|
||||||
destination: sync.destination,
|
destination: sync.destination,
|
||||||
environment: sync.environment?.name || environment
|
environment: sync.environment?.name || environment,
|
||||||
|
id: sync.id,
|
||||||
|
path: sync.folder?.path || "/"
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (secrets?.length || secretRotations?.length) {
|
if (secrets?.length || secretRotations?.length) {
|
||||||
|
|||||||
@@ -25,10 +25,18 @@ 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 }[];
|
usedBySecretSyncs?: UsedBySecretSyncs[];
|
||||||
totalUniqueSecretRotationsInPage: number;
|
totalUniqueSecretRotationsInPage: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type UsedBySecretSyncs = {
|
||||||
|
name: string;
|
||||||
|
destination: string;
|
||||||
|
environment: string;
|
||||||
|
id: string;
|
||||||
|
path: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type DashboardProjectSecretsDetailsResponse = {
|
export type DashboardProjectSecretsDetailsResponse = {
|
||||||
imports?: TSecretImport[];
|
imports?: TSecretImport[];
|
||||||
folders?: TSecretFolder[];
|
folders?: TSecretFolder[];
|
||||||
@@ -44,7 +52,7 @@ export type DashboardProjectSecretsDetailsResponse = {
|
|||||||
totalSecretRotationCount?: number;
|
totalSecretRotationCount?: number;
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
importedBy?: ProjectSecretsImportedBy[];
|
importedBy?: ProjectSecretsImportedBy[];
|
||||||
usedBySecretSyncs?: { name: string; destination: string; environment: string }[];
|
usedBySecretSyncs?: UsedBySecretSyncs[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ProjectSecretsImportedBy = {
|
export type ProjectSecretsImportedBy = {
|
||||||
|
|||||||
+5
-5
@@ -15,7 +15,7 @@ import {
|
|||||||
import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types";
|
import { ProjectPermissionSecretActions } from "@app/context/ProjectPermissionContext/types";
|
||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
import { useDeleteFolder, useDeleteSecretBatch } from "@app/hooks/api";
|
import { useDeleteFolder, useDeleteSecretBatch } from "@app/hooks/api";
|
||||||
import { ProjectSecretsImportedBy } from "@app/hooks/api/dashboard/types";
|
import { ProjectSecretsImportedBy, UsedBySecretSyncs } from "@app/hooks/api/dashboard/types";
|
||||||
import {
|
import {
|
||||||
SecretType,
|
SecretType,
|
||||||
SecretV3RawSanitized,
|
SecretV3RawSanitized,
|
||||||
@@ -38,7 +38,7 @@ type Props = {
|
|||||||
[EntryType.SECRET]: Record<string, Record<string, SecretV3RawSanitized>>;
|
[EntryType.SECRET]: Record<string, Record<string, SecretV3RawSanitized>>;
|
||||||
};
|
};
|
||||||
importedBy?: ProjectSecretsImportedBy[] | null;
|
importedBy?: ProjectSecretsImportedBy[] | null;
|
||||||
usedBySecretSyncs?: { name: string; destination: string; environment: string }[];
|
usedBySecretSyncs?: UsedBySecretSyncs[];
|
||||||
secretsToDeleteKeys: string[];
|
secretsToDeleteKeys: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -261,10 +261,10 @@ export const SelectionPanel = ({
|
|||||||
onChange={(isOpen) => handlePopUpToggle("bulkDeleteEntries", isOpen)}
|
onChange={(isOpen) => handlePopUpToggle("bulkDeleteEntries", isOpen)}
|
||||||
onDeleteApproved={handleBulkDelete}
|
onDeleteApproved={handleBulkDelete}
|
||||||
formContent={
|
formContent={
|
||||||
importedBy &&
|
((usedBySecretSyncsFiltered && usedBySecretSyncsFiltered.length > 0) ||
|
||||||
importedBy.some((element) => element.folders.length > 0) && (
|
(importedBy && importedBy.some((element) => element.folders.length > 0))) && (
|
||||||
<CollapsibleSecretImports
|
<CollapsibleSecretImports
|
||||||
importedBy={importedBy}
|
importedBy={importedBy || []}
|
||||||
secretsToDelete={secretsToDeleteKeys}
|
secretsToDelete={secretsToDeleteKeys}
|
||||||
usedBySecretSyncs={usedBySecretSyncsFiltered}
|
usedBySecretSyncs={usedBySecretSyncsFiltered}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+2
-1
@@ -69,6 +69,7 @@ import {
|
|||||||
dashboardKeys,
|
dashboardKeys,
|
||||||
fetchDashboardProjectSecretsByKeys
|
fetchDashboardProjectSecretsByKeys
|
||||||
} from "@app/hooks/api/dashboard/queries";
|
} from "@app/hooks/api/dashboard/queries";
|
||||||
|
import { UsedBySecretSyncs } from "@app/hooks/api/dashboard/types";
|
||||||
import { secretApprovalRequestKeys } from "@app/hooks/api/secretApprovalRequest/queries";
|
import { secretApprovalRequestKeys } from "@app/hooks/api/secretApprovalRequest/queries";
|
||||||
import { fetchProjectSecrets, secretKeys } from "@app/hooks/api/secrets/queries";
|
import { fetchProjectSecrets, secretKeys } from "@app/hooks/api/secrets/queries";
|
||||||
import { ApiErrorTypes, SecretType, TApiErrors, WsTag } from "@app/hooks/api/types";
|
import { ApiErrorTypes, SecretType, TApiErrors, WsTag } from "@app/hooks/api/types";
|
||||||
@@ -113,7 +114,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 }[];
|
usedBySecretSyncs?: UsedBySecretSyncs[];
|
||||||
importedBy?: {
|
importedBy?: {
|
||||||
environment: { name: string; slug: string };
|
environment: { name: string; slug: string };
|
||||||
folders: {
|
folders: {
|
||||||
|
|||||||
+35
-30
@@ -3,8 +3,9 @@ import React, { useMemo } from "react";
|
|||||||
import { faFileImport, faKey, faSync, 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, Tooltip, Tr } from "@app/components/v2";
|
||||||
import { useWorkspace } from "@app/context";
|
import { useWorkspace } from "@app/context";
|
||||||
|
import { UsedBySecretSyncs } from "@app/hooks/api/dashboard/types";
|
||||||
|
|
||||||
enum ItemType {
|
enum ItemType {
|
||||||
Folder = "Folder",
|
Folder = "Folder",
|
||||||
@@ -19,6 +20,8 @@ interface FlatItem {
|
|||||||
reference: string;
|
reference: string;
|
||||||
id: string;
|
id: string;
|
||||||
environment: { name: string; slug: string };
|
environment: { name: string; slug: string };
|
||||||
|
tooltipText?: string;
|
||||||
|
destination?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CollapsibleSecretImportsProps {
|
interface CollapsibleSecretImportsProps {
|
||||||
@@ -30,13 +33,7 @@ interface CollapsibleSecretImportsProps {
|
|||||||
isImported: boolean;
|
isImported: boolean;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
usedBySecretSyncs?:
|
usedBySecretSyncs?: UsedBySecretSyncs[] | null;
|
||||||
| {
|
|
||||||
name: string;
|
|
||||||
destination: string;
|
|
||||||
environment: string;
|
|
||||||
}[]
|
|
||||||
| null;
|
|
||||||
secretsToDelete: string[];
|
secretsToDelete: string[];
|
||||||
onlyReferences?: boolean;
|
onlyReferences?: boolean;
|
||||||
}
|
}
|
||||||
@@ -65,7 +62,7 @@ export const CollapsibleSecretImports: React.FC<CollapsibleSecretImportsProps> =
|
|||||||
const handlePathClick = (item: FlatItem) => {
|
const handlePathClick = (item: FlatItem) => {
|
||||||
if (item.type === ItemType.SecretSync) {
|
if (item.type === ItemType.SecretSync) {
|
||||||
window.open(
|
window.open(
|
||||||
`/secret-manager/${currentWorkspace.id}/integrations?selectedTab=secret-syncs`,
|
`/secret-manager/${currentWorkspace.id}/integrations/secret-syncs/${item.destination}/${item.id}`,
|
||||||
"_blank",
|
"_blank",
|
||||||
"noopener,noreferrer"
|
"noopener,noreferrer"
|
||||||
);
|
);
|
||||||
@@ -128,10 +125,12 @@ export const CollapsibleSecretImports: React.FC<CollapsibleSecretImportsProps> =
|
|||||||
usedBySecretSyncs?.forEach((syncItem) => {
|
usedBySecretSyncs?.forEach((syncItem) => {
|
||||||
items.push({
|
items.push({
|
||||||
type: ItemType.SecretSync,
|
type: ItemType.SecretSync,
|
||||||
path: syncItem.destination,
|
destination: syncItem.destination,
|
||||||
id: `secret-sync-${syncItem.name}-${syncItem.destination}`,
|
path: syncItem.path,
|
||||||
|
id: syncItem.id,
|
||||||
reference: "Secret Sync",
|
reference: "Secret Sync",
|
||||||
environment: { name: syncItem.environment, slug: "" }
|
environment: { name: syncItem.environment, slug: "" },
|
||||||
|
tooltipText: `Currently used by Secret Sync: ${syncItem.name}`
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -222,28 +221,34 @@ export const CollapsibleSecretImports: React.FC<CollapsibleSecretImportsProps> =
|
|||||||
className="cursor-pointer hover:bg-mineshaft-700"
|
className="cursor-pointer hover:bg-mineshaft-700"
|
||||||
title={
|
title={
|
||||||
item.type === ItemType.SecretSync
|
item.type === ItemType.SecretSync
|
||||||
? "Navigate to Secret Syncs"
|
? "Navigate to Secret Sync"
|
||||||
: `Navigate to ${item.path}`
|
: `Navigate to ${item.path}`
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Td>
|
<Td>
|
||||||
<FontAwesomeIcon
|
<Tooltip
|
||||||
icon={
|
className="max-w-md"
|
||||||
item.type === ItemType.Secret
|
content={item.tooltipText}
|
||||||
? faKey
|
isDisabled={!item.tooltipText}
|
||||||
: item.type === ItemType.Folder
|
>
|
||||||
? faFileImport
|
<FontAwesomeIcon
|
||||||
: faSync
|
icon={
|
||||||
}
|
item.type === ItemType.Secret
|
||||||
className={`h-4 w-4 ${
|
? faKey
|
||||||
item.type === ItemType.Secret
|
: item.type === ItemType.Folder
|
||||||
? "text-gray-400"
|
? faFileImport
|
||||||
: item.type === ItemType.Folder
|
: faSync
|
||||||
? "text-green-700"
|
}
|
||||||
: "text-blue-500"
|
className={`h-4 w-4 ${
|
||||||
}`}
|
item.type === ItemType.Secret
|
||||||
aria-hidden="true"
|
? "text-gray-400"
|
||||||
/>
|
: item.type === ItemType.Folder
|
||||||
|
? "text-green-700"
|
||||||
|
: "text-blue-500"
|
||||||
|
}`}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
</Td>
|
</Td>
|
||||||
<Td className="px-4">{item.environment.name}</Td>
|
<Td className="px-4">{item.environment.name}</Td>
|
||||||
<Td className="truncate px-4">{truncatePath(item.path)}</Td>
|
<Td className="truncate px-4">{truncatePath(item.path)}</Td>
|
||||||
|
|||||||
+4
-3
@@ -8,6 +8,7 @@ import { DeleteActionModal } from "@app/components/v2";
|
|||||||
import { usePopUp } from "@app/hooks";
|
import { usePopUp } from "@app/hooks";
|
||||||
import { useCreateSecretV3, useDeleteSecretV3, useUpdateSecretV3 } from "@app/hooks/api";
|
import { useCreateSecretV3, useDeleteSecretV3, useUpdateSecretV3 } from "@app/hooks/api";
|
||||||
import { dashboardKeys } from "@app/hooks/api/dashboard/queries";
|
import { dashboardKeys } from "@app/hooks/api/dashboard/queries";
|
||||||
|
import { UsedBySecretSyncs } from "@app/hooks/api/dashboard/types";
|
||||||
import { secretApprovalRequestKeys } from "@app/hooks/api/secretApprovalRequest/queries";
|
import { secretApprovalRequestKeys } from "@app/hooks/api/secretApprovalRequest/queries";
|
||||||
import { secretKeys } from "@app/hooks/api/secrets/queries";
|
import { secretKeys } from "@app/hooks/api/secrets/queries";
|
||||||
import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/secrets/types";
|
import { SecretType, SecretV3RawSanitized } from "@app/hooks/api/secrets/types";
|
||||||
@@ -29,7 +30,7 @@ type Props = {
|
|||||||
tags?: WsTag[];
|
tags?: WsTag[];
|
||||||
isVisible?: boolean;
|
isVisible?: boolean;
|
||||||
isProtectedBranch?: boolean;
|
isProtectedBranch?: boolean;
|
||||||
usedBySecretSyncs?: { environment: string; name: string; destination: string }[];
|
usedBySecretSyncs?: UsedBySecretSyncs[];
|
||||||
importedBy?: {
|
importedBy?: {
|
||||||
environment: { name: string; slug: string };
|
environment: { name: string; slug: string };
|
||||||
folders: {
|
folders: {
|
||||||
@@ -385,8 +386,8 @@ export const SecretListView = ({
|
|||||||
onDeleteApproved={handleSecretDelete}
|
onDeleteApproved={handleSecretDelete}
|
||||||
buttonText="Delete Secret"
|
buttonText="Delete Secret"
|
||||||
formContent={
|
formContent={
|
||||||
importedBy &&
|
((importedBy && importedBy.length > 0) ||
|
||||||
importedBy.length > 0 && (
|
(usedBySecretSyncs && usedBySecretSyncs?.length > 0)) && (
|
||||||
<CollapsibleSecretImports
|
<CollapsibleSecretImports
|
||||||
importedBy={importedBy}
|
importedBy={importedBy}
|
||||||
usedBySecretSyncs={usedBySecretSyncs}
|
usedBySecretSyncs={usedBySecretSyncs}
|
||||||
|
|||||||
Reference in New Issue
Block a user