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;