@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:
165 lines (145 loc) • 4.53 kB
JavaScript
import fs from 'node:fs'
import validator from 'validator'
import { defaultGetKey } from '../server/helpers/utils.js'
import logger from '../server/logger.js'
export const defaultOptions = {
server: {
protocol: 'http',
path: '',
},
providerOptions: {},
s3: {
endpoint: 'https://{service}.{region}.amazonaws.com',
conditions: [],
useAccelerateEndpoint: false,
getKey: defaultGetKey,
expires: 800, // seconds
},
enableUrlEndpoint: false,
enableGooglePickerEndpoint: false,
allowLocalUrls: false,
periodicPingUrls: [],
streamingUpload: true,
clientSocketConnectTimeout: 60000,
metrics: true,
}
/**
* @param {object} companionOptions
*/
export function getMaskableSecrets(companionOptions) {
const secrets = []
const { providerOptions, customProviders, s3 } = companionOptions
Object.keys(providerOptions).forEach((provider) => {
if (providerOptions[provider].secret) {
secrets.push(providerOptions[provider].secret)
}
})
if (customProviders) {
Object.keys(customProviders).forEach((provider) => {
if (customProviders[provider].config?.secret) {
secrets.push(customProviders[provider].config.secret)
}
})
}
if (s3?.secret) {
secrets.push(s3.secret)
}
return secrets
}
/**
* validates that the mandatory companion options are set.
* If it is invalid, it will console an error of unset options and exits the process.
* If it is valid, nothing happens.
*
* @param {object} companionOptions
*/
export const validateConfig = (companionOptions) => {
const mandatoryOptions = ['secret', 'filePath', 'server.host']
/** @type {string[]} */
const unspecified = []
mandatoryOptions.forEach((i) => {
const value = i
.split('.')
.reduce((prev, curr) => (prev ? prev[curr] : undefined), companionOptions)
if (!value) unspecified.push(`"${i}"`)
})
// vaidate that all required config is specified
if (unspecified.length) {
const messagePrefix =
'Please specify the following options to use companion:'
throw new Error(`${messagePrefix}\n${unspecified.join(',\n')}`)
}
// validate that specified filePath is writeable/readable.
try {
// @ts-ignore
fs.accessSync(
`${companionOptions.filePath}`,
fs.constants.R_OK | fs.constants.W_OK,
)
} catch (_err) {
throw new Error(
`No access to "${companionOptions.filePath}". Please ensure the directory exists and with read/write permissions.`,
)
}
const { providerOptions, periodicPingUrls, server } = companionOptions
if (server?.path) {
// see https://github.com/transloadit/uppy/issues/4271
// todo fix the code so we can allow `/`
if (server.path === '/')
throw new Error(
"If you want to use '/' as server.path, leave the 'path' variable unset",
)
}
if (providerOptions) {
const deprecatedOptions = {
microsoft: 'providerOptions.onedrive',
google: 'providerOptions.drive',
s3: 's3',
}
Object.keys(deprecatedOptions).forEach((deprecated) => {
if (Object.hasOwn(providerOptions, deprecated)) {
throw new Error(
`The Provider option "providerOptions.${deprecated}" is no longer supported. Please use the option "${deprecatedOptions[deprecated]}" instead.`,
)
}
})
}
if (
companionOptions.uploadUrls == null ||
companionOptions.uploadUrls.length === 0
) {
if (process.env.NODE_ENV === 'production')
throw new Error('uploadUrls is required')
logger.error(
'Running without uploadUrls is a security risk and Companion will refuse to start up when running in production (NODE_ENV=production)',
'startup.uploadUrls',
)
}
if (companionOptions.corsOrigins == null) {
throw new TypeError(
'Option corsOrigins is required. To disable security, pass true',
)
}
if (companionOptions.corsOrigins === '*') {
throw new TypeError(
'Option corsOrigins cannot be "*". To disable security, pass true',
)
}
if (
periodicPingUrls != null &&
(!Array.isArray(periodicPingUrls) ||
periodicPingUrls.some(
(url2) =>
!validator.isURL(url2, {
protocols: ['http', 'https'],
require_protocol: true,
require_tld: false,
}),
))
) {
throw new TypeError('Invalid periodicPingUrls')
}
if (companionOptions.maxFilenameLength <= 0) {
throw new TypeError('Option maxFilenameLength must be greater than 0')
}
}