meta: fix all ESLint warnings and turn them into errors (#4398)

This commit is contained in:
Antoine du Hamel 2023-04-04 15:54:13 +02:00 committed by GitHub
parent 6eac99776c
commit 0a7eceac21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
40 changed files with 1129 additions and 905 deletions

View file

@ -3,20 +3,6 @@ lib
dist
coverage
test/lib/**
website/public/**
website/src/examples/**/*.html
website/themes/uppy/source/js/smooth-scroll.min.js
website/themes/uppy/source/js/uppy.js
website/themes/uppy/source/uppy/**
test/endtoend/*/build
examples/svelte-example/public/build/
bundle-legacy.js
website/src/_posts/201*.md
website/src/_posts/2020-*.md
website/src/_posts/2021-01-*.md
website/src/_posts/2021-02-*.md
website/src/_posts/2021-03-*.md
website/src/_posts/2021-04-*.md
website/src/_posts/2021-05-*.md
website/src/_posts/2021-06-*.md
website/src/docs/react-dashboard.md

View file

@ -53,8 +53,15 @@ module.exports = {
ignore: svgPresentationAttributes,
}],
// Special rules for CI:
...(process.env.CI && {
// Some imports are available only after a full build, which we don't do on CI.
'import/no-unresolved': 'off',
}),
// rules we want to enforce
'array-callback-return': 'error',
'func-names': 'error',
'implicit-arrow-linebreak': 'error',
'import/no-dynamic-require': 'error',
'import/no-extraneous-dependencies': 'error',
@ -76,25 +83,8 @@ module.exports = {
'prefer-spread': 'error',
'unicorn/prefer-node-protocol': 'error',
// transloadit rules we would like to enforce in the future
// but will require separate PRs to gradually get there
// and so the meantime: just warn
'class-methods-use-this': ['warn'],
'consistent-return': ['warn'],
'default-case': ['warn'],
'global-require': ['warn'],
'import/no-unresolved': ['warn'],
'import/order': ['warn'],
'no-mixed-operators': ['warn'],
'no-param-reassign': ['warn'],
'no-redeclare': ['warn'],
'no-shadow': ['warn'],
'no-use-before-define': ['warn', { 'functions': false }],
'radix': ['warn'],
'react/button-has-type': 'error',
'react/destructuring-assignment': ['warn'],
'react/forbid-prop-types': 'error',
'react/jsx-props-no-spreading': ['warn'],
'react/no-access-state-in-setstate': 'error',
'react/no-array-index-key': 'error',
'react/no-deprecated': 'error',
@ -122,8 +112,8 @@ module.exports = {
// jsdoc
'jsdoc/check-alignment': 'error',
'jsdoc/check-examples': 'off', // cannot yet be supported for ESLint 8, see https://github.com/eslint/eslint/issues/14745
'jsdoc/check-param-names': ['warn'],
'jsdoc/check-syntax': ['warn'],
'jsdoc/check-param-names': 'error',
'jsdoc/check-syntax': 'error',
'jsdoc/check-tag-names': ['error', { jsxTags: true }],
'jsdoc/check-types': 'error',
'jsdoc/newline-after-description': 'error',
@ -253,7 +243,6 @@ module.exports = {
'packages/@uppy/webcam/src/**/*.js',
'packages/@uppy/xhr-upload/src/**/*.js',
'packages/@uppy/zoom/src/**/*.js',
'website/src/examples/*/*.es6',
],
parser: 'espree',
parserOptions: {
@ -359,20 +348,10 @@ module.exports = {
'no-use-before-define': 'warn',
},
},
{
files: [
'website/src/examples/*/*.es6',
],
rules: {
'import/no-extraneous-dependencies': 'off',
'no-console': 'off',
},
},
{
files: [
'*.test.js',
'test/endtoend/*.js',
'website/*.js',
'bin/**.js',
],
rules: {
@ -383,8 +362,8 @@ module.exports = {
files: [
'bin/**.js',
'bin/**.mjs',
'examples/**/*.config.js',
'examples/**/*.cjs',
'examples/**/*.js',
'packages/@uppy/companion/test/**/*.js',
'test/**/*.js',
'test/**/*.ts',
@ -392,8 +371,6 @@ module.exports = {
'*.test-d.ts',
'postcss.config.js',
'.eslintrc.js',
'website/*.js',
'website/**/*.js',
'private/**/*.js',
],
rules: {
@ -415,15 +392,6 @@ module.exports = {
},
},
{
files: [
'website/themes/uppy/source/js/*.js',
],
rules: {
'prefer-const': ['off'],
},
},
{
files: ['test/endtoend/*/*.mjs', 'test/endtoend/*/*.ts'],
rules: {

12
.gitignore vendored
View file

@ -25,18 +25,6 @@ test/endtoend/create-react-app/coverage/
uppy-*.tgz
generatedLocale.d.ts
website/db.json
website/*.log
website/node_modules/
website/public/
website/.deploy*/
website/src/_drafts
website/themes/uppy/source/uppy/
website/themes/uppy/_config.yml
website/src/_template/list_of_locale_packs.md
website/src/examples/locale_list.json
website/themes/uppy/layout/partials/generated_stargazers.ejs
**/output/*
!output/.keep
examples/dev/file.txt

View file

@ -10,10 +10,8 @@ describe('Dashboard with @uppy/aws-s3-multipart', () => {
it('should upload cat image successfully', () => {
cy.get('@file-input').selectFile('cypress/fixtures/images/cat.jpg', { force:true })
cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
cy.wait(['@post', '@get', '@put']).then(() => {
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
})
})
cy.get('.uppy-StatusBar-actionBtn--upload').click()
cy.wait(['@post', '@get', '@put'])
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
})
})

View file

@ -9,10 +9,8 @@ describe('Dashboard with @uppy/aws-s3', () => {
it('should upload cat image successfully', () => {
cy.get('@file-input').selectFile('cypress/fixtures/images/cat.jpg', { force:true })
cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
cy.wait(['@post', '@get']).then(() => {
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
})
})
cy.get('.uppy-StatusBar-actionBtn--upload').click()
cy.wait(['@post', '@get'])
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
})
})

View file

@ -10,10 +10,9 @@ describe('Dashboard with Transloadit', () => {
it('should upload cat image successfully', () => {
cy.get('@file-input').selectFile('cypress/fixtures/images/cat.jpg', { force:true })
cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
cy.wait(['@assemblies', '@resumable']).then(() => {
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
})
cy.get('.uppy-StatusBar-actionBtn--upload').click()
cy.wait(['@assemblies', '@resumable']).then(() => {
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
})
})
@ -31,17 +30,16 @@ describe('Dashboard with Transloadit', () => {
cy.get('@file-input').selectFile(
['cypress/fixtures/images/cat.jpg', 'cypress/fixtures/images/traffic.jpg', 'cypress/fixtures/images/car.jpg'],
{ force:true },
).then(() => {
cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
cy.wait(['@createAssemblies']).then(() => {
expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).every((a: any) => a.pollInterval)).to.equal(true)
)
cy.get('.uppy-StatusBar-actionBtn--upload').click()
uppy.cancelAll()
cy.wait(['@createAssemblies']).then(() => {
expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).every((a: any) => a.pollInterval)).to.equal(true)
cy.wait(['@delete']).then(() => {
expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).some((a: any) => a.pollInterval)).to.equal(false)
})
})
uppy.cancelAll()
cy.wait(['@delete']).then(() => {
expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).some((a: any) => a.pollInterval)).to.equal(false)
})
})
})
@ -75,20 +73,15 @@ describe('Dashboard with Transloadit', () => {
{ method: 'DELETE', pathname: '/assemblies/*', times: 1 },
).as('assemblyDeletion')
cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
cy.wait('@assemblyPolling').then(() => {
cy.get('button[data-cy=cancel]').click().then(() => {
cy.wait('@assemblyDeletion').then(() => {
// Unfortunately, waiting on a network request somehow often results in a race condition.
// We just want to know wether this is called or not, so waiting for 2 sec to be sure.
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(2000).then(() => {
expect(spy).to.be.calledOnce
})
})
})
})
})
cy.get('.uppy-StatusBar-actionBtn--upload').click()
cy.wait('@assemblyPolling')
cy.get('button[data-cy=cancel]').click()
cy.wait('@assemblyDeletion')
// Unfortunately, waiting on a network request somehow often results in a race condition.
// We just want to know wether this is called or not, so waiting for 2 sec to be sure.
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(2000)
expect(spy).to.be.calledOnce
})
})
@ -116,18 +109,16 @@ describe('Dashboard with Transloadit', () => {
{ method: 'DELETE', pathname: '/assemblies/*', times: 1 },
).as('assemblyDeletion')
cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
cy.wait('@assemblyPolling').then(() => {
expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).every((a: any) => a.pollInterval)).to.equal(true)
cy.get('.uppy-StatusBar-actionBtn--upload').click()
cy.wait('@assemblyPolling')
expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).every((a: any) => a.pollInterval)).to.equal(true)
const { files } = uppy.getState()
uppy.removeFiles(Object.keys(files))
const { files } = uppy.getState()
uppy.removeFiles(Object.keys(files))
cy.wait('@assemblyDeletion').then(() => {
expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).some((a: any) => a.pollInterval)).to.equal(false)
expect(spy).to.be.calledOnce
})
})
cy.wait('@assemblyDeletion').then(() => {
expect(Object.values(uppy.getPlugin('Transloadit').activeAssemblies).some((a: any) => a.pollInterval)).to.equal(false)
expect(spy).to.be.calledOnce
})
})
})

View file

@ -22,10 +22,9 @@ describe('Dashboard with Tus', () => {
it('should upload cat image successfully', () => {
cy.get('@file-input').selectFile('cypress/fixtures/images/cat.jpg', { force:true })
cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
cy.wait(['@post', '@patch']).then(() => {
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
})
cy.get('.uppy-StatusBar-actionBtn--upload').click()
cy.wait(['@post', '@patch']).then(() => {
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
})
})
@ -37,10 +36,9 @@ describe('Dashboard with Tus', () => {
{ statusCode: 429, body: {} },
).as('patch')
cy.get('.uppy-StatusBar-actionBtn--upload').click().then(() => {
cy.wait('@tus').then(() => {
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
})
cy.get('.uppy-StatusBar-actionBtn--upload').click()
cy.wait('@tus').then(() => {
cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
})
})

View file

@ -41,7 +41,7 @@
"@types/jasmine": "~4.0.0",
"eslint": "^8.0.0",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsdoc": "^39.0.0",
"eslint-plugin-jsdoc": "^40.0.0",
"eslint-plugin-prefer-arrow": "^1.2.3",
"jasmine-core": "~4.2.0",
"karma": "~6.4.0",

View file

@ -137,11 +137,11 @@ app.get('/s3/multipart/:uploadId', (req, res, next) => {
const { key } = req.query
if (typeof key !== 'string') {
return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
return
}
const parts = []
listPartsPage(0)
function listPartsPage (startAt) {
client.listParts({
@ -165,6 +165,7 @@ app.get('/s3/multipart/:uploadId', (req, res, next) => {
}
})
}
listPartsPage(0)
})
function isValidPart (part) {

View file

@ -112,6 +112,7 @@ export default function App () {
<View style={{ alignItems: 'center' }}>
<Image
style={styles.logo}
// eslint-disable-next-line global-require
source={require('./assets/uppy-logo.png')}
/>
</View>

View file

@ -4,7 +4,7 @@
"version": "0.0.0",
"description": "Extensible JavaScript file upload widget with support for drag&drop, resumable uploads, previews, restrictions, file processing/encoding, remote providers like Instagram, Dropbox, Google Drive, S3 and more :dog:",
"lint-staged": {
"*.js": "eslint",
"*.js": "eslint --fix",
"*.jsx": "eslint --fix",
"*.md": [
"remark --silently-ignore -i .remarkignore -foq",
@ -68,8 +68,8 @@
"eslint-plugin-compat": "^4.0.0",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-jest": "^26.0.0",
"eslint-plugin-jsdoc": "^39.0.0",
"eslint-plugin-jest": "^27.0.0",
"eslint-plugin-jsdoc": "^40.0.0",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-markdown": "^3.0.0",
"eslint-plugin-node": "^11.1.0",
@ -77,7 +77,7 @@
"eslint-plugin-promise": "^6.0.0",
"eslint-plugin-react": "^7.22.0",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-unicorn": "^43.0.0",
"eslint-plugin-unicorn": "^46.0.0",
"github-contributors-list": "^1.2.4",
"glob": "^8.0.0",
"jest": "^29.0.0",

View file

@ -532,8 +532,10 @@ class Uploader {
// previously made to providers. Deleting the field would prevent it from getting leaked
// to the frontend etc.
// @ts-ignore
// eslint-disable-next-line no-param-reassign
delete error.originalRequest
// @ts-ignore
// eslint-disable-next-line no-param-reassign
delete error.originalResponse
reject(error)
},

View file

@ -1,3 +1,4 @@
/* eslint-disable global-require */
module.exports = {
callback: require('./callback'),
deauthorizationCallback: require('./deauth-callback'),

View file

@ -12,19 +12,22 @@ module.exports = function oauthRedirect (req, res) {
const params = qs.stringify(req.query)
const { authProvider } = req.companion.provider
if (!req.companion.options.server.oauthDomain) {
return res.redirect(req.companion.buildURL(`/connect/${authProvider}/callback?${params}`, true))
res.redirect(req.companion.buildURL(`/connect/${authProvider}/callback?${params}`, true))
return
}
const state = oAuthState.getDynamicStateFromRequest(req)
if (!state) {
return res.status(400).send('Cannot find state in session')
res.status(400).send('Cannot find state in session')
return
}
const handler = oAuthState.getFromState(state, 'companionInstance', req.companion.options.secret)
const handlerHostName = (new URL(handler)).host
if (hasMatch(handlerHostName, req.companion.options.server.validHosts)) {
const url = `${handler}/connect/${authProvider}/callback?${params}`
return res.redirect(url)
res.redirect(url)
return
}
res.status(400).send('Invalid Host in state')

View file

@ -28,13 +28,15 @@ module.exports = function s3 (config) {
const client = req.companion.s3Client
if (!client || typeof config.bucket !== 'string') {
return res.status(400).json({ error: 'This Companion server does not support uploading to S3' })
res.status(400).json({ error: 'This Companion server does not support uploading to S3' })
return
}
const metadata = req.query.metadata || {}
const key = config.getKey(req, req.query.filename, metadata)
if (typeof key !== 'string') {
return res.status(500).json({ error: 'S3 uploads are misconfigured: filename returned from `getKey` must be a string' })
res.status(500).json({ error: 'S3 uploads are misconfigured: filename returned from `getKey` must be a string' })
return
}
const fields = {
@ -45,8 +47,8 @@ module.exports = function s3 (config) {
if (config.acl != null) fields.acl = config.acl
Object.keys(metadata).forEach((key) => {
fields[`x-amz-meta-${key}`] = metadata[key]
Object.keys(metadata).forEach((metadataKey) => {
fields[`x-amz-meta-${metadataKey}`] = metadata[metadataKey]
})
client.createPresignedPost({
@ -88,10 +90,12 @@ module.exports = function s3 (config) {
const key = config.getKey(req, req.body.filename, req.body.metadata || {})
const { type, metadata } = req.body
if (typeof key !== 'string') {
return res.status(500).json({ error: 's3: filename returned from `getKey` must be a string' })
res.status(500).json({ error: 's3: filename returned from `getKey` must be a string' })
return
}
if (typeof type !== 'string') {
return res.status(400).json({ error: 's3: content type must be a string' })
res.status(400).json({ error: 's3: content type must be a string' })
return
}
const params = {
@ -135,11 +139,11 @@ module.exports = function s3 (config) {
const { key } = req.query
if (typeof key !== 'string') {
return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
return
}
let parts = []
listPartsPage(0)
function listPartsPage (startAt) {
client.listParts({
@ -159,14 +163,11 @@ module.exports = function s3 (config) {
// Get the next page.
listPartsPage(data.NextPartNumberMarker)
} else {
done()
res.json(parts)
}
})
}
function done () {
res.json(parts)
}
listPartsPage(0)
}
/**
@ -187,10 +188,12 @@ module.exports = function s3 (config) {
const { key } = req.query
if (typeof key !== 'string') {
return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
return
}
if (!parseInt(partNumber, 10)) {
return res.status(400).json({ error: 's3: the part number must be a number between 1 and 10000.' })
res.status(400).json({ error: 's3: the part number must be a number between 1 and 10000.' })
return
}
client.getSignedUrl('uploadPart', {
@ -229,19 +232,20 @@ module.exports = function s3 (config) {
const { key, partNumbers } = req.query
if (typeof key !== 'string') {
return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
return
}
if (typeof partNumbers !== 'string') {
return res.status(400).json({ error: 's3: the part numbers must be passed as a comma separated query parameter. For example: "?partNumbers=4,6,7,21"' })
res.status(400).json({ error: 's3: the part numbers must be passed as a comma separated query parameter. For example: "?partNumbers=4,6,7,21"' })
return
}
const partNumbersArray = partNumbers.split(',')
partNumbersArray.forEach((partNumber) => {
if (!parseInt(partNumber, 10)) {
return res.status(400).json({ error: 's3: the part numbers must be a number between 1 and 10000.' })
}
})
if (!partNumbersArray.every((partNumber) => parseInt(partNumber, 10))) {
res.status(400).json({ error: 's3: the part numbers must be a number between 1 and 10000.' })
return
}
Promise.all(
partNumbersArray.map((partNumber) => {
@ -282,7 +286,8 @@ module.exports = function s3 (config) {
const { key } = req.query
if (typeof key !== 'string') {
return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
return
}
client.abortMultipartUpload({
@ -318,10 +323,15 @@ module.exports = function s3 (config) {
const { parts } = req.body
if (typeof key !== 'string') {
return res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
res.status(400).json({ error: 's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"' })
return
}
if (!Array.isArray(parts) || !parts.every(isValidPart)) {
return res.status(400).json({ error: 's3: `parts` must be an array of {ETag, PartNumber} objects.' })
if (
!Array.isArray(parts)
|| !parts.every(part => typeof part === 'object' && typeof part?.PartNumber === 'number' && typeof part.ETag === 'string')
) {
res.status(400).json({ error: 's3: `parts` must be an array of {ETag, PartNumber} objects.' })
return
}
client.completeMultipartUpload({
@ -352,7 +362,3 @@ module.exports = function s3 (config) {
.post('/multipart/:uploadId/complete', express.json({ limit: '1mb' }), completeMultipartUpload)
.delete('/multipart/:uploadId', abortMultipartUpload)
}
function isValidPart (part) {
return part && typeof part === 'object' && typeof part.PartNumber === 'number' && typeof part.ETag === 'string'
}

View file

@ -44,7 +44,8 @@ module.exports = function sendToken (req, res, next) {
const allowedClients = req.companion.options.clients
// if no preset clients then allow any client
if (!allowedClients || hasMatch(origin, allowedClients) || hasMatch((new URL(origin)).host, allowedClients)) {
return res.send(htmlContent(uppyAuthToken, origin))
res.send(htmlContent(uppyAuthToken, origin))
return
}
}
next()

View file

@ -37,6 +37,29 @@ module.exports = (redisUrl, redisPubSubScope) => {
}
}
/**
* Remove an event listener
*
* @param {string} eventName name of the event
* @param {any} handler the handler of the event to remove
*/
function removeListener (eventName, handler) {
if (eventName === 'error') return errorEmitter.removeListener('error', handler)
return runWhenConnected(() => {
const handlersByThisEventName = handlersByEvent.get(eventName)
if (handlersByThisEventName == null) return undefined
const actualHandler = handlersByThisEventName.get(handler)
if (actualHandler == null) return undefined
handlersByThisEventName.delete(handler)
if (handlersByThisEventName.size === 0) handlersByEvent.delete(eventName)
return subscriber.pUnsubscribe(getPrefixedEventName(eventName), actualHandler)
})
}
function addListener (eventName, handler, _once = false) {
function actualHandler (message) {
if (_once) removeListener(eventName, handler)
@ -92,29 +115,6 @@ module.exports = (redisUrl, redisPubSubScope) => {
runWhenConnected(() => publisher.publish(getPrefixedEventName(eventName), JSON.stringify(args)))
}
/**
* Remove an event listener
*
* @param {string} eventName name of the event
* @param {any} handler the handler of the event to remove
*/
function removeListener (eventName, handler) {
if (eventName === 'error') return errorEmitter.removeListener('error', handler)
return runWhenConnected(() => {
const handlersByThisEventName = handlersByEvent.get(eventName)
if (handlersByThisEventName == null) return undefined
const actualHandler = handlersByThisEventName.get(handler)
if (actualHandler == null) return undefined
handlersByThisEventName.delete(handler)
if (handlersByThisEventName.size === 0) handlersByEvent.delete(eventName)
return subscriber.pUnsubscribe(getPrefixedEventName(eventName), actualHandler)
})
}
/**
* Remove all listeners of an event
*

View file

@ -2,6 +2,16 @@ const crypto = require('node:crypto')
const atob = require('atob')
const { encrypt, decrypt } = require('./utils')
const setState = (state, secret) => {
const encodedState = Buffer.from(JSON.stringify(state)).toString('base64')
return encrypt(encodedState, secret)
}
const getState = (state, secret) => {
const encodedState = decrypt(state, secret)
return JSON.parse(atob(encodedState))
}
module.exports.generateState = (secret) => {
const state = {}
state.id = crypto.randomBytes(10).toString('hex')
@ -17,16 +27,6 @@ module.exports.getFromState = (state, name, secret) => {
return getState(state, secret)[name]
}
const setState = (state, secret) => {
const encodedState = Buffer.from(JSON.stringify(state)).toString('base64')
return encrypt(encodedState, secret)
}
const getState = (state, secret) => {
const encodedState = decrypt(state, secret)
return JSON.parse(atob(encodedState))
}
module.exports.getDynamicStateFromRequest = (req) => {
const dynamic = (req.session.grant || {}).dynamic || {}
const { state } = dynamic

View file

@ -23,7 +23,7 @@ exports.jsonStringify = (data) => {
if (typeof value === 'object' && value !== null) {
if (cache.indexOf(value) !== -1) {
// Circular reference found, discard key
return
return undefined
}
cache.push(value)
}
@ -90,8 +90,7 @@ function urlEncode (unencoded) {
}
function urlDecode (encoded) {
encoded = encoded.replace(/-/g, '+').replace(/_/g, '/').replace(/~/g, '=')
return encoded
return encoded.replace(/-/g, '+').replace(/_/g, '/').replace(/~/g, '=')
}
/**

View file

@ -8,8 +8,8 @@ const semver = require('semver')
* @returns {boolean}
*/
exports.gte = (v1, v2) => {
v1 = semver.coerce(v1).version
v2 = semver.coerce(v2).version
return semver.gte(v1, v2)
return semver.gte(
semver.coerce(v1).version,
semver.coerce(v2).version,
)
}

View file

@ -10,17 +10,6 @@ const logger = require('./logger')
// TODO rewrite to use require('timers/promises').setTimeout when we support newer node versions
const sleep = promisify(setTimeout)
/**
* Runs a function every 24 hours, to clean up stale, upload related files.
*
* @param {string} dirPath path to the directory which you want to clean
*/
exports.startCleanUpJob = (dirPath) => {
logger.info('starting clean up job', 'jobs.cleanup.start')
// run once a day
schedule.scheduleJob('0 23 * * *', () => cleanUpFinishedUploads(dirPath))
}
const cleanUpFinishedUploads = (dirPath) => {
logger.info(`running clean up job for path: ${dirPath}`, 'jobs.cleanup.progress.read')
fs.readdir(dirPath, (err, files) => {
@ -40,12 +29,12 @@ const cleanUpFinishedUploads = (dirPath) => {
}
const fullPath = path.join(dirPath, file)
fs.stat(fullPath, (err, stats) => {
fs.stat(fullPath, (err2, stats) => {
const twelveHoursAgo = 12 * 60 * 60 * 1000
if (err) {
if (err2) {
// we still delete the file if we can't get the stats
// but we also log the error
logger.error(err, 'jobs.cleanup.stat.error')
logger.error(err2, 'jobs.cleanup.stat.error')
// @ts-ignore
} else if (((new Date()) - stats.mtime) < twelveHoursAgo) {
logger.info(`skipping file ${file}`, 'jobs.cleanup.skip')
@ -53,14 +42,25 @@ const cleanUpFinishedUploads = (dirPath) => {
}
logger.info(`deleting file ${file}`, 'jobs.cleanup.progress.delete')
fs.unlink(fullPath, (err) => {
if (err) logger.error(err, 'jobs.cleanup.delete.error')
fs.unlink(fullPath, (err3) => {
if (err3) logger.error(err3, 'jobs.cleanup.delete.error')
})
})
})
})
}
/**
* Runs a function every 24 hours, to clean up stale, upload related files.
*
* @param {string} dirPath path to the directory which you want to clean
*/
exports.startCleanUpJob = (dirPath) => {
logger.info('starting clean up job', 'jobs.cleanup.start')
// run once a day
schedule.scheduleJob('0 23 * * *', () => cleanUpFinishedUploads(dirPath))
}
async function runPeriodicPing ({ urls, payload, requestTimeout }) {
// Run requests in parallel
await Promise.all(urls.map(async (url) => {

View file

@ -89,9 +89,11 @@ module.exports.addCustomProviders = (customProviders, providers, grantConfig) =>
Object.keys(customProviders).forEach((providerName) => {
const customProvider = customProviders[providerName]
// eslint-disable-next-line no-param-reassign
providers[providerName] = customProvider.module
if (isOAuthProvider(customProvider.module.authProvider)) {
// eslint-disable-next-line no-param-reassign
grantConfig[providerName] = {
...customProvider.config,
// todo: consider setting these options from a universal point also used
@ -116,6 +118,7 @@ module.exports.addProviderOptions = (companionOptions, grantConfig) => {
return
}
// eslint-disable-next-line no-param-reassign
grantConfig.defaults = {
host: server.host,
protocol: server.protocol,
@ -128,9 +131,12 @@ module.exports.addProviderOptions = (companionOptions, grantConfig) => {
const authProvider = providerNameToAuthName(providerName, companionOptions)
if (isOAuthProvider(authProvider) && grantConfig[authProvider]) {
// explicitly add providerOptions so users don't override other providerOptions.
// eslint-disable-next-line no-param-reassign
grantConfig[authProvider].key = providerOptions[providerName].key
// eslint-disable-next-line no-param-reassign
grantConfig[authProvider].secret = providerOptions[providerName].secret
if (providerOptions[providerName].credentialsURL) {
// eslint-disable-next-line no-param-reassign
grantConfig[authProvider].dynamic = ['key', 'secret', 'redirect_uri']
}
@ -140,16 +146,20 @@ module.exports.addProviderOptions = (companionOptions, grantConfig) => {
// override grant.js redirect uri with companion's custom redirect url
const isExternal = !!server.implicitPath
const redirectPath = `/${providerName}/redirect`
// eslint-disable-next-line no-param-reassign
grantConfig[authProvider].redirect_uri = getURLBuilder(companionOptions)(redirectPath, isExternal)
if (oauthDomain) {
const fullRedirectPath = getURLBuilder(companionOptions)(redirectPath, isExternal, true)
// eslint-disable-next-line no-param-reassign
grantConfig[authProvider].redirect_uri = `${server.protocol}://${oauthDomain}${fullRedirectPath}`
}
if (server.implicitPath) {
// no url builder is used for this because grant internally adds the path
// eslint-disable-next-line no-param-reassign
grantConfig[authProvider].callback = `${server.implicitPath}${grantConfig[authProvider].callback}`
} else if (server.path) {
// eslint-disable-next-line no-param-reassign
grantConfig[authProvider].callback = `${server.path}${grantConfig[authProvider].callback}`
}
}

View file

@ -13,6 +13,7 @@ class Upload {
setTimeout(this._triggerProgressThenSuccess.bind(this), 100)
}
// eslint-disable-next-line class-methods-use-this
abort () {
// noop
}

View file

@ -1,7 +1,6 @@
/* global jest:false, test:false, expect:false, describe:false */
const mockOauthState = require('../mockoauthstate')()
// eslint-disable-next-line import/order
const request = require('supertest')
const tokenService = require('../../src/server/helpers/jwt')
const { getServer } = require('../mockserver')

View file

@ -1,5 +1,3 @@
/* global jest:false, test:false, describe:false, expect:false */
const { cors } = require('../../src/server/middlewares')
function testWithMock ({ corsOptions, get = () => {}, origin = 'https://localhost:1234' } = {}) {

View file

@ -1,5 +1,3 @@
/* global test:false, expect:false, describe:false, */
const headerSanitize = require('../../src/server/header-blacklist')
describe('Header black-list testing', () => {

View file

@ -1,4 +1,3 @@
/* global test:false, expect:false, describe:false, beforeAll:false, */
const chalk = require('chalk')
const logger = require('../../src/server/logger')

View file

@ -1,5 +1,3 @@
/* global jest:false, test:false, expect:false, describe:false */
jest.mock('../../src/server/helpers/jwt', () => {
return {
generateToken: () => {},

View file

@ -1,5 +1,3 @@
/* global test:false, expect:false, describe:false, beforeEach:false */
const providerManager = require('../../src/server/provider')
const { getCompanionOptions } = require('../../src/standalone/helper')
const { setDefaultEnv } = require('../mockserver')
@ -10,6 +8,7 @@ let companionOptions
describe('Test Provider options', () => {
beforeEach(() => {
setDefaultEnv()
// eslint-disable-next-line global-require
grantConfig = require('../../src/config/grant')()
companionOptions = getCompanionOptions()
})

View file

@ -1,5 +1,3 @@
/* global jest:false, test:false, expect:false, describe:false */
const nock = require('nock')
const request = require('supertest')

View file

@ -62,6 +62,7 @@ module.exports.getServer = (extraEnv) => {
// todo rewrite companion to not use global state
// https://github.com/transloadit/uppy/issues/3284
jest.resetModules()
// eslint-disable-next-line global-require
const standalone = require('../src/standalone')
const authServer = express()

View file

@ -172,7 +172,7 @@ es_MX.strings = {
zoomOut: 'Alejar',
}
es_MX.pluralize = function (count) {
es_MX.pluralize = (count) => {
if (count === 1) {
return 0
}

View file

@ -185,7 +185,7 @@ hi_IN.strings = {
zoomOut: 'ज़ूम आउट',
}
hi_IN.pluralize = function (count) {
hi_IN.pluralize = (count) => {
if (count === 1) {
return 0
}

View file

@ -1,6 +1,6 @@
import * as React from 'react'
import type { DashboardOptions } from '@uppy/dashboard'
import { Omit, WithBaseUppyProps } from './CommonTypes'
import type { Omit, WithBaseUppyProps } from './CommonTypes'
// This type is mapped into `DashboardProps` below so IntelliSense doesn't display this big mess of nested types
type DashboardPropsInner = Omit<

View file

@ -1,5 +1,5 @@
import type { DashboardOptions } from '@uppy/dashboard'
import { Omit, ToUppyProps } from './CommonTypes'
import type { Omit, ToUppyProps } from './CommonTypes'
// This type is mapped into `DashboardModalProps` below so IntelliSense doesn't display this big mess of nested types
type DashboardModalPropsInner = {

View file

@ -1,5 +1,5 @@
import type { DragDropOptions } from '@uppy/drag-drop'
import { ToUppyProps } from './CommonTypes'
import type { ToUppyProps } from './CommonTypes'
export type DragDropProps = ToUppyProps<DragDropOptions> & React.BaseHTMLAttributes<HTMLDivElement>

View file

@ -1,5 +1,5 @@
import type { FileInputOptions } from '@uppy/file-input'
import { ToUppyProps } from './CommonTypes'
import type { ToUppyProps } from './CommonTypes'
export type FileInputProps = ToUppyProps<FileInputOptions>

View file

@ -1,5 +1,5 @@
import type { ProgressBarOptions } from '@uppy/progress-bar'
import { ToUppyProps } from './CommonTypes'
import type { ToUppyProps } from './CommonTypes'
export type ProgressBarProps = ToUppyProps<ProgressBarOptions> & React.BaseHTMLAttributes<HTMLDivElement>

View file

@ -1,5 +1,5 @@
import type { StatusBarOptions } from '@uppy/status-bar'
import { ToUppyProps } from './CommonTypes'
import type { ToUppyProps } from './CommonTypes'
export type StatusBarProps = ToUppyProps<StatusBarOptions> & React.BaseHTMLAttributes<HTMLDivElement>

1610
yarn.lock

File diff suppressed because it is too large Load diff