Feat: Import indicator

This commit is contained in:
Daniel Hougaard
2024-03-20 20:25:48 +01:00
parent 671adee4d7
commit 90be28b87a
4 changed files with 117 additions and 10 deletions
@@ -11,7 +11,7 @@ import {
TGetImportedFoldersByEnvDTO, TGetImportedFoldersByEnvDTO,
TGetImportedSecrets, TGetImportedSecrets,
TGetSecretImports, TGetSecretImports,
TImportedSecretFolder, TGetSecretImportsAllEnvs,
TImportedSecrets, TImportedSecrets,
TSecretImport, TSecretImport,
TuseGetImportedFoldersByEnv TuseGetImportedFoldersByEnv
@@ -27,7 +27,9 @@ export const secretImportKeys = {
}: Omit<TGetImportedSecrets, "decryptFileKey">) => }: Omit<TGetImportedSecrets, "decryptFileKey">) =>
[{ environment, path, projectId }, "secrets-import-sec"] as const, [{ environment, path, projectId }, "secrets-import-sec"] as const,
getImportedFoldersByEnv: ({ environment, projectId, path }: TGetImportedFoldersByEnvDTO) => getImportedFoldersByEnv: ({ environment, projectId, path }: TGetImportedFoldersByEnvDTO) =>
[{ environment, projectId, path }, "imported-folders"] as const [{ environment, projectId, path }, "imported-folders"] as const,
getImportedFoldersAllEnvs: ({ projectId, path, environment }: TGetImportedFoldersByEnvDTO) =>
[{ projectId, path, environment }, "imported-folders-all-envs"] as const
}; };
const fetchSecretImport = async ({ projectId, environment, path = "/" }: TGetSecretImports) => { const fetchSecretImport = async ({ projectId, environment, path = "/" }: TGetSecretImports) => {
@@ -90,7 +92,7 @@ const fetchImportedFolders = async ({
environment, environment,
path path
}: TGetImportedFoldersByEnvDTO) => { }: TGetImportedFoldersByEnvDTO) => {
const { data } = await apiRequest.get<{ secretImports: TImportedSecretFolder[] }>( const { data } = await apiRequest.get<{ secretImports: TSecretImport[] }>(
"/api/v1/secret-imports", "/api/v1/secret-imports",
{ {
params: { params: {
@@ -103,7 +105,7 @@ const fetchImportedFolders = async ({
return data.secretImports; return data.secretImports;
}; };
export const useGetImportedSecrets = ({ export const useGetImportedSecretsSingleEnv = ({
environment, environment,
decryptFileKey, decryptFileKey,
path, path,
@@ -188,6 +190,98 @@ export const useGetImportedSecrets = ({
) )
}); });
export const useGetImportedSecretsAllEnvs = ({
projectId,
environments,
path = "/",
decryptFileKey
}: TGetSecretImportsAllEnvs) => {
const secretImports = useQueries({
queries: environments.map((env) => ({
queryKey: secretImportKeys.getImportedFoldersAllEnvs({
environment: env,
projectId,
path
}),
queryFn: () => fetchImportedSecrets(projectId, env, path).catch(() => []),
enabled: Boolean(projectId) && Boolean(env),
// eslint-disable-next-line react-hooks/rules-of-hooks
select: useCallback(
(data: TImportedSecrets[]) => {
const PRIVATE_KEY = localStorage.getItem("PRIVATE_KEY") as string;
const latestKey = decryptFileKey;
const key = decryptAssymmetric({
ciphertext: latestKey.encryptedKey,
nonce: latestKey.nonce,
publicKey: latestKey.sender.publicKey,
privateKey: PRIVATE_KEY
});
return data.map((el) => ({
environment: el.environment,
secretPath: el.secretPath,
environmentInfo: el.environmentInfo,
folderId: el.folderId,
secrets: el.secrets.map((encSecret) => {
const secretKey = decryptSymmetric({
ciphertext: encSecret.secretKeyCiphertext,
iv: encSecret.secretKeyIV,
tag: encSecret.secretKeyTag,
key
});
const secretValue = decryptSymmetric({
ciphertext: encSecret.secretValueCiphertext,
iv: encSecret.secretValueIV,
tag: encSecret.secretValueTag,
key
});
const secretComment = decryptSymmetric({
ciphertext: encSecret.secretCommentCiphertext,
iv: encSecret.secretCommentIV,
tag: encSecret.secretCommentTag,
key
});
return {
id: encSecret.id,
env: encSecret.environment,
key: secretKey,
value: secretValue,
tags: encSecret.tags,
comment: secretComment,
createdAt: encSecret.createdAt,
updatedAt: encSecret.updatedAt,
version: encSecret.version
};
})
}));
},
[decryptFileKey]
)
}))
});
const isImportedSecretPresentInEnv = useCallback(
(secPath: string, envSlug: string) => {
const selectedEnvIndex = environments.indexOf(envSlug);
if (selectedEnvIndex !== -1) {
const isPresent = secretImports?.[selectedEnvIndex]?.data?.find(
({ secretPath }) => secretPath === secPath
);
return Boolean(isPresent);
}
return false;
},
[(secretImports || []).map((response) => response.data)]
);
return { secretImports, isImportedSecretPresentInEnv };
};
export const useGetImportedFoldersByEnv = ({ export const useGetImportedFoldersByEnv = ({
projectId, projectId,
environments, environments,
@@ -195,14 +289,16 @@ export const useGetImportedFoldersByEnv = ({
}: TuseGetImportedFoldersByEnv) => { }: TuseGetImportedFoldersByEnv) => {
const queryParams = new URLSearchParams(window.location.search); const queryParams = new URLSearchParams(window.location.search);
const currentPath = path;
const importedFolders = useQueries({ const importedFolders = useQueries({
queries: environments.map((env) => ({ queries: environments.map((env) => ({
queryKey: secretImportKeys.getImportedFoldersByEnv({ queryKey: secretImportKeys.getImportedFoldersByEnv({
projectId, projectId,
environment: env, environment: env,
path path: currentPath
}), }),
queryFn: async () => fetchImportedFolders({ projectId, environment: env, path }), queryFn: async () => fetchImportedFolders({ projectId, environment: env, path: currentPath }),
enabled: Boolean(projectId) && Boolean(env) enabled: Boolean(projectId) && Boolean(env)
})) }))
}); });
@@ -17,7 +17,7 @@ import {
} from "@app/context"; } from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { import {
useGetImportedSecrets, useGetImportedSecretsSingleEnv,
useGetProjectFolders, useGetProjectFolders,
useGetProjectSecrets, useGetProjectSecrets,
useGetSecretApprovalPolicyOfABoard, useGetSecretApprovalPolicyOfABoard,
@@ -124,7 +124,7 @@ export const SecretMainPage = () => {
}); });
// fetch imported secrets to show user the overriden ones // fetch imported secrets to show user the overriden ones
const { data: importedSecrets } = useGetImportedSecrets({ const { data: importedSecrets } = useGetImportedSecretsSingleEnv({
projectId: workspaceId, projectId: workspaceId,
environment, environment,
decryptFileKey: decryptFileKey!, decryptFileKey: decryptFileKey!,
@@ -56,6 +56,7 @@ import {
useDeleteSecretV3, useDeleteSecretV3,
useGetFoldersByEnv, useGetFoldersByEnv,
useGetImportedFoldersByEnv, useGetImportedFoldersByEnv,
useGetImportedSecretsAllEnvs,
useGetProjectSecretsAllEnv, useGetProjectSecretsAllEnv,
useGetUserWsKey, useGetUserWsKey,
useUpdateSecretV3 useUpdateSecretV3
@@ -134,7 +135,12 @@ export const SecretOverviewPage = () => {
const { isImportedFolderPresentInEnv } = useGetImportedFoldersByEnv({ const { isImportedFolderPresentInEnv } = useGetImportedFoldersByEnv({
projectId: workspaceId, projectId: workspaceId,
path: secretPath, environments: userAvailableEnvs.map(({ slug }) => slug)
});
const { isImportedSecretPresentInEnv } = useGetImportedSecretsAllEnvs({
projectId: workspaceId,
decryptFileKey: latestFileKey!,
environments: userAvailableEnvs.map(({ slug }) => slug) environments: userAvailableEnvs.map(({ slug }) => slug)
}); });
@@ -657,6 +663,7 @@ export const SecretOverviewPage = () => {
filteredSecretNames.map((key, index) => ( filteredSecretNames.map((key, index) => (
<SecretOverviewTableRow <SecretOverviewTableRow
secretPath={secretPath} secretPath={secretPath}
isImportedSecretPresentInEnv={isImportedSecretPresentInEnv}
onSecretCreate={handleSecretCreate} onSecretCreate={handleSecretCreate}
onSecretDelete={handleSecretDelete} onSecretDelete={handleSecretDelete}
onSecretUpdate={handleSecretUpdate} onSecretUpdate={handleSecretUpdate}
@@ -40,7 +40,11 @@ export const SecretOverviewFolderRow = ({
isPresent || isImportPresent ? "text-green-600" : "text-red-600" isPresent || isImportPresent ? "text-green-600" : "text-red-600"
)} )}
> >
<Tooltip isDisabled={!isImportPresent} content="Folder is imported"> <Tooltip
center
isDisabled={!isImportPresent}
content="Folder is imported from another environment"
>
<div className="flex justify-center"> <div className="flex justify-center">
<FontAwesomeIcon <FontAwesomeIcon
// eslint-disable-next-line no-nested-ternary // eslint-disable-next-line no-nested-ternary