storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/browser/app/js/objects/actions.js (about) 1 /* 2 * MinIO Cloud Storage (C) 2018 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 import web from "../web" 18 import history from "../history" 19 import { 20 sortObjectsByName, 21 sortObjectsBySize, 22 sortObjectsByDate, 23 } from "../utils" 24 import { getCurrentBucket } from "../buckets/selectors" 25 import { getCurrentPrefix, getCheckedList } from "./selectors" 26 import * as alertActions from "../alert/actions" 27 import { 28 minioBrowserPrefix, 29 SORT_BY_NAME, 30 SORT_BY_SIZE, 31 SORT_BY_LAST_MODIFIED, 32 SORT_ORDER_ASC, 33 SORT_ORDER_DESC, 34 } from "../constants" 35 import { getServerInfo, hasServerPublicDomain } from '../browser/selectors' 36 37 export const SET_LIST = "objects/SET_LIST" 38 export const RESET_LIST = "objects/RESET_LIST" 39 export const SET_FILTER = "objects/SET_FILTER" 40 export const APPEND_LIST = "objects/APPEND_LIST" 41 export const REMOVE = "objects/REMOVE" 42 export const SET_SORT_BY = "objects/SET_SORT_BY" 43 export const SET_SORT_ORDER = "objects/SET_SORT_ORDER" 44 export const SET_CURRENT_PREFIX = "objects/SET_CURRENT_PREFIX" 45 export const SET_PREFIX_WRITABLE = "objects/SET_PREFIX_WRITABLE" 46 export const SET_SHARE_OBJECT = "objects/SET_SHARE_OBJECT" 47 export const CHECKED_LIST_ADD = "objects/CHECKED_LIST_ADD" 48 export const CHECKED_LIST_REMOVE = "objects/CHECKED_LIST_REMOVE" 49 export const CHECKED_LIST_RESET = "objects/CHECKED_LIST_RESET" 50 export const SET_LIST_LOADING = "objects/SET_LIST_LOADING" 51 52 export const setList = (objects) => ({ 53 type: SET_LIST, 54 objects, 55 }) 56 57 export const resetList = () => ({ 58 type: RESET_LIST, 59 }) 60 61 export const setFilter = filter => { 62 return { 63 type: SET_FILTER, 64 filter 65 } 66 } 67 68 export const setListLoading = (listLoading) => ({ 69 type: SET_LIST_LOADING, 70 listLoading, 71 }) 72 73 export const fetchObjects = () => { 74 return function (dispatch, getState) { 75 dispatch(resetList()) 76 const { 77 buckets: { currentBucket }, 78 objects: { currentPrefix }, 79 } = getState() 80 if (currentBucket) { 81 dispatch(setListLoading(true)) 82 return web 83 .ListObjects({ 84 bucketName: currentBucket, 85 prefix: currentPrefix, 86 }) 87 .then((res) => { 88 // we need to check if the bucket name and prefix are the same as 89 // when the request was made before updating the displayed objects 90 if ( 91 currentBucket === getCurrentBucket(getState()) && 92 currentPrefix === getCurrentPrefix(getState()) 93 ) { 94 let objects = [] 95 if (res.objects) { 96 objects = res.objects.map((object) => { 97 return { 98 ...object, 99 name: object.name.replace(currentPrefix, ""), 100 } 101 }) 102 } 103 104 const sortBy = SORT_BY_LAST_MODIFIED 105 const sortOrder = SORT_ORDER_DESC 106 dispatch(setSortBy(sortBy)) 107 dispatch(setSortOrder(sortOrder)) 108 const sortedList = sortObjectsList(objects, sortBy, sortOrder) 109 dispatch(setList(sortedList)) 110 111 dispatch(setPrefixWritable(res.writable)) 112 dispatch(setListLoading(false)) 113 } 114 }) 115 .catch((err) => { 116 if (web.LoggedIn()) { 117 dispatch( 118 alertActions.set({ 119 type: "danger", 120 message: err.message, 121 autoClear: true, 122 }) 123 ) 124 dispatch(resetList()) 125 } else { 126 history.push("/login") 127 } 128 dispatch(setListLoading(false)) 129 }) 130 } 131 } 132 } 133 134 export const sortObjects = (sortBy) => { 135 return function (dispatch, getState) { 136 const { objects } = getState() 137 let sortOrder = SORT_ORDER_ASC 138 // Reverse sort order if the list is already sorted on same field 139 if (objects.sortBy === sortBy && objects.sortOrder === SORT_ORDER_ASC) { 140 sortOrder = SORT_ORDER_DESC 141 } 142 dispatch(setSortBy(sortBy)) 143 dispatch(setSortOrder(sortOrder)) 144 const sortedList = sortObjectsList(objects.list, sortBy, sortOrder) 145 dispatch(setList(sortedList)) 146 } 147 } 148 149 const sortObjectsList = (list, sortBy, sortOrder) => { 150 switch (sortBy) { 151 case SORT_BY_NAME: 152 return sortObjectsByName(list, sortOrder) 153 case SORT_BY_SIZE: 154 return sortObjectsBySize(list, sortOrder) 155 case SORT_BY_LAST_MODIFIED: 156 return sortObjectsByDate(list, sortOrder) 157 } 158 } 159 160 export const setSortBy = (sortBy) => ({ 161 type: SET_SORT_BY, 162 sortBy, 163 }) 164 165 export const setSortOrder = (sortOrder) => ({ 166 type: SET_SORT_ORDER, 167 sortOrder, 168 }) 169 170 export const selectPrefix = (prefix) => { 171 return function (dispatch, getState) { 172 dispatch(setCurrentPrefix(prefix)) 173 dispatch(fetchObjects()) 174 dispatch(resetCheckedList()) 175 const currentBucket = getCurrentBucket(getState()) 176 history.replace(`/${currentBucket}/${prefix}`) 177 } 178 } 179 180 export const setCurrentPrefix = (prefix) => { 181 return { 182 type: SET_CURRENT_PREFIX, 183 prefix, 184 } 185 } 186 187 export const setPrefixWritable = (prefixWritable) => ({ 188 type: SET_PREFIX_WRITABLE, 189 prefixWritable, 190 }) 191 192 export const deleteObject = (object) => { 193 return function (dispatch, getState) { 194 const currentBucket = getCurrentBucket(getState()) 195 const currentPrefix = getCurrentPrefix(getState()) 196 const objectName = `${currentPrefix}${object}` 197 return web 198 .RemoveObject({ 199 bucketName: currentBucket, 200 objects: [objectName], 201 }) 202 .then(() => { 203 dispatch(removeObject(object)) 204 }) 205 .catch((e) => { 206 dispatch( 207 alertActions.set({ 208 type: "danger", 209 message: e.message, 210 }) 211 ) 212 }) 213 } 214 } 215 216 export const removeObject = (object) => ({ 217 type: REMOVE, 218 object, 219 }) 220 221 export const deleteCheckedObjects = () => { 222 return function (dispatch, getState) { 223 const checkedObjects = getCheckedList(getState()) 224 for (let i = 0; i < checkedObjects.length; i++) { 225 dispatch(deleteObject(checkedObjects[i])) 226 } 227 dispatch(resetCheckedList()) 228 } 229 } 230 231 export const shareObject = (object, days, hours, minutes) => { 232 return function (dispatch, getState) { 233 const hasServerDomain = hasServerPublicDomain(getState()) 234 const currentBucket = getCurrentBucket(getState()) 235 const currentPrefix = getCurrentPrefix(getState()) 236 const objectName = `${currentPrefix}${object}` 237 const expiry = days * 24 * 60 * 60 + hours * 60 * 60 + minutes * 60 238 if (web.LoggedIn()) { 239 return web 240 .GetBucketPolicy({ bucketName: currentBucket, prefix: currentPrefix }) 241 .catch(() => ({ policy: null })) 242 .then(({ policy }) => { 243 if (hasServerDomain && ['readonly', 'readwrite'].includes(policy)) { 244 const domain = getServerInfo(getState()).info.domains[0] 245 const url = `${domain}/${currentBucket}/${encodeURI(objectName)}` 246 dispatch(showShareObject(object, url, false)) 247 dispatch( 248 alertActions.set({ 249 type: "success", 250 message: "Object shared." 251 }) 252 ) 253 } else { 254 return web 255 .PresignedGet({ 256 host: location.host, 257 bucket: currentBucket, 258 object: objectName, 259 expiry: expiry 260 }) 261 } 262 }) 263 .then((obj) => { 264 if (!obj) return 265 dispatch(showShareObject(object, obj.url)) 266 dispatch( 267 alertActions.set({ 268 type: "success", 269 message: `Object shared. Expires in ${days} days ${hours} hours ${minutes} minutes`, 270 }) 271 ) 272 }) 273 .catch((err) => { 274 dispatch( 275 alertActions.set({ 276 type: "danger", 277 message: err.message, 278 }) 279 ) 280 }) 281 } else { 282 dispatch( 283 showShareObject( 284 object, 285 `${location.host}` + 286 "/" + 287 `${currentBucket}` + 288 "/" + 289 encodeURI(objectName) 290 ) 291 ) 292 dispatch( 293 alertActions.set({ 294 type: "success", 295 message: `Object shared.`, 296 }) 297 ) 298 } 299 } 300 } 301 302 export const showShareObject = (object, url, showExpiryDate = true) => ({ 303 type: SET_SHARE_OBJECT, 304 show: true, 305 object, 306 url, 307 showExpiryDate, 308 }) 309 310 export const hideShareObject = (object, url) => ({ 311 type: SET_SHARE_OBJECT, 312 show: false, 313 object: "", 314 url: "", 315 }) 316 export const getObjectURL = (object, callback) => { 317 return function (dispatch, getState) { 318 const currentBucket = getCurrentBucket(getState()) 319 const currentPrefix = getCurrentPrefix(getState()) 320 const objectName = `${currentPrefix}${object}` 321 const encObjectName = encodeURI(objectName) 322 if (web.LoggedIn()) { 323 return web 324 .CreateURLToken() 325 .then((res) => { 326 const url = `${window.location.origin}${minioBrowserPrefix}/download/${currentBucket}/${encObjectName}?token=${res.token}` 327 callback(url) 328 }) 329 .catch((err) => { 330 dispatch( 331 alertActions.set({ 332 type: "danger", 333 message: err.message, 334 }) 335 ) 336 }) 337 } else { 338 const url = `${window.location.origin}${minioBrowserPrefix}/download/${currentBucket}/${encObjectName}?token=` 339 callback(url) 340 } 341 } 342 } 343 export const downloadObject = (object) => { 344 return function (dispatch, getState) { 345 const currentBucket = getCurrentBucket(getState()) 346 const currentPrefix = getCurrentPrefix(getState()) 347 const objectName = `${currentPrefix}${object}` 348 const encObjectName = encodeURI(objectName) 349 if (web.LoggedIn()) { 350 return web 351 .CreateURLToken() 352 .then((res) => { 353 const url = `${window.location.origin}${minioBrowserPrefix}/download/${currentBucket}/${encObjectName}?token=${res.token}` 354 window.location = url 355 }) 356 .catch((err) => { 357 dispatch( 358 alertActions.set({ 359 type: "danger", 360 message: err.message, 361 }) 362 ) 363 }) 364 } else { 365 const url = `${window.location.origin}${minioBrowserPrefix}/download/${currentBucket}/${encObjectName}?token=` 366 window.location = url 367 } 368 } 369 } 370 371 export const downloadPrefix = (object) => { 372 return function (dispatch, getState) { 373 return downloadObjects( 374 getCurrentBucket(getState()), 375 getCurrentPrefix(getState()), 376 [object], 377 `${object.slice(0, -1)}.zip`, 378 dispatch 379 ) 380 } 381 } 382 383 384 export const checkObject = (object) => ({ 385 type: CHECKED_LIST_ADD, 386 object, 387 }) 388 389 export const uncheckObject = (object) => ({ 390 type: CHECKED_LIST_REMOVE, 391 object, 392 }) 393 394 export const resetCheckedList = () => ({ 395 type: CHECKED_LIST_RESET, 396 }) 397 398 export const downloadCheckedObjects = () => { 399 return function (dispatch, getState) { 400 return downloadObjects( 401 getCurrentBucket(getState()), 402 getCurrentPrefix(getState()), 403 getCheckedList(getState()), 404 null, 405 dispatch 406 ) 407 } 408 } 409 410 const downloadObjects = (bucketName, prefix, objects, filename, dispatch) => { 411 const req = { 412 bucketName: bucketName, 413 prefix: prefix, 414 objects: objects, 415 } 416 if (web.LoggedIn()) { 417 return web 418 .CreateURLToken() 419 .then((res) => { 420 const requestUrl = `${location.origin}${minioBrowserPrefix}/zip?token=${res.token}` 421 downloadZip(requestUrl, req, filename, dispatch) 422 }) 423 .catch((err) => 424 dispatch( 425 alertActions.set({ 426 type: "danger", 427 message: err.message, 428 }) 429 ) 430 ) 431 } else { 432 const requestUrl = `${location.origin}${minioBrowserPrefix}/zip?token=` 433 downloadZip(requestUrl, req, filename, dispatch) 434 } 435 } 436 437 const downloadZip = (url, req, filename, dispatch) => { 438 var anchor = document.createElement("a") 439 document.body.appendChild(anchor) 440 441 var xhr = new XMLHttpRequest() 442 xhr.open("POST", url, true) 443 xhr.responseType = "blob" 444 445 xhr.onload = function (e) { 446 if (this.status == 200) { 447 dispatch(resetCheckedList()) 448 var blob = new Blob([this.response], { 449 type: "octet/stream", 450 }) 451 var blobUrl = window.URL.createObjectURL(blob) 452 var separator = req.prefix.length > 1 ? "-" : "" 453 454 anchor.href = blobUrl 455 anchor.download = filename || 456 req.bucketName + separator + req.prefix.slice(0, -1) + ".zip" 457 458 anchor.click() 459 window.URL.revokeObjectURL(blobUrl) 460 anchor.remove() 461 } 462 } 463 xhr.send(JSON.stringify(req)) 464 }