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  }