@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:
124 lines (123 loc) • 4.74 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');
}
};