Skip to content

Commit

Permalink
@uppy/companion: bump Node.js version support matrix (#5035)
Browse files Browse the repository at this point in the history
Co-authored-by: Mikael Finstad <finstaden@gmail.com>
Co-authored-by: Merlijn Vos <merlijn@soverin.net>
  • Loading branch information
3 people authored Apr 22, 2024
1 parent 915a563 commit a565c3e
Show file tree
Hide file tree
Showing 22 changed files with 1,476 additions and 1,224 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/companion.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
node-version: [18.x, 20.x, latest]
steps:
- name: Checkout sources
uses: actions/checkout@v3
Expand Down
23 changes: 10 additions & 13 deletions packages/@uppy/companion/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,10 @@
"@aws-sdk/lib-storage": "^3.338.0",
"@aws-sdk/s3-presigned-post": "^3.338.0",
"@aws-sdk/s3-request-presigner": "^3.338.0",
"atob": "2.1.2",
"body-parser": "1.20.0",
"body-parser": "1.20.2",
"chalk": "4.1.2",
"common-tags": "1.8.2",
"connect-redis": "7.1.0",
"connect-redis": "7.1.1",
"content-disposition": "^0.5.4",
"cookie-parser": "1.4.6",
"cors": "^2.8.5",
Expand All @@ -48,26 +47,25 @@
"express-prom-bundle": "6.5.0",
"express-request-id": "1.4.1",
"express-session": "1.17.3",
"form-data": "^3.0.0",
"got": "11",
"grant": "5.4.21",
"got": "^13.0.0",
"grant": "5.4.22",
"helmet": "^4.6.0",
"ipaddr.js": "^2.0.1",
"jsonwebtoken": "9.0.0",
"jsonwebtoken": "9.0.2",
"lodash": "^4.17.21",
"mime-types": "2.1.35",
"moment": "^2.29.2",
"moment-timezone": "^0.5.31",
"morgan": "1.10.0",
"ms": "2.1.3",
"node-schedule": "2.1.0",
"node-schedule": "2.1.1",
"prom-client": "14.0.1",
"redis": "4.2.0",
"redis": "4.6.13",
"serialize-error": "^2.1.0",
"serialize-javascript": "^6.0.0",
"tus-js-client": "^3.1.3",
"validator": "^13.0.0",
"ws": "8.8.1"
"ws": "8.16.0"
},
"devDependencies": {
"@types/compression": "1.7.0",
Expand All @@ -84,7 +82,6 @@
"@types/request": "2.48.8",
"@types/webpack": "^5.28.0",
"@types/ws": "8.5.3",
"into-stream": "^6.0.0",
"jest": "^29.0.0",
"nock": "^13.1.3",
"supertest": "6.2.4",
Expand All @@ -109,10 +106,10 @@
"deploy": "kubectl apply -f infra/kube/companion-kube.yml",
"prepublishOnly": "yarn run build",
"start": "node ./lib/standalone/start-server.js",
"test": "jest"
"test": "NODE_OPTIONS=--experimental-vm-modules jest --runInBand"
},
"engines": {
"node": "^14.19.0 || ^16.15.0 || >=18.0.0"
"node": "^18.20.0 || ^20.10.0 || >=22.0.0"
},
"installConfig": {
"hoistingLimits": "workspaces"
Expand Down
34 changes: 24 additions & 10 deletions packages/@uppy/companion/src/server/Uploader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@
const tus = require('tus-js-client')
const { randomUUID } = require('node:crypto')
const validator = require('validator')
const got = require('got').default
const { pipeline: pipelineCb } = require('node:stream')
const { join } = require('node:path')
const fs = require('node:fs')
const { promisify } = require('node:util')
const FormData = require('form-data')
const throttle = require('lodash/throttle')

const { Upload } = require('@aws-sdk/lib-storage')

const { rfc2047EncodeMetadata, getBucket } = require('./helpers/utils')

const got = require('./got')

// TODO move to `require('streams/promises').pipeline` when dropping support for Node.js 14.x.
const pipeline = promisify(pipelineCb)

Expand Down Expand Up @@ -142,6 +142,21 @@ const states = {
done: 'done',
}

class StreamableBlob {
#stream

constructor(stream) {
this.#stream = stream
}

stream(){
return this.#stream
}

// eslint-disable-next-line class-methods-use-this
get [Symbol.toStringTag]() { return "File" }
}

class Uploader {
/**
* Uploads file to destination based on the supplied protocol (tus, s3-multipart, multipart)
Expand Down Expand Up @@ -613,16 +628,15 @@ class Uploader {
}

if (this.options.useFormData) {
// todo refactor once upgraded to got 12
const formData = new FormData()

Object.entries(this.options.metadata).forEach(([key, value]) => formData.append(key, value))

formData.append(this.options.fieldname, stream, {
filename: this.uploadFileName,
contentType: this.options.metadata.type,
knownLength: this.size,
})
formData.append(
this.options.fieldname,
// @ts-expect-error Our StreamableBlob is actually spec compliant enough for our purpose
new StreamableBlob(stream),
this.uploadFileName)

reqOptions.body = formData
} else {
Expand All @@ -632,7 +646,7 @@ class Uploader {

try {
const httpMethod = (this.options.httpMethod || '').toUpperCase() === 'PUT' ? 'put' : 'post'
const runRequest = got[httpMethod]
const runRequest = (await got)[httpMethod]

const response = await runRequest(url, reqOptions)

Expand Down Expand Up @@ -662,7 +676,7 @@ class Uploader {
extraData: getRespObj(err.response),
})
}
throw new Error('Unknown multipart upload error')
throw new Error('Unknown multipart upload error', {cause: err})
}
}

Expand Down
1 change: 0 additions & 1 deletion packages/@uppy/companion/src/server/controllers/connect.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
const atob = require('atob')
const oAuthState = require('../helpers/oauth-state')

const queryString = (params, prefix = '?') => {
Expand Down
2 changes: 1 addition & 1 deletion packages/@uppy/companion/src/server/controllers/url.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const downloadURL = async (url, blockLocalIPs, traceId) => {
// TODO in next major, rename all blockLocalIPs to allowLocalUrls and invert the bool, to make it consistent
// see discussion https://github.com/transloadit/uppy/pull/4554/files#r1268677162
try {
const protectedGot = getProtectedGot({ blockLocalIPs })
const protectedGot = await getProtectedGot({ blockLocalIPs })
const stream = protectedGot.stream.get(url, { responseType: 'json' })
await prepareStream(stream)
return stream
Expand Down
4 changes: 4 additions & 0 deletions packages/@uppy/companion/src/server/got.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// eslint-disable-next-line import/no-unresolved
const gotPromise = import('got')

module.exports = gotPromise.then((got) => got.default)
1 change: 0 additions & 1 deletion packages/@uppy/companion/src/server/helpers/oauth-state.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const crypto = require('node:crypto')
const atob = require('atob')
const { encrypt, decrypt } = require('./utils')

module.exports.encodeState = (state, secret) => {
Expand Down
9 changes: 5 additions & 4 deletions packages/@uppy/companion/src/server/helpers/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ const http = require('node:http')
const https = require('node:https')
const dns = require('node:dns')
const ipaddr = require('ipaddr.js')
const got = require('got').default
const path = require('node:path')
const contentDisposition = require('content-disposition')
const validator = require('validator')

const got = require('../got')

const FORBIDDEN_IP_ADDRESS = 'Forbidden IP address'

// Example scary IPs that should return false (ipv6-to-ipv4 mapped):
Expand Down Expand Up @@ -84,15 +85,15 @@ const getProtectedHttpAgent = ({ protocol, blockLocalIPs }) => {

module.exports.getProtectedHttpAgent = getProtectedHttpAgent

function getProtectedGot ({ blockLocalIPs }) {
async function getProtectedGot ({ blockLocalIPs }) {
const HttpAgent = getProtectedHttpAgent({ protocol: 'http', blockLocalIPs })
const HttpsAgent = getProtectedHttpAgent({ protocol: 'https', blockLocalIPs })
const httpAgent = new HttpAgent()
const httpsAgent = new HttpsAgent()


// @ts-ignore
return got.extend({ agent: { http: httpAgent, https: httpsAgent } })
return (await got).extend({ agent: { http: httpAgent, https: httpsAgent } })
}

module.exports.getProtectedGot = getProtectedGot
Expand All @@ -106,7 +107,7 @@ module.exports.getProtectedGot = getProtectedGot
*/
exports.getURLMeta = async (url, blockLocalIPs = false) => {
async function requestWithMethod (method) {
const protectedGot = getProtectedGot({ blockLocalIPs })
const protectedGot = await getProtectedGot({ blockLocalIPs })
const stream = protectedGot.stream(url, { method, throwHttpErrors: false })

return new Promise((resolve, reject) => (
Expand Down
5 changes: 3 additions & 2 deletions packages/@uppy/companion/src/server/jobs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ const schedule = require('node-schedule')
const fs = require('node:fs')
const path = require('node:path')
const { promisify } = require('node:util')
const got = require('got').default

const got = require('./got')

const { FILE_NAME_PREFIX } = require('./Uploader')
const logger = require('./logger')
Expand Down Expand Up @@ -65,7 +66,7 @@ async function runPeriodicPing ({ urls, payload, requestTimeout }) {
// Run requests in parallel
await Promise.all(urls.map(async (url) => {
try {
await got.post(url, { json: payload, timeout: { request: requestTimeout } })
await (await got).post(url, { json: payload, timeout: { request: requestTimeout } })
} catch (err) {
logger.warn(err, 'jobs.periodic.ping')
}
Expand Down
19 changes: 9 additions & 10 deletions packages/@uppy/companion/src/server/provider/box/index.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
const got = require('got').default

const Provider = require('../Provider')
const adaptData = require('./adapter')
const { withProviderErrorHandling } = require('../providerErrors')
const { prepareStream } = require('../../helpers/utils')

const got = require('../../got')

const BOX_FILES_FIELDS = 'id,modified_at,name,permissions,size,type'
const BOX_THUMBNAIL_SIZE = 256

const getClient = ({ token }) => got.extend({
const getClient = async ({ token }) => (await 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()
return (await getClient({ token })).get('users/me', { responseType: 'json' }).json()
}

async function list ({ directory, query, token }) {
const rootFolderID = '0'
// https://developer.box.com/reference/resources/items/
return getClient({ token }).get(`folders/${directory || rootFolderID}/items`, { searchParams: { fields: BOX_FILES_FIELDS, offset: query.cursor, limit: 1000 }, responseType: 'json' }).json()
return (await getClient({ token })).get(`folders/${directory || rootFolderID}/items`, { searchParams: { fields: BOX_FILES_FIELDS, offset: query.cursor, limit: 1000 }, responseType: 'json' }).json()
}

/**
Expand Down Expand Up @@ -61,7 +60,7 @@ class Box extends Provider {

async download ({ id, token }) {
return this.#withErrorHandling('provider.box.download.error', async () => {
const stream = getClient({ token }).stream.get(`files/${id}/content`, { responseType: 'json' })
const stream = (await getClient({ token })).stream.get(`files/${id}/content`, { responseType: 'json' })

await prepareStream(stream)
return { stream }
Expand All @@ -81,7 +80,7 @@ class Box extends Provider {
// 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}`, {
const stream = (await getClient({ token })).stream.get(`files/${id}/thumbnail.${extension}`, {
searchParams: { max_height: BOX_THUMBNAIL_SIZE, max_width: BOX_THUMBNAIL_SIZE },
responseType: 'json',
})
Expand All @@ -93,15 +92,15 @@ class Box extends Provider {

async size ({ id, token }) {
return this.#withErrorHandling('provider.box.size.error', async () => {
const { size } = await getClient({ token }).get(`files/${id}`, { responseType: 'json' }).json()
const { size } = await (await getClient({ token })).get(`files/${id}`, { responseType: 'json' }).json()
return parseInt(size, 10)
})
}

logout ({ companion, token }) {
return this.#withErrorHandling('provider.box.logout.error', async () => {
const { key, secret } = companion.options.providerOptions.box
await getClient({ token }).post('oauth2/revoke', {
await (await getClient({ token })).post('oauth2/revoke', {
prefixUrl: 'https://api.box.com',
form: {
client_id: key,
Expand Down
6 changes: 3 additions & 3 deletions packages/@uppy/companion/src/server/provider/credentials.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
const got = require('got').default
const atob = require('atob')
const { htmlEscape } = require('escape-goat')
const logger = require('../logger')
const oAuthState = require('../helpers/oauth-state')
const tokenService = require('../helpers/jwt')
// eslint-disable-next-line
const Provider = require('./Provider')

const got = require('../got')

/**
* @param {string} url
* @param {string} providerName
* @param {object|null} credentialRequestParams - null asks for default credentials.
*/
async function fetchKeys (url, providerName, credentialRequestParams) {
try {
const { credentials } = await got.post(url, {
const { credentials } = await (await got).post(url, {
json: { provider: providerName, parameters: credentialRequestParams },
}).json()

Expand Down
Loading

0 comments on commit a565c3e

Please sign in to comment.