github.com/minio/console@v1.4.1/web-app/src/screens/Console/Buckets/ListBuckets/Objects/ObjectDetails/ShareFile.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, useEffect, useState } from "react";
    18  import { useSelector } from "react-redux";
    19  import {
    20    Button,
    21    CopyIcon,
    22    ReadBox,
    23    ShareIcon,
    24    Grid,
    25    ProgressBar,
    26    Tooltip,
    27  } from "mds";
    28  import CopyToClipboard from "react-copy-to-clipboard";
    29  import ModalWrapper from "../../../../Common/ModalWrapper/ModalWrapper";
    30  import DaysSelector from "../../../../Common/FormComponents/DaysSelector/DaysSelector";
    31  import {
    32    encodeURLString,
    33    niceTimeFromSeconds,
    34  } from "../../../../../../common/utils";
    35  import {
    36    selDistSet,
    37    setModalErrorSnackMessage,
    38    setModalSnackMessage,
    39  } from "../../../../../../systemSlice";
    40  import { useAppDispatch } from "../../../../../../store";
    41  import { BucketObject } from "api/consoleApi";
    42  import { api } from "api";
    43  import { errorToHandler } from "api/errors";
    44  import { getMaxShareLinkExpTime } from "screens/Console/ObjectBrowser/objectBrowserThunks";
    45  import { maxShareLinkExpTime } from "screens/Console/ObjectBrowser/objectBrowserSlice";
    46  
    47  interface IShareFileProps {
    48    open: boolean;
    49    bucketName: string;
    50    dataObject: BucketObject;
    51    closeModalAndRefresh: () => void;
    52  }
    53  
    54  const ShareFile = ({
    55    open,
    56    closeModalAndRefresh,
    57    bucketName,
    58    dataObject,
    59  }: IShareFileProps) => {
    60    const dispatch = useAppDispatch();
    61    const distributedSetup = useSelector(selDistSet);
    62    const maxshareLinkExpTimeVal = useSelector(maxShareLinkExpTime);
    63    const [shareURL, setShareURL] = useState<string>("");
    64    const [isLoadingVersion, setIsLoadingVersion] = useState<boolean>(true);
    65    const [isLoadingFile, setIsLoadingFile] = useState<boolean>(false);
    66    const [selectedDate, setSelectedDate] = useState<string>("");
    67    const [dateValid, setDateValid] = useState<boolean>(true);
    68    const [versionID, setVersionID] = useState<string>("null");
    69  
    70    const initialDate = new Date();
    71  
    72    const dateChanged = (newDate: string, isValid: boolean) => {
    73      setDateValid(isValid);
    74      if (isValid) {
    75        setSelectedDate(newDate);
    76        return;
    77      }
    78      setSelectedDate("");
    79      setShareURL("");
    80    };
    81  
    82    useEffect(() => {
    83      dispatch(getMaxShareLinkExpTime());
    84    }, [dispatch]);
    85  
    86    useEffect(() => {
    87      // In case version is undefined, we get the latest version of the object
    88      if (dataObject.version_id === undefined) {
    89        // In case it is not distributed setup, then we default to "null";
    90        if (distributedSetup) {
    91          api.buckets
    92            .listObjects(bucketName, {
    93              prefix: encodeURLString(dataObject.name || ""),
    94              with_versions: distributedSetup,
    95            })
    96            .then((res) => {
    97              const result: BucketObject[] = res.data.objects || [];
    98  
    99              const latestVersion: BucketObject | undefined = result.find(
   100                (elem: BucketObject) => elem.is_latest,
   101              );
   102  
   103              if (latestVersion) {
   104                setVersionID(`${latestVersion.version_id}`);
   105                return;
   106              }
   107  
   108              // Version couldn't be retrieved, we default
   109              setVersionID("null");
   110            })
   111            .catch((err) => {
   112              dispatch(setModalErrorSnackMessage(errorToHandler(err.error)));
   113            });
   114  
   115          setIsLoadingVersion(false);
   116          return;
   117        }
   118        setVersionID("null");
   119        setIsLoadingVersion(false);
   120        return;
   121      }
   122      setVersionID(dataObject.version_id || "null");
   123      setIsLoadingVersion(false);
   124    }, [bucketName, dataObject, distributedSetup, dispatch]);
   125  
   126    useEffect(() => {
   127      if (dateValid && !isLoadingVersion) {
   128        setIsLoadingFile(true);
   129        setShareURL("");
   130  
   131        const slDate = new Date(`${selectedDate}`);
   132        const currDate = new Date();
   133  
   134        const diffDate = Math.ceil(
   135          (slDate.getTime() - currDate.getTime()) / 1000,
   136        );
   137  
   138        if (diffDate > 0) {
   139          api.buckets
   140            .shareObject(bucketName, {
   141              prefix: encodeURLString(dataObject.name || ""),
   142              version_id: versionID,
   143              expires: selectedDate !== "" ? `${diffDate}s` : "",
   144            })
   145            .then((res) => {
   146              setShareURL(res.data);
   147              setIsLoadingFile(false);
   148            })
   149            .catch((err) => {
   150              dispatch(setModalErrorSnackMessage(errorToHandler(err.error)));
   151              setShareURL("");
   152              setIsLoadingFile(false);
   153            });
   154        }
   155      }
   156    }, [
   157      dataObject,
   158      selectedDate,
   159      bucketName,
   160      dateValid,
   161      setShareURL,
   162      dispatch,
   163      distributedSetup,
   164      isLoadingVersion,
   165      versionID,
   166    ]);
   167  
   168    return (
   169      <React.Fragment>
   170        <ModalWrapper
   171          title="Share File"
   172          titleIcon={<ShareIcon style={{ fill: "#4CCB92" }} />}
   173          modalOpen={open}
   174          onClose={() => {
   175            closeModalAndRefresh();
   176          }}
   177        >
   178          {isLoadingVersion && (
   179            <Grid item xs={12}>
   180              <ProgressBar />
   181            </Grid>
   182          )}
   183          {!isLoadingVersion && (
   184            <Fragment>
   185              <Grid
   186                item
   187                xs={12}
   188                sx={{
   189                  fontSize: 14,
   190                  fontWeight: 400,
   191                }}
   192              >
   193                <Tooltip
   194                  placement="right"
   195                  tooltip={
   196                    <span>
   197                      You can reset your session by logging out and logging back
   198                      in to the web UI. <br /> <br />
   199                      You can increase the maximum configuration time by setting
   200                      the MINIO_STS_DURATION environment variable on all your
   201                      nodes. <br /> <br />
   202                      You can use <b>mc share</b> as an alternative to this UI,
   203                      where the session length does not limit the URL validity.
   204                    </span>
   205                  }
   206                >
   207                  <span>
   208                    The following URL lets you share this object without requiring
   209                    a login. <br />
   210                    The URL expires automatically at the earlier of your
   211                    configured time ({niceTimeFromSeconds(maxshareLinkExpTimeVal)}
   212                    ) or the expiration of your current web session.
   213                  </span>
   214                </Tooltip>
   215              </Grid>
   216              <br />
   217              <Grid item xs={12}>
   218                <DaysSelector
   219                  initialDate={initialDate}
   220                  id="date"
   221                  label="Active for"
   222                  maxSeconds={maxshareLinkExpTimeVal}
   223                  onChange={dateChanged}
   224                  entity="Link"
   225                />
   226              </Grid>
   227              <Grid
   228                item
   229                xs={12}
   230                sx={{
   231                  marginBottom: 10,
   232                }}
   233              >
   234                <ReadBox
   235                  actionButton={
   236                    <CopyToClipboard text={shareURL}>
   237                      <Button
   238                        id={"copy-path"}
   239                        variant="regular"
   240                        onClick={() => {
   241                          dispatch(
   242                            setModalSnackMessage("Share URL Copied to clipboard"),
   243                          );
   244                        }}
   245                        disabled={shareURL === "" || isLoadingFile}
   246                        style={{
   247                          width: "28px",
   248                          height: "28px",
   249                          padding: "0px",
   250                        }}
   251                        icon={<CopyIcon />}
   252                      />
   253                    </CopyToClipboard>
   254                  }
   255                >
   256                  {shareURL}
   257                </ReadBox>
   258              </Grid>
   259            </Fragment>
   260          )}
   261        </ModalWrapper>
   262      </React.Fragment>
   263    );
   264  };
   265  
   266  export default ShareFile;