feat: added handling of folder/secret deletion

This commit is contained in:
Sheen Capadngan
2024-05-07 15:16:37 +08:00
parent a3552d00d1
commit a9b72b2da3
3 changed files with 97 additions and 6 deletions

View File

@@ -94,7 +94,21 @@ export const useGetFoldersByEnv = ({
[(folders || []).map((folder) => folder.data)]
);
return { folders, folderNames, isFolderPresentInEnv };
const getFolderByNameAndEnv = useCallback(
(name: string, env: string) => {
const selectedEnvIndex = environments.indexOf(env);
if (selectedEnvIndex !== -1) {
return folders?.[selectedEnvIndex]?.data?.find(
({ name: folderName }) => folderName === name
);
}
return undefined;
},
[(folders || []).map((folder) => folder.data)]
);
return { folders, folderNames, isFolderPresentInEnv, getFolderByNameAndEnv };
};
export const useCreateFolder = () => {

View File

@@ -134,7 +134,8 @@ export const SecretOverviewPage = () => {
secretPath,
decryptFileKey: latestFileKey!
});
const { folders, folderNames, isFolderPresentInEnv } = useGetFoldersByEnv({
const { folders, folderNames, isFolderPresentInEnv, getFolderByNameAndEnv } = useGetFoldersByEnv({
projectId: workspaceId,
path: secretPath,
environments: userAvailableEnvs.map(({ slug }) => slug)
@@ -548,7 +549,11 @@ export const SecretOverviewPage = () => {
</div>
</div>
</div>
<SelectionPanel secretPath={secretPath} />
<SelectionPanel
secretPath={secretPath}
getSecretByKey={getSecretByKey}
getFolderByNameAndEnv={getFolderByNameAndEnv}
/>
<div className="thin-scrollbar mt-4" ref={parentTableRef}>
<TableContainer className="max-h-[calc(100vh-250px)] overflow-y-auto">
<Table>

View File

@@ -3,17 +3,27 @@ import { faMinusSquare, faTrash } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge";
import { createNotification } from "@app/components/notifications";
import { Button, DeleteActionModal, IconButton, Tooltip } from "@app/components/v2";
import { ProjectPermissionActions, ProjectPermissionSub, useProjectPermission } from "@app/context";
import {
ProjectPermissionActions,
ProjectPermissionSub,
useProjectPermission,
useWorkspace
} from "@app/context";
import { usePopUp } from "@app/hooks";
import { useDeleteFolder, useDeleteSecretBatch } from "@app/hooks/api";
import { DecryptedSecret, TDeleteSecretBatchDTO, TSecretFolder } from "@app/hooks/api/types";
import { useSelectedEntries, useSelectedEntryActions } from "../../SecretOverviewPage.store";
type Props = {
secretPath: string;
getSecretByKey: (slug: string, key: string) => DecryptedSecret | undefined;
getFolderByNameAndEnv: (name: string, env: string) => TSecretFolder | undefined;
};
export const SelectionPanel = ({ secretPath }: Props) => {
export const SelectionPanel = ({ getFolderByNameAndEnv, getSecretByKey, secretPath }: Props) => {
const { permission } = useProjectPermission();
const { handlePopUpOpen, handlePopUpToggle, handlePopUpClose, popUp } = usePopUp([
@@ -25,6 +35,12 @@ export const SelectionPanel = ({ secretPath }: Props) => {
const selectedCount =
Object.keys(selectedEntries.folder).length + Object.keys(selectedEntries.secret).length;
const { currentWorkspace } = useWorkspace();
const workspaceId = currentWorkspace?.id || "";
const userAvailableEnvs = currentWorkspace?.environments || [];
const { mutateAsync: deleteBatchSecretV3 } = useDeleteSecretBatch();
const { mutateAsync: deleteFolder } = useDeleteFolder();
const isMultiSelectActive = selectedCount > 0;
// TODO: REVISIT RBAC
@@ -34,7 +50,63 @@ export const SelectionPanel = ({ secretPath }: Props) => {
);
const handleBulkDelete = async () => {
handlePopUpClose("bulkDeleteEntries");
const promises = userAvailableEnvs.map(async (env) => {
await Promise.all(
Object.keys(selectedEntries.folder).map(async (folderName) => {
const folder = getFolderByNameAndEnv(folderName, env.slug);
if (folder) {
await deleteFolder({
folderId: folder?.id,
path: secretPath,
environment: env.slug,
projectId: workspaceId
});
}
})
);
const secretsToDelete = Object.keys(selectedEntries.secret).reduce(
(accum: TDeleteSecretBatchDTO["secrets"], secretName) => {
const entry = getSecretByKey(env.slug, secretName);
if (entry) {
return [
...accum,
{
secretName: entry.key,
type: "shared" as "shared"
}
];
}
return accum;
},
[]
);
if (secretsToDelete.length > 0) {
await deleteBatchSecretV3({
secretPath,
workspaceId,
environment: env.slug,
secrets: secretsToDelete
});
}
});
const results = await Promise.allSettled(promises);
const areEntriesDeleted = results.some((result) => result.status === "fulfilled");
if (areEntriesDeleted) {
handlePopUpClose("bulkDeleteEntries");
resetSelectedEntries();
createNotification({
type: "success",
text: "Successfully deleted selected secrets and folders"
});
} else {
createNotification({
type: "error",
text: "Failed to delete selected secrets and folders"
});
}
};
return (