UNPKG

@uppy/companion

Version:

OAuth helper and remote fetcher for Uppy's (https://uppy.io) extensible file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Dropbox and Google Drive, S3 and more :dog:

151 lines (133 loc) 4.57 kB
import got from 'got' import { prepareStream } from '../../helpers/utils.js' import Provider from '../Provider.js' import { withProviderErrorHandling } from '../providerErrors.js' import adaptData from './adapter.js' const BOX_FILES_FIELDS = 'id,modified_at,name,permissions,size,type' const BOX_THUMBNAIL_SIZE = 256 const getClient = ({ token }) => got.extend({ prefixUrl: 'https://api.box.com/2.0', headers: { authorization: `Bearer ${token}`, }, }) async function getUserInfo({ token }) { return getClient({ token }).get('users/me', { responseType: 'json' }).json() } async function list({ directory, query, token }) { const rootFolderID = '0' return getClient({ token }) .get(`folders/${directory || rootFolderID}/items`, { searchParams: { fields: BOX_FILES_FIELDS, offset: query.cursor, limit: 1000, }, responseType: 'json', }) .json() } /** * Adapter for API https://developer.box.com/reference/ */ export default class Box extends Provider { constructor(options) { super(options) // needed for the thumbnails fetched via companion this.needsCookieAuth = true } static get oauthProvider() { return 'box' } /** * Lists files and folders from Box API * * @param {object} options * @param {string} options.directory * @param {any} options.query * @param {{ accessToken: string }} options.providerUserSession * @param {unknown} options.companion */ async list({ directory, providerUserSession: { accessToken: token }, query, companion, }) { return this.#withErrorHandling('provider.box.list.error', async () => { const [userInfo, files] = await Promise.all([ getUserInfo({ token }), list({ directory, query, token }), ]) return adaptData(files, userInfo.login, companion) }) } async download({ id, providerUserSession: { accessToken: token } }) { return this.#withErrorHandling('provider.box.download.error', async () => { const stream = getClient({ token }).stream.get(`files/${id}/content`, { responseType: 'json', }) const { size } = await prepareStream(stream) return { stream, size } }) } async thumbnail({ id, providerUserSession: { accessToken: token } }) { return this.#withErrorHandling('provider.box.thumbnail.error', async () => { const extension = 'jpg' // you can set this to png to more easily reproduce http 202 retry-after // From box API docs: // Sometimes generating a thumbnail can take a few seconds. // In these situations the API returns a Location-header pointing to a placeholder graphic // for this file type. // The placeholder graphic can be used in a user interface until the thumbnail generation has completed. // The Retry-After-header indicates when to the thumbnail will be ready. // At that time, retry this endpoint to retrieve the thumbnail. // // This can be reproduced more easily by changing extension to png and trying on a newly uploaded image const stream = getClient({ token }).stream.get( `files/${id}/thumbnail.${extension}`, { searchParams: { max_height: BOX_THUMBNAIL_SIZE, max_width: BOX_THUMBNAIL_SIZE, }, responseType: 'json', }, ) await prepareStream(stream) return { stream, contentType: 'image/jpeg' } }) } async size({ id, providerUserSession: { accessToken: token } }) { return this.#withErrorHandling('provider.box.size.error', async () => { const { size } = await getClient({ token }) .get(`files/${id}`, { responseType: 'json' }) .json() return parseInt(size, 10) }) } logout({ companion, providerUserSession: { accessToken: token } }) { return this.#withErrorHandling('provider.box.logout.error', async () => { const { key, secret } = companion.options.providerOptions.box await getClient({ token }).post('oauth2/revoke', { prefixUrl: 'https://api.box.com', form: { client_id: key, client_secret: secret, token, }, responseType: 'json', }) return { revoked: true } }) } async #withErrorHandling(tag, fn) { return withProviderErrorHandling({ fn, tag, providerName: Box.oauthProvider, isAuthError: (response) => response.statusCode === 401, getJsonErrorMessage: (body) => body?.message, }) } }