Explorar o código

Merge pull request #824 from transloadit/cookieless-server-token

refactor: use local storage to store provider token
Ifedapo .A. Olarewaju %!s(int64=6) %!d(string=hai) anos
pai
achega
c2b5f0b66a

+ 5 - 0
src/plugins/Instagram/index.js

@@ -84,6 +84,11 @@ module.exports = class Instagram extends Plugin {
   }
 
   getItemIcon (item) {
+    if (!item.images) {
+      return <svg viewBox="0 0 58 58" opacity="0.6">
+        <path d="M36.537 28.156l-11-7a1.005 1.005 0 0 0-1.02-.033C24.2 21.3 24 21.635 24 22v14a1 1 0 0 0 1.537.844l11-7a1.002 1.002 0 0 0 0-1.688zM26 34.18V23.82L34.137 29 26 34.18z" /><path d="M57 6H1a1 1 0 0 0-1 1v44a1 1 0 0 0 1 1h56a1 1 0 0 0 1-1V7a1 1 0 0 0-1-1zM10 28H2v-9h8v9zm-8 2h8v9H2v-9zm10 10V8h34v42H12V40zm44-12h-8v-9h8v9zm-8 2h8v9h-8v-9zm8-22v9h-8V8h8zM2 8h8v9H2V8zm0 42v-9h8v9H2zm54 0h-8v-9h8v9z" />
+      </svg>
+    }
     return <img src={item.images.thumbnail.url} />
   }
 

+ 10 - 19
src/plugins/Tus.js

@@ -1,6 +1,7 @@
 const Plugin = require('../core/Plugin')
 const tus = require('tus-js-client')
 const UppySocket = require('../core/UppySocket')
