|
@@ -1,26 +1,25 @@
|
|
|
const fs = require('fs')
|
|
|
const express = require('express')
|
|
|
+const ms = require('ms')
|
|
|
// @ts-ignore
|
|
|
const Grant = require('grant').express()
|
|
|
const grantConfig = require('./config/grant')()
|
|
|
const providerManager = require('./server/provider')
|
|
|
const controllers = require('./server/controllers')
|
|
|
const s3 = require('./server/controllers/s3')
|
|
|
+const getS3Client = require('./server/s3-client')
|
|
|
const url = require('./server/controllers/url')
|
|
|
-const SocketServer = require('ws').Server
|
|
|
const emitter = require('./server/emitter')
|
|
|
const merge = require('lodash.merge')
|
|
|
const redis = require('./server/redis')
|
|
|
const cookieParser = require('cookie-parser')
|
|
|
-const { jsonStringify, getURLBuilder } = require('./server/helpers/utils')
|
|
|
+const { getURLBuilder } = require('./server/helpers/utils')
|
|
|
const jobs = require('./server/jobs')
|
|
|
const interceptor = require('express-interceptor')
|
|
|
const logger = require('./server/logger')
|
|
|
-const { STORAGE_PREFIX } = require('./server/Uploader')
|
|
|
const middlewares = require('./server/middlewares')
|
|
|
-const { shortenToken } = require('./server/Uploader')
|
|
|
const { ProviderApiError, ProviderAuthError } = require('./server/provider/error')
|
|
|
-const ms = require('ms')
|
|
|
+const { getCredentialsOverrideMiddleware } = require('./server/provider/credentials')
|
|
|
|
|
|
const defaultOptions = {
|
|
|
server: {
|
|
@@ -42,6 +41,7 @@ const defaultOptions = {
|
|
|
|
|
|
// make the errors available publicly for custom providers
|
|
|
module.exports.errors = { ProviderApiError, ProviderAuthError }
|
|
|
+module.exports.socket = require('./server/socket')
|
|
|
|
|
|
/**
|
|
|
* Entry point into initializing the Companion app.
|
|
@@ -74,6 +74,8 @@ module.exports.app = (options = {}) => {
|
|
|
app.use(cookieParser()) // server tokens are added to cookies
|
|
|
|
|
|
app.use(interceptGrantErrorResponse)
|
|
|
+ // override provider credentials at request time
|
|
|
+ app.use('/connect/:authProvider/:override?', getCredentialsOverrideMiddleware(providers, options))
|
|
|
app.use(Grant(grantConfig))
|
|
|
app.use((req, res, next) => {
|
|
|
res.header('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, DELETE')
|
|
@@ -82,6 +84,7 @@ module.exports.app = (options = {}) => {
|
|
|
[
|
|
|
'uppy-auth-token',
|
|
|
'uppy-versions',
|
|
|
+ 'uppy-credentials-params',
|
|
|
res.get('Access-Control-Allow-Headers')
|
|
|
].join(',')
|
|
|
)
|
|
@@ -113,6 +116,7 @@ module.exports.app = (options = {}) => {
|
|
|
app.use('/s3', s3(options.providerOptions.s3))
|
|
|
app.use('/url', url())
|
|
|
|
|
|
+ app.post('/:providerName/preauth', middlewares.hasSessionAndProvider, controllers.preauth)
|
|
|
app.get('/:providerName/connect', middlewares.hasSessionAndProvider, controllers.connect)
|
|
|
app.get('/:providerName/redirect', middlewares.hasSessionAndProvider, controllers.redirect)
|
|
|
app.get('/:providerName/callback', middlewares.hasSessionAndProvider, controllers.callback)
|
|
@@ -125,7 +129,7 @@ module.exports.app = (options = {}) => {
|
|
|
app.get('/search/:searchProviderName/list', middlewares.hasSearchQuery, middlewares.loadSearchProviderToken, controllers.list)
|
|
|
app.post('/search/:searchProviderName/get/:id', middlewares.loadSearchProviderToken, controllers.get)
|
|
|
|
|
|
- app.param('providerName', providerManager.getProviderMiddleware(providers))
|
|
|
+ app.param('providerName', providerManager.getProviderMiddleware(providers, true))
|
|
|
app.param('searchProviderName', providerManager.getProviderMiddleware(searchProviders))
|
|
|
|
|
|
if (app.get('env') !== 'test') {
|
|
@@ -135,65 +139,6 @@ module.exports.app = (options = {}) => {
|
|
|
return app
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * the socket is used to send progress events during an upload
|
|
|
- *
|
|
|
- * @param {object} server
|
|
|
- */
|
|
|
-module.exports.socket = (server) => {
|
|
|
- const wss = new SocketServer({ server })
|
|
|
- const redisClient = redis.client()
|
|
|
-
|
|
|
- // A new connection is usually created when an upload begins,
|
|
|
- // or when connection fails while an upload is on-going and,
|
|
|
- // client attempts to reconnect.
|
|
|
- wss.on('connection', (ws, req) => {
|
|
|
- // @ts-ignore
|
|
|
- const fullPath = req.url
|
|
|
- // the token identifies which ongoing upload's progress, the socket
|
|
|
- // connection wishes to listen to.
|
|
|
- const token = fullPath.replace(/^.*\/api\//, '')
|
|
|
- logger.info(`connection received from ${token}`, 'socket.connect')
|
|
|
-
|
|
|
- /**
|
|
|
- *
|
|
|
- * @param {{action: string, payload: object}} data
|
|
|
- */
|
|
|
- function sendProgress (data) {
|
|
|
- ws.send(jsonStringify(data), (err) => {
|
|
|
- if (err) logger.error(err, 'socket.progress.error', shortenToken(token))
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- // if the redisClient is available, then we attempt to check the storage
|
|
|
- // if we have any already stored progress data on the upload.
|
|
|
- if (redisClient) {
|
|
|
- redisClient.get(`${STORAGE_PREFIX}:${token}`, (err, data) => {
|
|
|
- if (err) logger.error(err, 'socket.redis.error', shortenToken(token))
|
|
|
- if (data) {
|
|
|
- const dataObj = JSON.parse(data.toString())
|
|
|
- if (dataObj.action) sendProgress(dataObj)
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- emitter().emit(`connection:${token}`)
|
|
|
- emitter().on(token, sendProgress)
|
|
|
-
|
|
|
- ws.on('message', (jsonData) => {
|
|
|
- const data = JSON.parse(jsonData.toString())
|
|
|
- // whitelist triggered actions
|
|
|
- if (['pause', 'resume', 'cancel'].includes(data.action)) {
|
|
|
- emitter().emit(`${data.action}:${token}`)
|
|
|
- }
|
|
|
- })
|
|
|
-
|
|
|
- ws.on('close', () => {
|
|
|
- emitter().removeListener(token, sendProgress)
|
|
|
- })
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
// intercepts grantJS' default response error when something goes
|
|
|
// wrong during oauth process.
|
|
|
const interceptGrantErrorResponse = interceptor((req, res) => {
|
|
@@ -225,41 +170,6 @@ const interceptGrantErrorResponse = interceptor((req, res) => {
|
|
|
* @param {object} options
|
|
|
*/
|
|
|
const getOptionsMiddleware = (options) => {
|
|
|
- let s3Client = null
|
|
|
- if (options.providerOptions.s3) {
|
|
|
- const S3 = require('aws-sdk/clients/s3')
|
|
|
- const AWS = require('aws-sdk')
|
|
|
- const s3ProviderOptions = options.providerOptions.s3
|
|
|
-
|
|
|
- if (s3ProviderOptions.accessKeyId || s3ProviderOptions.secretAccessKey) {
|
|
|
- throw new Error('Found `providerOptions.s3.accessKeyId` or `providerOptions.s3.secretAccessKey` configuration, but Companion requires `key` and `secret` option names instead. Please use the `key` property instead of `accessKeyId` and the `secret` property instead of `secretAccessKey`.')
|
|
|
- }
|
|
|
-
|
|
|
- const rawClientOptions = s3ProviderOptions.awsClientOptions
|
|
|
- if (rawClientOptions && (rawClientOptions.accessKeyId || rawClientOptions.secretAccessKey)) {
|
|
|
- throw new Error('Found unsupported `providerOptions.s3.awsClientOptions.accessKeyId` or `providerOptions.s3.awsClientOptions.secretAccessKey` configuration. Please use the `providerOptions.s3.key` and `providerOptions.s3.secret` options instead.')
|
|
|
- }
|
|
|
-
|
|
|
- const s3ClientOptions = Object.assign({
|
|
|
- signatureVersion: 'v4',
|
|
|
- endpoint: s3ProviderOptions.endpoint,
|
|
|
- region: s3ProviderOptions.region,
|
|
|
- // backwards compat
|
|
|
- useAccelerateEndpoint: s3ProviderOptions.useAccelerateEndpoint
|
|
|
- }, rawClientOptions)
|
|
|
-
|
|
|
- // Use credentials to allow assumed roles to pass STS sessions in.
|
|
|
- // If the user doesn't specify key and secret, the default credentials (process-env)
|
|
|
- // will be used by S3 in calls below.
|
|
|
- if (s3ProviderOptions.key && s3ProviderOptions.secret && !s3ClientOptions.credentials) {
|
|
|
- s3ClientOptions.credentials = new AWS.Credentials(
|
|
|
- s3ProviderOptions.key,
|
|
|
- s3ProviderOptions.secret,
|
|
|
- s3ProviderOptions.sessionToken)
|
|
|
- }
|
|
|
- s3Client = new S3(s3ClientOptions)
|
|
|
- }
|
|
|
-
|
|
|
/**
|
|
|
* @param {object} req
|
|
|
* @param {object} res
|
|
@@ -269,7 +179,7 @@ const getOptionsMiddleware = (options) => {
|
|
|
const versionFromQuery = req.query.uppyVersions ? decodeURIComponent(req.query.uppyVersions) : null
|
|
|
req.companion = {
|
|
|
options,
|
|
|
- s3Client,
|
|
|
+ s3Client: getS3Client(options),
|
|
|
authToken: req.header('uppy-auth-token') || req.query.uppyAuthToken,
|
|
|
clientVersion: req.header('uppy-versions') || versionFromQuery || '1.0.0',
|
|
|
buildURL: getURLBuilder(options)
|