github.com/minio/console@v1.4.1/web-app/src/screens/Console/ObjectBrowser/objectBrowserThunks.ts (about)

     1  // This file is part of MinIO Console Server
     2  // Copyright (c) 2022 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 { createAsyncThunk } from "@reduxjs/toolkit";
    18  import { AppState } from "../../../store";
    19  import { encodeURLString, getClientOS } from "../../../common/utils";
    20  import { BucketObjectItem } from "../Buckets/ListBuckets/Objects/ListObjects/types";
    21  import { makeid, storeCallForObjectWithID } from "./transferManager";
    22  import {
    23    download,
    24    downloadSelectedAsZip,
    25  } from "../Buckets/ListBuckets/Objects/utils";
    26  import {
    27    cancelObjectInList,
    28    completeObject,
    29    failObject,
    30    setAnonymousAccessOpen,
    31    setDownloadRenameModal,
    32    setMaxShareLinkExpTime,
    33    setNewObject,
    34    setPreviewOpen,
    35    setSelectedPreview,
    36    setShareFileModalOpen,
    37    updateProgress,
    38  } from "./objectBrowserSlice";
    39  import { setSnackBarMessage } from "../../../systemSlice";
    40  import { DateTime } from "luxon";
    41  import { api } from "api";
    42  
    43  export const downloadSelected = createAsyncThunk(
    44    "objectBrowser/downloadSelected",
    45    async (bucketName: string, { getState, rejectWithValue, dispatch }) => {
    46      const state = getState() as AppState;
    47  
    48      const downloadObject = (object: BucketObjectItem) => {
    49        const identityDownload = encodeURLString(
    50          `${bucketName}-${object.name}-${new Date().getTime()}-${Math.random()}`,
    51        );
    52  
    53        const ID = makeid(8);
    54  
    55        const downloadCall = download(
    56          bucketName,
    57          encodeURLString(object.name),
    58          object.version_id,
    59          object.size,
    60          null,
    61          ID,
    62          (progress) => {
    63            dispatch(
    64              updateProgress({
    65                instanceID: identityDownload,
    66                progress: progress,
    67              }),
    68            );
    69          },
    70          () => {
    71            dispatch(completeObject(identityDownload));
    72          },
    73          (msg: string) => {
    74            dispatch(failObject({ instanceID: identityDownload, msg }));
    75          },
    76          () => {
    77            dispatch(cancelObjectInList(identityDownload));
    78          },
    79          () => {
    80            dispatch(
    81              setSnackBarMessage(
    82                "File download will be handled directly by the browser.",
    83              ),
    84            );
    85          },
    86        );
    87        storeCallForObjectWithID(ID, downloadCall);
    88        dispatch(
    89          setNewObject({
    90            ID,
    91            bucketName,
    92            done: false,
    93            instanceID: identityDownload,
    94            percentage: 0,
    95            prefix: object.name,
    96            type: "download",
    97            waitingForFile: true,
    98            failed: false,
    99            cancelled: false,
   100            errorMessage: "",
   101          }),
   102        );
   103      };
   104  
   105      if (state.objectBrowser.selectedObjects.length !== 0) {
   106        let itemsToDownload: BucketObjectItem[] = [];
   107  
   108        const filterFunction = (currValue: BucketObjectItem) =>
   109          state.objectBrowser.selectedObjects.includes(currValue.name);
   110  
   111        itemsToDownload = state.objectBrowser.records.filter(filterFunction);
   112  
   113        // In case just one element is selected, then we trigger download modal validation.
   114        if (itemsToDownload.length === 1) {
   115          if (
   116            itemsToDownload[0].name.length > 200 &&
   117            getClientOS().toLowerCase().includes("win")
   118          ) {
   119            dispatch(setDownloadRenameModal(itemsToDownload[0]));
   120            return;
   121          } else {
   122            downloadObject(itemsToDownload[0]);
   123          }
   124        } else {
   125          if (itemsToDownload.length === 1) {
   126            downloadObject(itemsToDownload[0]);
   127          } else if (itemsToDownload.length > 1) {
   128            const fileName = `${DateTime.now().toFormat(
   129              "LL-dd-yyyy-HH-mm-ss",
   130            )}_files_list.zip`;
   131  
   132            // We are enforcing zip download when multiple files are selected for better user experience
   133            const multiObjList = itemsToDownload.reduce((dwList: any[], bi) => {
   134              // Download objects/prefixes(recursively) as zip
   135              // Skip any deleted files selected via "Show deleted objects" in selection and log for debugging
   136              const isDeleted = bi?.delete_flag;
   137              if (bi && !isDeleted) {
   138                dwList.push(bi.name);
   139              } else {
   140                console.log(`Skipping ${bi?.name} from download.`);
   141              }
   142              return dwList;
   143            }, []);
   144  
   145            await downloadSelectedAsZip(bucketName, multiObjList, fileName);
   146            return;
   147          }
   148        }
   149      }
   150    },
   151  );
   152  
   153  export const openPreview = createAsyncThunk(
   154    "objectBrowser/openPreview",
   155    async (_, { getState, rejectWithValue, dispatch }) => {
   156      const state = getState() as AppState;
   157  
   158      if (state.objectBrowser.selectedObjects.length === 1) {
   159        let fileObject: BucketObjectItem | undefined;
   160  
   161        const findFunction = (currValue: BucketObjectItem) =>
   162          state.objectBrowser.selectedObjects.includes(currValue.name);
   163  
   164        fileObject = state.objectBrowser.records.find(findFunction);
   165  
   166        if (fileObject) {
   167          dispatch(setSelectedPreview(fileObject));
   168          dispatch(setPreviewOpen(true));
   169        }
   170      }
   171    },
   172  );
   173  
   174  export const openShare = createAsyncThunk(
   175    "objectBrowser/openShare",
   176    async (_, { getState, rejectWithValue, dispatch }) => {
   177      const state = getState() as AppState;
   178  
   179      if (state.objectBrowser.selectedObjects.length === 1) {
   180        let fileObject: BucketObjectItem | undefined;
   181  
   182        const findFunction = (currValue: BucketObjectItem) =>
   183          state.objectBrowser.selectedObjects.includes(currValue.name);
   184  
   185        fileObject = state.objectBrowser.records.find(findFunction);
   186  
   187        if (fileObject) {
   188          dispatch(setSelectedPreview(fileObject));
   189          dispatch(setShareFileModalOpen(true));
   190        }
   191      }
   192    },
   193  );
   194  
   195  export const openAnonymousAccess = createAsyncThunk(
   196    "objectBrowser/openAnonymousAccess",
   197    async (_, { getState, dispatch }) => {
   198      const state = getState() as AppState;
   199  
   200      if (
   201        state.objectBrowser.selectedObjects.length === 1 &&
   202        state.objectBrowser.selectedObjects[0].endsWith("/")
   203      ) {
   204        dispatch(setAnonymousAccessOpen(true));
   205      }
   206    },
   207  );
   208  
   209  export const getMaxShareLinkExpTime = createAsyncThunk(
   210    "objectBrowser/maxShareLinkExpTime",
   211    async (_, { rejectWithValue, dispatch }) => {
   212      return api.buckets
   213        .getMaxShareLinkExp()
   214        .then((res) => {
   215          dispatch(setMaxShareLinkExpTime(res.data.exp));
   216        })
   217        .catch(async (res) => {
   218          return rejectWithValue(res.error);
   219        });
   220    },
   221  );