github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ListObjects/DeleteMultipleObjects.tsx (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2021 MinIO, Inc.
     3  //
     4  // This program is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Affero General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // This program is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  // GNU Affero General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Affero General Public License
    15  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16  
    17  import React, { Fragment, useState } from "react";
    18  import { ErrorResponseHandler } from "../../../../../../common/types";
    19  import useApi from "../../../../Common/Hooks/useApi";
    20  import ConfirmDialog from "../../../../Common/ModalWrapper/ConfirmDialog";
    21  import { ConfirmDeleteIcon, Switch } from "mds";
    22  import { setErrorSnackMessage } from "../../../../../../systemSlice";
    23  import { AppState, useAppDispatch } from "../../../../../../store";
    24  import { hasPermission } from "../../../../../../common/SecureComponent";
    25  import { IAM_SCOPES } from "../../../../../../common/SecureComponent/permissions";
    26  import { useSelector } from "react-redux";
    27  import { BucketVersioningResponse } from "api/consoleApi";
    28  import { api } from "../../../../../../api";
    29  import { encodeURLString } from "../../../../../../common/utils";
    30  
    31  interface IDeleteObjectProps {
    32    closeDeleteModalAndRefresh: (refresh: boolean) => void;
    33    deleteOpen: boolean;
    34    selectedObjects: string[];
    35    selectedBucket: string;
    36  
    37    versioning: BucketVersioningResponse;
    38  }
    39  
    40  const DeleteObject = ({
    41    closeDeleteModalAndRefresh,
    42    deleteOpen,
    43    selectedBucket,
    44    selectedObjects,
    45  
    46    versioning,
    47  }: IDeleteObjectProps) => {
    48    const dispatch = useAppDispatch();
    49    const onDelSuccess = () => closeDeleteModalAndRefresh(true);
    50    const onDelError = (err: ErrorResponseHandler) =>
    51      dispatch(setErrorSnackMessage(err));
    52    const onClose = () => closeDeleteModalAndRefresh(false);
    53  
    54    const [deleteLoading, invokeDeleteApi] = useApi(onDelSuccess, onDelError);
    55  
    56    const [deleteVersions, setDeleteVersions] = useState<boolean>(false);
    57    const [bypassGovernance, setBypassGovernance] = useState<boolean>(false);
    58  
    59    const retentionConfig = useSelector(
    60      (state: AppState) => state.objectBrowser.retentionConfig,
    61    );
    62  
    63    const canBypass =
    64      hasPermission(
    65        [selectedBucket],
    66        [IAM_SCOPES.S3_BYPASS_GOVERNANCE_RETENTION],
    67      ) && retentionConfig?.mode === "governance";
    68  
    69    if (!selectedObjects) {
    70      return null;
    71    }
    72    const onConfirmDelete = () => {
    73      let toSend = [];
    74      for (let i = 0; i < selectedObjects.length; i++) {
    75        if (selectedObjects[i].endsWith("/")) {
    76          toSend.push({
    77            path: selectedObjects[i],
    78            versionID: "",
    79            recursive: true,
    80          });
    81        } else {
    82          toSend.push({
    83            path: selectedObjects[i],
    84            versionID: "",
    85            recursive: false,
    86          });
    87        }
    88      }
    89  
    90      if (toSend) {
    91        if (selectedObjects.length === 1) {
    92          const firstObject = selectedObjects[0];
    93          api.buckets
    94            .deleteObject(selectedBucket, {
    95              prefix: encodeURLString(firstObject),
    96              all_versions: deleteVersions,
    97              bypass: bypassGovernance,
    98              recursive: firstObject.endsWith("/"), //if it is just a prefix
    99            })
   100            .then(onDelSuccess)
   101            .catch((err) => {
   102              dispatch(
   103                setErrorSnackMessage({
   104                  errorMessage: `Could not delete object. ${err.statusText}. ${
   105                    retentionConfig
   106                      ? "Please check retention mode and if object is WORM protected."
   107                      : ""
   108                  }`,
   109                  detailedError: "",
   110                }),
   111              );
   112            });
   113        } else {
   114          invokeDeleteApi(
   115            "POST",
   116            `/api/v1/buckets/${selectedBucket}/delete-objects?all_versions=${deleteVersions}${
   117              bypassGovernance ? "&bypass=true" : ""
   118            }`,
   119            toSend,
   120          );
   121        }
   122      }
   123    };
   124  
   125    const isVersionedDelete =
   126      versioning?.status === "Enabled" || versioning?.status === "Suspended";
   127  
   128    return (
   129      <ConfirmDialog
   130        title={`Delete Objects`}
   131        confirmText={"Delete"}
   132        isOpen={deleteOpen}
   133        titleIcon={<ConfirmDeleteIcon />}
   134        isLoading={deleteLoading}
   135        onConfirm={onConfirmDelete}
   136        onClose={onClose}
   137        confirmationContent={
   138          <Fragment>
   139            Are you sure you want to delete the selected {selectedObjects.length}{" "}
   140            objects?{" "}
   141            {isVersionedDelete && (
   142              <Fragment>
   143                <br />
   144                <br />
   145                <Switch
   146                  label={"Delete All Versions"}
   147                  indicatorLabels={["Yes", "No"]}
   148                  checked={deleteVersions}
   149                  value={"delete_versions"}
   150                  id="delete-versions"
   151                  name="delete-versions"
   152                  onChange={(e) => {
   153                    setDeleteVersions(!deleteVersions);
   154                  }}
   155                  description=""
   156                />
   157                {canBypass && deleteVersions && (
   158                  <Fragment>
   159                    <div
   160                      style={{
   161                        marginTop: 10,
   162                      }}
   163                    >
   164                      <Switch
   165                        label={"Bypass Governance Mode"}
   166                        indicatorLabels={["Yes", "No"]}
   167                        checked={bypassGovernance}
   168                        value={"bypass_governance"}
   169                        id="bypass_governance"
   170                        name="bypass_governance"
   171                        onChange={(e) => {
   172                          setBypassGovernance(!bypassGovernance);
   173                        }}
   174                        description=""
   175                      />
   176                    </div>
   177                  </Fragment>
   178                )}
   179                {deleteVersions && (
   180                  <Fragment>
   181                    <div
   182                      style={{
   183                        marginTop: 10,
   184                        border: "#c83b51 1px solid",
   185                        borderRadius: 3,
   186                        padding: 5,
   187                        backgroundColor: "#c83b5120",
   188                        color: "#c83b51",
   189                      }}
   190                    >
   191                      This will remove the objects as well as all of their
   192                      versions, <br />
   193                      This action is irreversible.
   194                    </div>
   195                    <br />
   196                    Are you sure you want to continue?
   197                  </Fragment>
   198                )}
   199              </Fragment>
   200            )}
   201          </Fragment>
   202        }
   203      />
   204    );
   205  };
   206  
   207  export default DeleteObject;