github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/BucketDetails/BrowserHandler.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, useCallback, useEffect } from "react";
    18  import { useSelector } from "react-redux";
    19  import { useLocation, useParams } from "react-router-dom";
    20  import { api } from "api";
    21  import { AppState, useAppDispatch } from "../../../../store";
    22  import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
    23  import { decodeURLString, encodeURLString } from "../../../../common/utils";
    24  import {
    25    resetMessages,
    26    setIsVersioned,
    27    setLoadingLocking,
    28    setLoadingObjectInfo,
    29    setLoadingVersioning,
    30    setLoadingVersions,
    31    setLockingEnabled,
    32    setObjectDetailsView,
    33    setRequestInProgress,
    34    setSelectedObjectView,
    35    setVersionsModeEnabled,
    36  } from "../../ObjectBrowser/objectBrowserSlice";
    37  import ListObjects from "../ListBuckets/Objects/ListObjects/ListObjects";
    38  import hasPermission from "../../../../common/SecureComponent/accessControl";
    39  import OBHeader from "../../ObjectBrowser/OBHeader";
    40  
    41  const BrowserHandler = () => {
    42    const dispatch = useAppDispatch();
    43    const params = useParams();
    44    const location = useLocation();
    45  
    46    const loadingVersioning = useSelector(
    47      (state: AppState) => state.objectBrowser.loadingVersioning,
    48    );
    49  
    50    const rewindEnabled = useSelector(
    51      (state: AppState) => state.objectBrowser.rewind.rewindEnabled,
    52    );
    53    const rewindDate = useSelector(
    54      (state: AppState) => state.objectBrowser.rewind.dateToRewind,
    55    );
    56    const showDeleted = useSelector(
    57      (state: AppState) => state.objectBrowser.showDeleted,
    58    );
    59    const requestInProgress = useSelector(
    60      (state: AppState) => state.objectBrowser.requestInProgress,
    61    );
    62    const loadingLocking = useSelector(
    63      (state: AppState) => state.objectBrowser.loadingLocking,
    64    );
    65    const reloadObjectsList = useSelector(
    66      (state: AppState) => state.objectBrowser.reloadObjectsList,
    67    );
    68    const simplePath = useSelector(
    69      (state: AppState) => state.objectBrowser.simplePath,
    70    );
    71    const anonymousMode = useSelector(
    72      (state: AppState) => state.system.anonymousMode,
    73    );
    74    const selectedBucket = useSelector(
    75      (state: AppState) => state.objectBrowser.selectedBucket,
    76    );
    77    const records = useSelector((state: AppState) => state.objectBrowser.records);
    78  
    79    const bucketName = params.bucketName || "";
    80    const pathSegment = location.pathname.split(`/browser/${bucketName}/`);
    81    const internalPaths = pathSegment.length === 2 ? pathSegment[1] : "";
    82  
    83    const initWSRequest = useCallback(
    84      (path: string) => {
    85        let currDate = new Date();
    86  
    87        let date = currDate.toISOString();
    88  
    89        if (rewindDate !== null && rewindEnabled) {
    90          date = rewindDate;
    91        }
    92  
    93        const payloadData = {
    94          bucketName,
    95          path,
    96          rewindMode: rewindEnabled || showDeleted,
    97          date: date,
    98        };
    99  
   100        dispatch({ type: "socket/OBRequest", payload: payloadData });
   101      },
   102      [bucketName, showDeleted, rewindDate, rewindEnabled, dispatch],
   103    );
   104  
   105    // Common path load
   106    const pathLoad = useCallback(
   107      (forceLoad: boolean = false) => {
   108        const decodedInternalPaths = decodeURLString(internalPaths);
   109  
   110        // We exit Versions mode in case of path change
   111        dispatch(setVersionsModeEnabled({ status: false }));
   112  
   113        let searchPath = decodedInternalPaths;
   114  
   115        if (!decodedInternalPaths.endsWith("/") && decodedInternalPaths !== "") {
   116          searchPath = `${decodedInternalPaths
   117            .split("/")
   118            .slice(0, -1)
   119            .join("/")}/`;
   120        }
   121  
   122        if (searchPath === "/") {
   123          searchPath = "";
   124        }
   125  
   126        // If the path is different of the actual path or reload objects list is requested, then we initialize a new request to load a new record set.
   127        if (
   128          searchPath !== simplePath ||
   129          bucketName !== selectedBucket ||
   130          forceLoad
   131        ) {
   132          dispatch(setRequestInProgress(true));
   133          initWSRequest(searchPath);
   134        }
   135      },
   136      [
   137        internalPaths,
   138        dispatch,
   139        simplePath,
   140        selectedBucket,
   141        bucketName,
   142        initWSRequest,
   143      ],
   144    );
   145  
   146    useEffect(() => {
   147      return () => {
   148        dispatch({ type: "socket/OBCancelLast" });
   149      };
   150    }, [dispatch]);
   151  
   152    // Object Details handler
   153    useEffect(() => {
   154      const decodedIPaths = decodeURLString(internalPaths);
   155  
   156      dispatch(setLoadingVersioning(true));
   157  
   158      if (decodedIPaths.endsWith("/") || decodedIPaths === "") {
   159        dispatch(setObjectDetailsView(false));
   160        dispatch(setSelectedObjectView(null));
   161        dispatch(setLoadingLocking(true));
   162      } else {
   163        dispatch(setLoadingObjectInfo(true));
   164        dispatch(setObjectDetailsView(true));
   165        dispatch(setLoadingVersions(true));
   166        dispatch(
   167          setSelectedObjectView(
   168            `${decodedIPaths ? `${encodeURLString(decodedIPaths)}` : ``}`,
   169          ),
   170        );
   171      }
   172    }, [bucketName, internalPaths, rewindDate, rewindEnabled, dispatch]);
   173  
   174    // Navigation Listing Request
   175    useEffect(() => {
   176      pathLoad(false);
   177    }, [pathLoad]);
   178  
   179    // Reload Handler
   180    useEffect(() => {
   181      if (reloadObjectsList && records.length === 0 && !requestInProgress) {
   182        pathLoad(true);
   183      }
   184    }, [reloadObjectsList, records, requestInProgress, pathLoad]);
   185  
   186    const displayListObjects =
   187      hasPermission(bucketName, [
   188        IAM_SCOPES.S3_LIST_BUCKET,
   189        IAM_SCOPES.S3_ALL_LIST_BUCKET,
   190      ]) || anonymousMode;
   191  
   192    useEffect(() => {
   193      if (loadingVersioning && !anonymousMode) {
   194        if (displayListObjects) {
   195          api.buckets
   196            .getBucketVersioning(bucketName)
   197            .then((res) => {
   198              dispatch(setIsVersioned(res.data));
   199              dispatch(setLoadingVersioning(false));
   200            })
   201            .catch((err) => {
   202              console.error(
   203                "Error Getting Object Versioning Status: ",
   204                err.error.detailedMessage,
   205              );
   206              dispatch(setLoadingVersioning(false));
   207            });
   208        } else {
   209          dispatch(setLoadingVersioning(false));
   210          dispatch(resetMessages());
   211        }
   212      }
   213    }, [
   214      bucketName,
   215      loadingVersioning,
   216      dispatch,
   217      displayListObjects,
   218      anonymousMode,
   219    ]);
   220  
   221    useEffect(() => {
   222      if (loadingLocking) {
   223        if (displayListObjects) {
   224          api.buckets
   225            .getBucketObjectLockingStatus(bucketName)
   226            .then((res) => {
   227              dispatch(setLockingEnabled(res.data.object_locking_enabled));
   228              dispatch(setLoadingLocking(false));
   229            })
   230            .catch((err) => {
   231              console.error(
   232                "Error Getting Object Locking Status: ",
   233                err.error.detailedMessage,
   234              );
   235              dispatch(setLoadingLocking(false));
   236            });
   237        } else {
   238          dispatch(resetMessages());
   239          dispatch(setLoadingLocking(false));
   240        }
   241      }
   242    }, [bucketName, loadingLocking, dispatch, displayListObjects]);
   243  
   244    return (
   245      <Fragment>
   246        {!anonymousMode && <OBHeader bucketName={bucketName} />}
   247        <ListObjects />
   248      </Fragment>
   249    );
   250  };
   251  
   252  export default BrowserHandler;