+const Provider = require('../server/Provider')
 const {
   emitSocketProgress,
   getSocketHost,
@@ -238,31 +239,21 @@ module.exports = class Tus extends Plugin {
           .catch(reject)
       }
 
-      fetch(file.remote.url, {
-        method: 'post',
-        credentials: 'include',
-        headers: {
-          'Accept': 'application/json',
-          'Content-Type': 'application/json'
-        },
-        body: JSON.stringify(Object.assign({}, file.remote.body, {
+      this.uppy.emit('upload-started', file)
+      const provider = new Provider(this.uppy, file.remote.providerOptions)
+      provider.post(
+        file.remote.url,
+        Object.assign({}, file.remote.body, {
           endpoint: opts.endpoint,
           uploadUrl: opts.uploadUrl,
           protocol: 'tus',
           size: file.data.size,
           metadata: file.meta
-        }))
-      })
-      .then((res) => {
-        if (res.status < 200 || res.status > 300) {
-          return reject(res.statusText)
-        }
-
-        return res.json().then((data) => {
-          this.uppy.setFileState(file.id, { serverToken: data.token })
-          file = this.uppy.getFile(file.id)
-          return file
         })
+      ).then((res) => {
+        this.uppy.setFileState(file.id, { serverToken: res.token })
+        file = this.uppy.getFile(file.id)
+        return file
       })
       .then((file) => {
         return this.connectToServerSocket(file)

+ 0 - 1
src/plugins/Url/index.js

@@ -4,7 +4,6 @@ const { h } = require('preact')
 const { RequestClient } = require('../../server')
 const UrlUI = require('./UrlUI.js')
 const { toArray } = require('../../core/Utils')
-require('whatwg-fetch')
 
 /**
  * Url

+ 25 - 34
src/plugins/XHRUpload.js

@@ -2,6 +2,7 @@ const Plugin = require('../core/Plugin')
 const cuid = require('cuid')
 const Translator = require('../core/Translator')
 const UppySocket = require('../core/UppySocket')
+const Provider = require('../server/Provider')
 const {
   emitSocketProgress,
   getSocketHost,
@@ -310,49 +311,39 @@ module.exports = class XHRUpload extends Plugin {
         fields[name] = file.meta[name]
       })
 
-      fetch(file.remote.url, {
-        method: 'post',
-        credentials: 'include',
-        headers: {
-          'Accept': 'application/json',
-          'Content-Type': 'application/json'
-        },
-        body: JSON.stringify(Object.assign({}, file.remote.body, {
+      const provider = new Provider(this.uppy, file.remote.providerOptions)
+      provider.post(
+        file.remote.url,
+        Object.assign({}, file.remote.body, {
           endpoint: opts.endpoint,
           size: file.data.size,
           fieldname: opts.fieldName,
           metadata: fields,
           headers: opts.headers
-        }))
-      })
+        })
+      )
       .then((res) => {
-        if (res.status < 200 && res.status > 300) {
-          return reject(res.statusText)
-        }
+        const token = res.token
+        const host = getSocketHost(file.remote.host)
+        const socket = new UppySocket({ target: `${host}/api/${token}` })
 
-        res.json().then((data) => {
-          const token = data.token
-          const host = getSocketHost(file.remote.host)
-          const socket = new UppySocket({ target: `${host}/api/${token}` })
+        socket.on('progress', (progressData) => emitSocketProgress(this, progressData, file))
 
-          socket.on('progress', (progressData) => emitSocketProgress(this, progressData, file))
-
-          socket.on('success', (data) => {
-            const resp = opts.getResponseData(data.response.responseText, data.response)
-            const uploadURL = resp[opts.responseUrlFieldName]
-            this.uppy.emit('upload-success', file, resp, uploadURL)
-            socket.close()
-            return resolve()
-          })
+        socket.on('success', (data) => {
+          const resp = opts.getResponseData(data.response.responseText, data.response)
+          const uploadURL = resp[opts.responseUrlFieldName]
+          this.uppy.emit('upload-success', file, resp, uploadURL)
+          socket.close()
+          return resolve()
+        })
 
-          socket.on('error', (errData) => {
-            const resp = errData.response
-            const error = resp
-              ? opts.getResponseError(resp.responseText, resp)
-              : Object.assign(new Error(errData.error.message), { cause: errData.error })
-            this.uppy.emit('upload-error', file, error)
-            reject(error)
-          })
+        socket.on('error', (errData) => {
+          const resp = errData.response
+          const error = resp
+            ? opts.getResponseError(resp.responseText, resp)
+            : Object.assign(new Error(errData.error.message), { cause: errData.error })
+          this.uppy.emit('upload-error', file, error)
+          reject(error)
         })
       })
     })

+ 15 - 0
src/server/Provider.js

@@ -14,6 +14,17 @@ module.exports = class Provider extends RequestClient {
     this.id = this.provider
     this.authProvider = opts.authProvider || this.provider
     this.name = this.opts.name || _getName(this.id)
+    this.tokenKey = `uppy-server-${this.id}-auth-token`
+  }
+
+  get defaultHeaders () {
+    return Object.assign({}, super.defaultHeaders, {'uppy-auth-token': localStorage.getItem(this.tokenKey)})
+  }
+
+  // @todo(i.olarewaju) consider whether or not this method should be exposed
+  setAuthToken (token) {
+    // @todo(i.olarewaju) add fallback for OOM storage
+    localStorage.setItem(this.tokenKey, token)
   }
 
   checkAuth () {
@@ -37,5 +48,9 @@ module.exports = class Provider extends RequestClient {
 
   logout (redirect = location.href) {
     return this.get(`${this.id}/logout?redirect=${redirect}`)
+      .then((res) => {
+        localStorage.removeItem(this.tokenKey)
+        return res
+      })
   }
 }

+ 24 - 14
src/server/RequestClient.js

@@ -15,6 +15,13 @@ module.exports = class RequestClient {
     return uppyServer && uppyServer[host] ? uppyServer[host] : host
   }
 
+  get defaultHeaders () {
+    return {
+      'Accept': 'application/json',
+      'Content-Type': 'application/json'
+    }
+  }
+
   onReceiveResponse (response) {
     const state = this.uppy.getState()
     const uppyServer = state.uppyServer || {}
@@ -32,13 +39,9 @@ module.exports = class RequestClient {
   }
 
   get (path) {
-    return fetch(`${this.hostname}/${path}`, {
+    return fetch(this._getUrl(path), {
       method: 'get',
-      credentials: 'include',
-      headers: {
-        'Accept': 'application/json',
-        'Content-Type': 'application/json'
-      }
+      headers: this.defaultHeaders
     })
       // @todo validate response status before calling json
       .then(this.onReceiveResponse)
@@ -46,18 +49,25 @@ module.exports = class RequestClient {
   }
 
   post (path, data) {
-    return fetch(`${this.hostname}/${path}`, {
+    return fetch(this._getUrl(path), {
       method: 'post',
-      credentials: 'include',
-      headers: {
-        'Accept': 'application/json',
-        'Content-Type': 'application/json'
-      },
+      headers: this.defaultHeaders,
       body: JSON.stringify(data)
     })
       .then(this.onReceiveResponse)
-      // @todo validate response status before calling json
-      .then((res) => res.json())
+      .then((res) => {
+        if (res.status < 200 || res.status > 300) {
+          throw new Error(res.statusText)
+        }
+        return res.json()
+      })
+  }
+
+  _getUrl (url) {
+    if (/^https?:/.test(url)) {
+      return url
+    }
+    return `${this.hostname}/${url}`
   }
 
   delete (path, data) {

+ 11 - 25
src/views/ProviderView/index.js

@@ -186,7 +186,8 @@ module.exports = class ProviderView {
         url: `${this.Provider.fileUrl(this.plugin.getItemRequestPath(file))}`,
         body: {
           fileId: this.plugin.getItemId(file)
-        }
+        },
+        providerOptions: this.Provider.opts
       }
     }
 
@@ -446,35 +447,20 @@ module.exports = class ProviderView {
   }
 
   handleAuth () {
-    const urlId = Math.floor(Math.random() * 999999) + 1
-    const redirect = `${location.href}${location.search ? '&' : '?'}id=${urlId}`
-
-    const authState = btoa(JSON.stringify({ redirect }))
+    const authState = btoa(JSON.stringify({ origin: location.origin }))
     const link = `${this.Provider.authUrl()}?state=${authState}`
 
     const authWindow = window.open(link, '_blank')
-    authWindow.opener = null
-    const checkAuth = () => {
-      let authWindowUrl
-
-      try {
-        authWindowUrl = authWindow.location.href
-      } catch (e) {
-        if (e instanceof DOMException || e instanceof TypeError) {
-          return setTimeout(checkAuth, 100)
-        } else throw e
-      }
-
-      // split url because chrome adds '#' to redirects
-      if (authWindowUrl && authWindowUrl.split('#')[0] === redirect) {
-        authWindow.close()
-        this._loaderWrapper(this.Provider.checkAuth(), this.plugin.onAuth, this.handleError)
-      } else {
-        setTimeout(checkAuth, 100)
+    const handleToken = (e) => {
+      if (e.origin !== this.plugin.opts.host || e.source !== authWindow) {
+        return
       }
+      authWindow.close()
+      window.removeEventListener('message', handleToken)
+      this.Provider.setAuthToken(e.data.token)
+      this._loaderWrapper(this.Provider.checkAuth(), this.plugin.onAuth, this.handleError)
     }
-
-    checkAuth()
+    window.addEventListener('message', handleToken)
   }
 
   handleError (error) {