diff --git a/.drone.star b/.drone.star new file mode 100644 index 0000000000..32b6bab4de --- /dev/null +++ b/.drone.star @@ -0,0 +1,280 @@ +ALPINE_GIT = "alpine/git:latest" +OC_CI_ALPINE = "owncloudci/alpine:latest" +OC_CI_BAZEL_BUILDIFIER = "owncloudci/bazel-buildifier" +OC_CI_NODEJS = "owncloudci/nodejs:18" +PLUGINS_DOCKER = "plugins/docker:20.14" +PLUGINS_GIT_ACTION = "plugins/git-action:1" +PLUGINS_GITHUB_RELEASE = "plugins/github-release:1" + +def main(ctx): + before = beforePipelines(ctx) + + stages = pipelinesDependsOn(stagePipelines(ctx), before) + + if (stages == False): + print("Errors detected. Review messages above.") + return [] + + after = pipelinesDependsOn(afterPipelines(ctx), stages) + + pipelines = before + stages + after + + pipelineSanityChecks(ctx, pipelines) + return pipelines + +def beforePipelines(ctx): + return checkStarlark() + lint(ctx) + +def stagePipelines(ctx): + return [] + +def afterPipelines(ctx): + return build(ctx) + +def lint(ctx): + pipelines = [] + + result = { + "kind": "pipeline", + "type": "docker", + "name": "lint", + "steps": [{ + "name": "lint", + "image": OC_CI_NODEJS, + "commands": [ + "yarn set version 3.6.1", + "yarn install --immutable", + "yarn lint", + ], + }], + "trigger": { + "ref": [ + "refs/heads/main", + "refs/heads/webdav-imports", + "refs/tags/**", + "refs/pull/**", + ], + }, + } + + pipelines.append(result) + + return pipelines + +def build(ctx): + pipelines = [] + steps = buildRelease(ctx) + buildDockerImage(ctx) + + result = { + "kind": "pipeline", + "type": "docker", + "name": "build", + "steps": steps, + "trigger": { + "ref": [ + "refs/heads/main", + "refs/heads/webdav-imports", + "refs/tags/**", + "refs/pull/**", + ], + }, + } + + pipelines.append(result) + + return pipelines + +def buildDockerImage(ctx): + return [ + { + "name": "docker", + "image": PLUGINS_DOCKER, + "settings": { + "username": { + "from_secret": "docker_username", + }, + "password": { + "from_secret": "docker_password", + }, + "auto_tag": ctx.build.event == "tag", + "dockerfile": "Dockerfile", + "repo": "owncloud/uppy-companion", + "dry_run": ctx.build.event != "tag", + }, + "depends_on": ["clone"], + }, + ] + +def determineReleaseVersion(ctx): + return ctx.build.ref.replace("refs/tags/v", "") + +def buildRelease(ctx): + version = determineReleaseVersion(ctx) + + return [ + { + "name": "build", + "image": OC_CI_NODEJS, + "commands": [ + "yarn set version 3.6.1", + "yarn install --immutable", + "yarn run build", + "yarn workspaces foreach pack", + "./collect_release_artifacts.sh", + ], + "depends_on": ["clone"], + }, + { + "name": "publish", + "image": PLUGINS_GITHUB_RELEASE, + "settings": { + "api_key": { + "from_secret": "github_token", + }, + "files": [ + "release/*", + ], + "checksum": [ + "md5", + "sha256", + ], + "title": ctx.build.ref.replace("refs/tags/v", ""), + "overwrite": True, + }, + "depends_on": ["build"], + "when": { + "ref": [ + "refs/tags/**", + ], + }, + }, + ] + +def checkStarlark(): + return [{ + "kind": "pipeline", + "type": "docker", + "name": "check-starlark", + "steps": [ + { + "name": "format-check-starlark", + "image": OC_CI_BAZEL_BUILDIFIER, + "commands": [ + "buildifier --mode=check .drone.star", + ], + }, + { + "name": "show-diff", + "image": OC_CI_BAZEL_BUILDIFIER, + "commands": [ + "buildifier --mode=fix .drone.star", + "git diff", + ], + "when": { + "status": [ + "failure", + ], + }, + }, + ], + "trigger": { + "ref": [ + "refs/pull/**", + ], + }, + }] + +def pipelineDependsOn(pipeline, dependant_pipelines): + if "depends_on" in pipeline.keys(): + pipeline["depends_on"] = pipeline["depends_on"] + getPipelineNames(dependant_pipelines) + else: + pipeline["depends_on"] = getPipelineNames(dependant_pipelines) + return pipeline + +def pipelinesDependsOn(pipelines, dependant_pipelines): + pipes = [] + for pipeline in pipelines: + pipes.append(pipelineDependsOn(pipeline, dependant_pipelines)) + + return pipes + +def getPipelineNames(pipelines = []): + """getPipelineNames returns names of pipelines as a string array + + Args: + pipelines: array of drone pipelines + + Returns: + names of the given pipelines as string array + """ + names = [] + for pipeline in pipelines: + names.append(pipeline["name"]) + return names + +def pipelineSanityChecks(ctx, pipelines): + """pipelineSanityChecks helps the CI developers to find errors before running it + + These sanity checks are only executed on when converting starlark to yaml. + Error outputs are only visible when the conversion is done with the drone cli. + + Args: + ctx: drone passes a context with information which the pipeline can be adapted to + pipelines: pipelines to be checked, normally you should run this on the return value of main() + + Returns: + none + """ + + # check if name length of pipeline and steps are exceeded. + max_name_length = 50 + for pipeline in pipelines: + pipeline_name = pipeline["name"] + if len(pipeline_name) > max_name_length: + print("Error: pipeline name %s is longer than 50 characters" % (pipeline_name)) + + for step in pipeline["steps"]: + step_name = step["name"] + if len(step_name) > max_name_length: + print("Error: step name %s in pipeline %s is longer than 50 characters" % (step_name, pipeline_name)) + + # check for non existing depends_on + possible_depends = [] + for pipeline in pipelines: + possible_depends.append(pipeline["name"]) + + for pipeline in pipelines: + if "depends_on" in pipeline.keys(): + for depends in pipeline["depends_on"]: + if not depends in possible_depends: + print("Error: depends_on %s for pipeline %s is not defined" % (depends, pipeline["name"])) + + # check for non declared volumes + for pipeline in pipelines: + pipeline_volumes = [] + if "volumes" in pipeline.keys(): + for volume in pipeline["volumes"]: + pipeline_volumes.append(volume["name"]) + + for step in pipeline["steps"]: + if "volumes" in step.keys(): + for volume in step["volumes"]: + if not volume["name"] in pipeline_volumes: + print("Warning: volume %s for step %s is not defined in pipeline %s" % (volume["name"], step["name"], pipeline["name"])) + + # list used docker images + print("") + print("List of used docker images:") + + images = {} + + for pipeline in pipelines: + for step in pipeline["steps"]: + image = step["image"] + if image in images.keys(): + images[image] = images[image] + 1 + else: + images[image] = 1 + + for image in images.keys(): + print(" %sx\t%s" % (images[image], image)) diff --git a/.env.example b/.env.example index 89fef4aee4..ce7e70e946 100644 --- a/.env.example +++ b/.env.example @@ -51,6 +51,12 @@ COMPANION_ZOOM_SECRET=*** COMPANION_UNSPLASH_KEY=*** COMPANION_UNSPLASH_SECRET=*** +COMPANION_ONEDRIVE_KEY=*** +COMPANION_ONEDRIVE_SECRET=**** + +COMPANION_WEBDAV_KEY=*** +COMPANION_WEBDAV_SECRET=*** + # Development environment # ======================= diff --git a/.eslintrc.js b/.eslintrc.js index 645bdbc351..3788219992 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -242,6 +242,7 @@ module.exports = { 'packages/@uppy/utils/src/**/*.js', 'packages/@uppy/vue/src/**/*.js', 'packages/@uppy/webcam/src/**/*.js', + 'packages/@uppy/webdav/src/**/*.js', 'packages/@uppy/xhr-upload/src/**/*.js', 'packages/@uppy/zoom/src/**/*.js', ], diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..db9ba4179a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: 'npm' + directory: '/' + schedule: + interval: 'weekly' diff --git a/.gitignore b/.gitignore index c95631cf94..bf58b43be3 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ issues.txt # companion deployment files transloadit-cluster-kubeconfig.yaml companion-env.yml + +.drone.yml diff --git a/.yarn/patches/webdav-npm-5.2.2-791e72c3de.patch b/.yarn/patches/webdav-npm-5.2.2-791e72c3de.patch new file mode 100644 index 0000000000..c55c340687 --- /dev/null +++ b/.yarn/patches/webdav-npm-5.2.2-791e72c3de.patch @@ -0,0 +1,12 @@ +diff --git a/dist/node/types.d.ts b/dist/node/types.d.ts +index f83ab515d230cb299a8aed4e13d6ad867efe2e61..346c3b762e6364e3bb016bc4fb50bc56fd649a56 100644 +--- a/dist/node/types.d.ts ++++ b/dist/node/types.d.ts +@@ -1,6 +1,6 @@ + /// + /// +-import Stream from "node:stream"; ++import * as Stream from "node:stream"; + import { Response } from "@buttercup/fetch"; + export { Request, Response } from "@buttercup/fetch"; + export type AuthHeader = string; diff --git a/collect_release_artifacts.sh b/collect_release_artifacts.sh new file mode 100755 index 0000000000..307b312439 --- /dev/null +++ b/collect_release_artifacts.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -euo pipefail + +mkdir -p release/ +for f in packages/**/**/package.tgz; do + package="$(echo "${f}" | cut -b 11- | rev | cut -b 13- | rev)" + cp "${f}" "release/$(echo ${package} | sed 's/\//-/').tgz" +done + diff --git a/package.json b/package.json index a508453754..9078746df4 100644 --- a/package.json +++ b/package.json @@ -176,6 +176,11 @@ "preact": "patch:preact@npm:10.10.0#.yarn/patches/preact-npm-10.10.0-dd04de05e8.patch", "start-server-and-test": "patch:start-server-and-test@npm:1.14.0#.yarn/patches/start-server-and-test-npm-1.14.0-841aa34fdf.patch", "stylelint-config-rational-order": "patch:stylelint-config-rational-order@npm%3A0.1.2#./.yarn/patches/stylelint-config-rational-order-npm-0.1.2-d8336e84ed.patch", - "uuid@^8.3.2": "patch:uuid@npm:8.3.2#.yarn/patches/uuid-npm-8.3.2-eca0baba53.patch" + "uuid@^8.3.2": "patch:uuid@npm:8.3.2#.yarn/patches/uuid-npm-8.3.2-eca0baba53.patch", + "webdav@^5.2.2": "patch:webdav@npm%3A5.2.2#./.yarn/patches/webdav-npm-5.2.2-791e72c3de.patch" + }, + "volta": { + "node": "18.17.0", + "yarn": "3.6.1" } } diff --git a/packages/@uppy/companion-client/src/Provider.js b/packages/@uppy/companion-client/src/Provider.js index 41790b13f3..bcf392fc55 100644 --- a/packages/@uppy/companion-client/src/Provider.js +++ b/packages/@uppy/companion-client/src/Provider.js @@ -7,6 +7,11 @@ const getName = (id) => { return id.split('-').map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join(' ') } +const queryString = (params, prefix = '?') => { + const str = new URLSearchParams(params).toString() + return str ? `${prefix}${str}` : '' +} + function getOrigin () { // eslint-disable-next-line no-restricted-globals return location.origin @@ -92,15 +97,15 @@ export default class Provider extends RequestClient { } authUrl (queries = {}) { - const params = new URLSearchParams({ + const qs = queryString({ state: btoa(JSON.stringify({ origin: getOrigin() })), ...queries, + ...this.cutomQueryParams, + ...(this.preAuthToken && { + uppyPreAuthToken: this.preAuthToken, + }), }) - if (this.preAuthToken) { - params.set('uppyPreAuthToken', this.preAuthToken) - } - - return `${this.hostname}/${this.id}/connect?${params}` + return `${this.hostname}/${this.id}/connect${qs}` } async login (queries) { @@ -148,11 +153,11 @@ export default class Provider extends RequestClient { } refreshTokenUrl () { - return `${this.hostname}/${this.id}/refresh-token` + return `${this.hostname}/${this.id}/refresh-token${queryString(this.cutomQueryParams)}` } fileUrl (id) { - return `${this.hostname}/${this.id}/get/${id}` + return `${this.hostname}/${this.id}/get/${id}${queryString(this.cutomQueryParams)}` } /** @protected */ @@ -199,11 +204,11 @@ export default class Provider extends RequestClient { } list (directory, options) { - return this.get(`${this.id}/list/${directory || ''}`, options) + return this.get(`${this.id}/list/${directory || ''}${queryString(this.cutomQueryParams)}`, options) } async logout (options) { - const response = await this.get(`${this.id}/logout`, options) + const response = await this.get(`${this.id}/logout${queryString(this.cutomQueryParams)}`, options) await this.#removeAuthToken() return response } diff --git a/packages/@uppy/companion/package.json b/packages/@uppy/companion/package.json index f1503c5950..4e89caf3cc 100644 --- a/packages/@uppy/companion/package.json +++ b/packages/@uppy/companion/package.json @@ -48,10 +48,12 @@ "express-prom-bundle": "6.5.0", "express-request-id": "1.4.1", "express-session": "1.17.3", + "fast-xml-parser": "^4.2.5", "form-data": "^3.0.0", "got": "11", "grant": "5.4.21", "helmet": "^4.6.0", + "ioredis": "5.3.2", "ipaddr.js": "^2.0.1", "jsonwebtoken": "8.5.1", "lodash": "^4.17.21", @@ -62,12 +64,12 @@ "ms": "2.1.3", "node-schedule": "2.1.0", "prom-client": "14.0.1", - "redis": "4.2.0", "semver": "7.5.3", "serialize-error": "^2.1.0", "serialize-javascript": "^6.0.0", "tus-js-client": "^3.0.0", "validator": "^13.0.0", + "webdav": "^5.2.2", "ws": "8.8.1" }, "devDependencies": { diff --git a/packages/@uppy/companion/src/companion.js b/packages/@uppy/companion/src/companion.js index 3e314e72d0..cba59855bd 100644 --- a/packages/@uppy/companion/src/companion.js +++ b/packages/@uppy/companion/src/companion.js @@ -73,6 +73,7 @@ module.exports.app = (optionsArg = {}) => { const providers = providerManager.getDefaultProviders() providerManager.addProviderOptions(options, grantConfig) + options.providerOptions = merge(options.providerOptions, grantConfig) const { customProviders } = options if (customProviders) { diff --git a/packages/@uppy/companion/src/config/grant.js b/packages/@uppy/companion/src/config/grant.js index 1e77be22ac..8c43e55d1d 100644 --- a/packages/@uppy/companion/src/config/grant.js +++ b/packages/@uppy/companion/src/config/grant.js @@ -44,5 +44,16 @@ module.exports = () => { access_url: 'https://zoom.us/oauth/token', callback: '/zoom/callback', }, + webdavAuth: { + transport: 'session', + dynamic: [ + 'subdomain', + ], + authorize_url: 'http://[subdomain]/apps/oauth2/authorize', + access_url: 'http://[subdomain]/apps/oauth2/api/v1/token', + scope: ['profile'], + oauth: 2, + callback: '/webdavAuth/callback', + }, } } diff --git a/packages/@uppy/companion/src/server/controllers/connect.js b/packages/@uppy/companion/src/server/controllers/connect.js index 1e98927744..0b1fb0ff0a 100644 --- a/packages/@uppy/companion/src/server/controllers/connect.js +++ b/packages/@uppy/companion/src/server/controllers/connect.js @@ -1,6 +1,11 @@ const atob = require('atob') const oAuthState = require('../helpers/oauth-state') +const queryString = (params, prefix = '') => { + const str = new URLSearchParams(params).toString() + return str ? `${prefix}${str}` : '' +} + /** * initializes the oAuth flow for a provider. * @@ -27,5 +32,10 @@ module.exports = function connect (req, res) { state = oAuthState.addToState(state, { preAuthToken: req.query.uppyPreAuthToken }, secret) } - res.redirect(req.companion.buildURL(`/connect/${req.companion.provider.authProvider}?state=${state}`, true)) + const providerName = req.companion.provider.authProvider + const qs = queryString(Object.fromEntries( + req.companion.options.providerOptions[providerName]?.dynamic?.map(p => [p, req.query[p]]) || [], + ), '&') + + res.redirect(req.companion.buildURL(`/connect/${providerName}?state=${state}${qs}`, true)) } diff --git a/packages/@uppy/companion/src/server/controllers/list.js b/packages/@uppy/companion/src/server/controllers/list.js index 284660c1be..481bdfc8d8 100644 --- a/packages/@uppy/companion/src/server/controllers/list.js +++ b/packages/@uppy/companion/src/server/controllers/list.js @@ -1,5 +1,10 @@ const { respondWithError } = require('../provider/error') +/** + * + * @param {{ query: object, params: object, companion: object }} req + * @param {object} res + */ async function list ({ query, params, companion }, res, next) { const { accessToken } = companion.providerTokens diff --git a/packages/@uppy/companion/src/server/controllers/logout.js b/packages/@uppy/companion/src/server/controllers/logout.js index 1ec87e303f..226dc57252 100644 --- a/packages/@uppy/companion/src/server/controllers/logout.js +++ b/packages/@uppy/companion/src/server/controllers/logout.js @@ -3,18 +3,18 @@ const { respondWithError } = require('../provider/error') /** * - * @param {object} req + * @param {{ query: object, params: object, companion: object, session: object }} req * @param {object} res */ async function logout (req, res, next) { + const { query, params, companion, session } = req const cleanSession = () => { - if (req.session.grant) { - req.session.grant.state = null - req.session.grant.dynamic = null + if (session.grant) { + session.grant.state = null + session.grant.dynamic = null } } - const { providerName } = req.params - const { companion } = req + const { providerName } = params const tokens = companion.allProvidersTokens ? companion.allProvidersTokens[providerName] : null if (!tokens) { @@ -25,7 +25,7 @@ async function logout (req, res, next) { try { const { accessToken } = tokens - const data = await companion.provider.logout({ token: accessToken, companion }) + const data = await companion.provider.logout({ companion, token: accessToken, query }) delete companion.allProvidersTokens[providerName] tokenService.removeFromCookies(res, companion.options, companion.provider.authProvider) cleanSession() diff --git a/packages/@uppy/companion/src/server/controllers/thumbnail.js b/packages/@uppy/companion/src/server/controllers/thumbnail.js index 0994964ed2..24e9755496 100644 --- a/packages/@uppy/companion/src/server/controllers/thumbnail.js +++ b/packages/@uppy/companion/src/server/controllers/thumbnail.js @@ -1,15 +1,15 @@ /** * - * @param {object} req + * @param {{ params: object, companion: object, query: object }} req * @param {object} res */ -async function thumbnail (req, res, next) { - const { providerName, id } = req.params - const { accessToken } = req.companion.allProvidersTokens[providerName] - const { provider } = req.companion +async function thumbnail ({ params, companion, query }, res, next) { + const { providerName, id } = params + const { accessToken } = companion.allProvidersTokens[providerName] + const { provider } = companion try { - const { stream } = await provider.thumbnail({ id, token: accessToken }) + const { stream } = await provider.thumbnail({ id, token: accessToken, query }) stream.pipe(res) } catch (err) { if (err.isAuthError) res.sendStatus(401) diff --git a/packages/@uppy/companion/src/server/controllers/url.js b/packages/@uppy/companion/src/server/controllers/url.js index 616c6368b9..d8c6bf23fd 100644 --- a/packages/@uppy/companion/src/server/controllers/url.js +++ b/packages/@uppy/companion/src/server/controllers/url.js @@ -36,7 +36,7 @@ const downloadURL = async (url, blockLocalIPs, traceId) => { } /** - * Fteches the size and content type of a URL + * Fetches the size and content type of a URL * * @param {object} req expressJS request object * @param {object} res expressJS response object diff --git a/packages/@uppy/companion/src/server/emitter/redis-emitter.js b/packages/@uppy/companion/src/server/emitter/redis-emitter.js index 9ec56b8062..0d23c1b077 100644 --- a/packages/@uppy/companion/src/server/emitter/redis-emitter.js +++ b/packages/@uppy/companion/src/server/emitter/redis-emitter.js @@ -10,13 +10,13 @@ const logger = require('../logger') module.exports = (redisClient, redisPubSubScope) => { const prefix = redisPubSubScope ? `${redisPubSubScope}:` : '' const getPrefixedEventName = (eventName) => `${prefix}${eventName}` - const publisher = redisClient.duplicate() - publisher.on('error', err => logger.error('publisher redis error', err)) + const publisher = redisClient.duplicate({ lazyConnect: true }) + publisher.on('error', err => logger.error('publisher redis error', err.toString())) let subscriber const connectedPromise = publisher.connect().then(() => { subscriber = publisher.duplicate() - subscriber.on('error', err => logger.error('subscriber redis error', err)) + subscriber.on('error', err => logger.error('subscriber redis error', err.toString())) return subscriber.connect() }) @@ -55,12 +55,17 @@ module.exports = (redisClient, redisPubSubScope) => { handlersByThisEventName.delete(handler) if (handlersByThisEventName.size === 0) handlersByEvent.delete(eventName) - return subscriber.pUnsubscribe(getPrefixedEventName(eventName), actualHandler) + subscriber.off('message', actualHandler) + return subscriber.punsubscribe(getPrefixedEventName(eventName)) }) } function addListener (eventName, handler, _once = false) { - function actualHandler (message) { + function actualHandler (pattern, channel, message) { + if (pattern !== getPrefixedEventName(eventName)) { + return + } + if (_once) removeListener(eventName, handler) let args try { @@ -78,7 +83,10 @@ module.exports = (redisClient, redisPubSubScope) => { } handlersByThisEventName.set(handler, actualHandler) - runWhenConnected(() => subscriber.pSubscribe(getPrefixedEventName(eventName), actualHandler)) + runWhenConnected(() => { + subscriber.on('pmessage', actualHandler) + return subscriber.psubscribe(getPrefixedEventName(eventName)) + }) } /** @@ -124,7 +132,7 @@ module.exports = (redisClient, redisPubSubScope) => { return runWhenConnected(() => { handlersByEvent.delete(eventName) - return subscriber.pUnsubscribe(getPrefixedEventName(eventName)) + return subscriber.punsubscribe(getPrefixedEventName(eventName)) }) } diff --git a/packages/@uppy/companion/src/server/provider/Provider.js b/packages/@uppy/companion/src/server/provider/Provider.js index 074d18d156..5f89fd017d 100644 --- a/packages/@uppy/companion/src/server/provider/Provider.js +++ b/packages/@uppy/companion/src/server/provider/Provider.js @@ -4,12 +4,14 @@ class Provider { /** * - * @param {{providerName: string, allowLocalUrls: boolean}} options + * + * @param {{providerName: string, allowLocalUrls: boolean, providerOptions: object}} options */ - constructor ({ allowLocalUrls }) { + constructor ({ allowLocalUrls, providerOptions }) { // Some providers might need cookie auth for the thumbnails fetched via companion this.needsCookieAuth = false this.allowLocalUrls = allowLocalUrls + this.providerOptions = providerOptions return this } diff --git a/packages/@uppy/companion/src/server/provider/credentials.js b/packages/@uppy/companion/src/server/provider/credentials.js index fa41dbaef1..da99ffe5ef 100644 --- a/packages/@uppy/companion/src/server/provider/credentials.js +++ b/packages/@uppy/companion/src/server/provider/credentials.js @@ -27,7 +27,7 @@ async function fetchKeys (url, providerName, credentialRequestParams) { } /** - * Fetches for a providers OAuth credentials. If the config for thtat provider allows fetching + * Fetches for a providers OAuth credentials. If the config for that provider allows fetching * of the credentials via http, and the `credentialRequestParams` argument is provided, the oauth * credentials will be fetched via http. Otherwise, the credentials provided via companion options * will be used instead. diff --git a/packages/@uppy/companion/src/server/provider/index.js b/packages/@uppy/companion/src/server/provider/index.js index e49aff660d..7858509588 100644 --- a/packages/@uppy/companion/src/server/provider/index.js +++ b/packages/@uppy/companion/src/server/provider/index.js @@ -9,6 +9,8 @@ const facebook = require('./facebook') const onedrive = require('./onedrive') const unsplash = require('./unsplash') const zoom = require('./zoom') +const webdavAuth = require('./webdav/auth') +const webdavPublicLink = require('./webdav/publicLink') const { getURLBuilder } = require('../helpers/utils') const logger = require('../logger') const { getCredentialsResolver } = require('./credentials') @@ -53,8 +55,9 @@ module.exports.getProviderMiddleware = (providers) => { const middleware = (req, res, next, providerName) => { const ProviderClass = providers[providerName] if (ProviderClass && validOptions(req.companion.options)) { + const providerOptions = req.companion.options.providerOptions[providerName] || {} const { allowLocalUrls } = req.companion.options - req.companion.provider = new ProviderClass({ providerName, allowLocalUrls }) + req.companion.provider = new ProviderClass({ providerName, providerOptions, allowLocalUrls }) req.companion.providerClass = ProviderClass if (isOAuthProvider(ProviderClass.authProvider)) { @@ -73,7 +76,7 @@ module.exports.getProviderMiddleware = (providers) => { * @returns {Record} */ module.exports.getDefaultProviders = () => { - const providers = { dropbox, box, drive, facebook, onedrive, zoom, instagram, unsplash } + const providers = { dropbox, box, drive, facebook, onedrive, zoom, instagram, unsplash, webdavAuth, webdavPublicLink } return providers } diff --git a/packages/@uppy/companion/src/server/provider/webdav/WebdavProvider.js b/packages/@uppy/companion/src/server/provider/webdav/WebdavProvider.js new file mode 100644 index 0000000000..ede7b572a0 --- /dev/null +++ b/packages/@uppy/companion/src/server/provider/webdav/WebdavProvider.js @@ -0,0 +1,105 @@ +const Provider = require('../Provider') +const logger = require('../../logger') +const { getProtectedHttpAgent, validateURL } = require('../../helpers/request') +const { withProviderErrorHandling } = require('../providerErrors') + +/** + * WebdavProvider base class provides implementations shared by WebdavAuth and WebdavPublicLink providers + */ +class WebdavProvider extends Provider { + async getClientHelper ({ url, ...options }) { + const { allowLocalUrls } = this + if (!validateURL(url, allowLocalUrls)) { + throw new Error('invalid webdav url') + } + const { protocol } = new URL(url) + const HttpAgentClass = getProtectedHttpAgent({ protocol, blockLocalIPs: !allowLocalUrls }) + + const { createClient } = await import('webdav') // eslint-disable-line import/no-unresolved + return createClient(url, { + ...options, + [`${protocol}Agent`] : new HttpAgentClass(), + }) + } + + async getClient ({ username, token, query }) { // eslint-disable-line no-unused-vars,class-methods-use-this + logger.error('call to getUsername is not implemented', 'provider.webdav.getUsername.error') + throw new Error('call to getUsername is not implemented') + // FIXME: use @returns to specify the return type + return this.getClientHelper() // eslint-disable-line + } + + async getUsername ({ query, token }) { // eslint-disable-line no-unused-vars,class-methods-use-this,class-methods-use-this + logger.error('call to getUsername is not implemented', 'provider.webdav.getUsername.error') + throw new Error('call to getUsername is not implemented') + } + + async list ({ directory, token, query }) { + return this.#withErrorHandling('provider.webdav.list.error', async () => { + const username = await this.getUsername({ token, query }) + const data = { username, items: [] } + const client = await this.getClient({ username, token, query }) + + /** @type {any} */ + const dir = await client.getDirectoryContents(directory || '/') + + dir.forEach(item => { + const isFolder = item.type === 'directory' + const requestPath = encodeURIComponent(`${directory || ''}/${item.basename}`) + data.items.push({ + isFolder, + id: requestPath, + name: item.basename, + requestPath, // TODO + modifiedDate: item.lastmod, // TODO: convert 'Tue, 04 Jul 2023 13:09:47 GMT' to ISO 8601 + ...(!isFolder && { + mimeType: item.mime, + size: item.size, + thumbnail: null, + + }), + }) + }) + + return data + }) + } + + async download ({ id, token, query }) { + return this.#withErrorHandling('provider.webdav.download.error', async () => { + // maybe we can avoid this by putting the username in front of the request path/id + const username = await this.getUsername({ token, query }) + const client = await this.getClient({ username, token, query }) + const stream = client.createReadStream(`/${id}`) + return { stream } + }) + } + + // eslint-disable-next-line + async thumbnail ({ id, token, query }) { + // not implementing this because a public thumbnail from webdav will be used instead + logger.error('call to thumbnail is not implemented', 'provider.webdav.thumbnail.error') + throw new Error('call to thumbnail is not implemented') + } + + // FIXME: implement + // eslint-disable-next-line + async size ({ id, token, query }) { + return this.#withErrorHandling('provider.webdav.size.error', async () => { + return 0 + }) + } + + // FIXME: not adjusted to webdav yet ... + async #withErrorHandling (tag, fn) { // eslint-disable-line class-methods-use-this + return withProviderErrorHandling({ + fn, + tag, + isAuthError: (response) => response.statusCode === 190, // Invalid OAuth 2.0 Access Token + getJsonErrorMessage: (body) => body?.error?.message, + providerName: 'webdav', + }) + } +} + +module.exports = WebdavProvider diff --git a/packages/@uppy/companion/src/server/provider/webdav/auth/index.js b/packages/@uppy/companion/src/server/provider/webdav/auth/index.js new file mode 100644 index 0000000000..88739814ca --- /dev/null +++ b/packages/@uppy/companion/src/server/provider/webdav/auth/index.js @@ -0,0 +1,79 @@ +const { XMLParser } = require('fast-xml-parser') + +const WebdavProvider = require('../WebdavProvider') +const { getProtectedGot, validateURL } = require('../../../helpers/request') + +const cloudTypePathMappings = { + nextcloud: { + manual_revoke_url: '/settings/user/security', + }, + owncloud: { + manual_revoke_url: '/settings/personal?sectionid=security', + }, +} + +class WebdavAuth extends WebdavProvider { + constructor (options) { + super(options) + this.authProvider = WebdavAuth.authProvider + } + + // for "grant" + static getExtraConfig () { + return {} + } + + static get authProvider () { + return 'webdavAuth' + } + + getBaseUrl ({ query: { subdomain } }) { + const { protocol } = this.providerOptions + + return `${protocol}://${subdomain}` + } + + async getUsername ({ token, query }) { + const { allowLocalUrls } = this + + const url = `${this.getBaseUrl({ query })}/ocs/v1.php/cloud/user` + if (!validateURL(url, allowLocalUrls)) { + throw new Error('invalid user url') + } + + const response = await getProtectedGot({ url, blockLocalIPs: !allowLocalUrls }).get(url, { + headers: { + Authorization: `Bearer ${token}`, + }, + }).text() + + const parser = new XMLParser() + const data = parser.parse(response) + return data?.ocs?.data?.id + } + + async getClient ({ username, token, query }) { + const url = `${this.getBaseUrl({ query })}/remote.php/dav/files/${username}` + + const { AuthType } = await import('webdav') // eslint-disable-line import/no-unresolved + return this.getClientHelper({ + url, + authType: AuthType.Token, + token: { + access_token: token, + token_type: 'Bearer', + }, + }) + } + + async logout ({ query }) { + const { cloudType } = query + const manual_revoke_url = cloudTypePathMappings[cloudType]?.manual_revoke_url + return { + revoked: false, + ...(manual_revoke_url && { manual_revoke_url: `${this.getBaseUrl({ query })}${manual_revoke_url}` }), + } + } +} + +module.exports = WebdavAuth diff --git a/packages/@uppy/companion/src/server/provider/webdav/publicLink/index.js b/packages/@uppy/companion/src/server/provider/webdav/publicLink/index.js new file mode 100644 index 0000000000..a6ca271922 --- /dev/null +++ b/packages/@uppy/companion/src/server/provider/webdav/publicLink/index.js @@ -0,0 +1,31 @@ +const WebdavProvider = require('../WebdavProvider') +const { validateURL } = require('../../../helpers/request') + +class WebdavPublicLink extends WebdavProvider { + async getUsername () { // eslint-disable-line class-methods-use-this + return null + } + + async getClient ({ query }) { + const publicLinkURL = query?.publicLinkURL + const { allowLocalUrls } = this + if (!validateURL(publicLinkURL, allowLocalUrls)) { + throw new Error('invalid public link url') + } + + const [baseURL, publicLinkToken] = publicLinkURL.split('/s/') + const { AuthType } = await import('webdav') // eslint-disable-line import/no-unresolved + return this.getClientHelper({ + url: `${baseURL.replace('/index.php', '')}/public.php/webdav/`, + authType: AuthType.Password, + username: publicLinkToken, + password: 'null', + }) + } + + async logout () { // eslint-disable-line class-methods-use-this + return { revoked: true } + } +} + +module.exports = WebdavPublicLink diff --git a/packages/@uppy/companion/src/server/redis.js b/packages/@uppy/companion/src/server/redis.js index e7391e3234..11e476d7d2 100644 --- a/packages/@uppy/companion/src/server/redis.js +++ b/packages/@uppy/companion/src/server/redis.js @@ -1,4 +1,4 @@ -const redis = require('redis') +const Redis = require('ioredis').default const logger = require('./logger') @@ -8,36 +8,26 @@ let redisClient * A Singleton module that provides a single redis client through out * the lifetime of the server * - * @param {{ redisUrl?: string, redisOptions?: Record }} [companionOptions] options + * @param {string} [redisUrl] ioredis url + * @param {Record} [redisOptions] ioredis client options */ -function createClient (companionOptions) { +function createClient (redisUrl, redisOptions) { if (!redisClient) { - const { redisUrl, redisOptions } = companionOptions - redisClient = redis.createClient({ - ...redisOptions, - ...(redisUrl && { url: redisUrl }), - }) - - redisClient.on('error', err => logger.error('redis error', err)) - - ;(async () => { - try { - // fire and forget. - // any requests made on the client before connection is established will be auto-queued by node-redis - await redisClient.connect() - } catch (err) { - logger.error(err.message, 'redis.error') - } - })() + if (redisUrl) { + redisClient = new Redis(redisUrl, redisOptions) + } else { + redisClient = new Redis(redisOptions) + } + redisClient.on('error', err => logger.error('redis error', err.toString())) } return redisClient } -module.exports.client = (companionOptions) => { - if (!companionOptions?.redisUrl && !companionOptions?.redisOptions) { +module.exports.client = ({ redisUrl, redisOptions } = { redisUrl: undefined, redisOptions: undefined }) => { + if (!redisUrl && !redisOptions) { return redisClient } - return createClient(companionOptions) + return createClient(redisUrl, redisOptions) } diff --git a/packages/@uppy/companion/src/standalone/helper.js b/packages/@uppy/companion/src/standalone/helper.js index f1ce3dbfe7..eb75bcd9b5 100644 --- a/packages/@uppy/companion/src/standalone/helper.js +++ b/packages/@uppy/companion/src/standalone/helper.js @@ -115,6 +115,14 @@ const getConfigFromEnv = () => { key: process.env.COMPANION_UNSPLASH_KEY, secret: process.env.COMPANION_UNSPLASH_SECRET, }, + webdavAuth: { + key: process.env.COMPANION_WEBDAV_KEY, + secret: getSecret('COMPANION_WEBDAV_SECRET'), + protocol: process.env.COMPANION_WEBDAV_PROTOCOL || 'https', + }, + webdavPublicLink: { + key: 'webdav_public_link_needs_no_key', + }, }, s3: { key: process.env.COMPANION_AWS_KEY, @@ -144,9 +152,9 @@ const getConfigFromEnv = () => { periodicPingCount: process.env.COMPANION_PERIODIC_PING_COUNT ? parseInt(process.env.COMPANION_PERIODIC_PING_COUNT, 10) : undefined, filePath: process.env.COMPANION_DATADIR, - redisUrl: process.env.COMPANION_REDIS_URL, redisPubSubScope: process.env.COMPANION_REDIS_PUBSUB_SCOPE, - // redisOptions refers to https://www.npmjs.com/package/redis#options-object-properties + redisUrl: process.env.COMPANION_REDIS_URL, + // redisOptions refers to https://redis.github.io/ioredis/index.html#RedisOptions redisOptions: (() => { try { if (!process.env.COMPANION_REDIS_OPTIONS) { diff --git a/packages/@uppy/locales/src/en_US.js b/packages/@uppy/locales/src/en_US.js index b7a16db1cd..8cf52fdc23 100644 --- a/packages/@uppy/locales/src/en_US.js +++ b/packages/@uppy/locales/src/en_US.js @@ -121,6 +121,8 @@ en_US.strings = { pluginNameGoogleDrive: 'Google Drive', pluginNameInstagram: 'Instagram', pluginNameOneDrive: 'OneDrive', + pluginNameWebdavAuth: 'WebDAV', + pluginNameWebdavPublicLink: 'Public Link', pluginNameZoom: 'Zoom', poweredBy: 'Powered by %{uppy}', processingXFiles: { diff --git a/packages/@uppy/provider-views/src/ProviderView/AuthView.jsx b/packages/@uppy/provider-views/src/ProviderView/AuthView.jsx index 5cf51f82f6..cfc008ecb0 100644 --- a/packages/@uppy/provider-views/src/ProviderView/AuthView.jsx +++ b/packages/@uppy/provider-views/src/ProviderView/AuthView.jsx @@ -37,7 +37,7 @@ function GoogleIcon () { } function AuthView (props) { - const { pluginName, pluginIcon, i18nArray, handleAuth } = props + const { pluginName, pluginIcon, i18nArray, handleAuth, inputs } = props // In order to comply with Google's brand we need to create a different button // for the Google Drive plugin const isGoogleDrive = pluginName === 'Google Drive' @@ -56,26 +56,35 @@ function AuthView (props) { pluginName: pluginNameComponent, })} - {isGoogleDrive ? ( - - ) : ( - - )} +
+ {inputs?.map((i) => ( +
+ + {i.description && ({i.description})} +
+ ))} + {isGoogleDrive ? ( + + ) : ( + + )} +
) } diff --git a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx index e0991350e2..88471df207 100644 --- a/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx +++ b/packages/@uppy/provider-views/src/ProviderView/ProviderView.jsx @@ -242,7 +242,18 @@ export default class ProviderView extends View { this.plugin.setPluginState({ filterInput: '' }) } - async handleAuth () { + async handleAuth (event) { + event.preventDefault() + + const formData = new FormData(event.target) + this.provider.cutomQueryParams = Object.fromEntries(formData.entries()) + this.opts.authInputs?.forEach(i => { + if (!i.serialize) { + return + } + this.provider.cutomQueryParams[i.name] = i.serialize(this.provider.cutomQueryParams[i.name]) + }) + const clientVersion = `@uppy/provider-views=${ProviderView.VERSION}` try { await this.provider.login({ uppyVersions: clientVersion }) @@ -478,6 +489,7 @@ export default class ProviderView extends View { handleAuth={this.handleAuth} i18n={this.plugin.uppy.i18n} i18nArray={this.plugin.uppy.i18nArray} + inputs={this.opts.authInputs} /> ) diff --git a/packages/@uppy/provider-views/src/style.scss b/packages/@uppy/provider-views/src/style.scss index 4ec4a06cd3..f53cdebb1f 100644 --- a/packages/@uppy/provider-views/src/style.scss +++ b/packages/@uppy/provider-views/src/style.scss @@ -28,6 +28,28 @@ color: $gray-500; } +.uppy-Provider-authForm { + display: flex; + justify-content: center; + flex-wrap: wrap; +} + +.uppy-Provider-authForm input { + display: block; + width: 100%; + margin-bottom: 5px; +} + +.uppy-Provider-authForm label span { + display: inline-block; + margin-bottom: 5px; +} + +.uppy-Provider-authInput { + width: 80%; + margin-bottom: 20px; +} + .uppy-Provider-empty { color: $gray-500; } diff --git a/packages/@uppy/webdav/CHANGELOG.md b/packages/@uppy/webdav/CHANGELOG.md new file mode 100644 index 0000000000..b02dc177cb --- /dev/null +++ b/packages/@uppy/webdav/CHANGELOG.md @@ -0,0 +1,5 @@ +# @uppy/webdav + +## unreleased + +Initial implementation diff --git a/packages/@uppy/webdav/LICENSE b/packages/@uppy/webdav/LICENSE new file mode 100644 index 0000000000..c237473300 --- /dev/null +++ b/packages/@uppy/webdav/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2018 Transloadit + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/@uppy/webdav/README.md b/packages/@uppy/webdav/README.md new file mode 100644 index 0000000000..c16cee5637 --- /dev/null +++ b/packages/@uppy/webdav/README.md @@ -0,0 +1,60 @@ +# @uppy/webdav + +Uppy logo: a smiling puppy above a pink upwards arrow + +[![npm version](https://img.shields.io/npm/v/@uppy/webdav.svg?style=flat-square)](https://www.npmjs.com/package/@uppy/webdav) +![CI status for Uppy tests](https://github.com/transloadit/uppy/workflows/Tests/badge.svg) +![CI status for Companion tests](https://github.com/transloadit/uppy/workflows/Companion/badge.svg) +![CI status for browser tests](https://github.com/transloadit/uppy/workflows/End-to-end%20tests/badge.svg) + +The WebDAV plugins let users import files from [ownCloud 10](https://owncloud.com) or [Nextcloud](https://nextcloud.com) instances. +Files from the personal folder can be imported with the `WebDavAuth` plugin and files from (unprotected) public links can be selected via the `WebDavPublicLink` plugin. +Both plugins can be used in parallel and several times while showing icons for ownCloud and Nextcloud. + +A [Companion](https://uppy.io/docs/companion) instance is required for the WebDAV plugins to work. +Companion handles downloads the files, and uploads them to the destination. This saves the user bandwidth, especially helpful if they are on a mobile connection. +In case of the `WebDavAuth` plugin Companion handles also OAuth 2 authentication with ownCloud and Nextcloud. + +Uppy is being developed by the folks at [Transloadit](https://transloadit.com), a versatile file encoding service. + +## Example + +```js +import Uppy from '@uppy/core' +import { WebDavAuth, WebDavPublicLink } from '@uppy/webdav' + +const uppy = new Uppy() +uppy.use(WebDavAuth, { + id: 'owncloudAuth', + cloudType: 'owncloud', +}) +uppy.use(WebDavAuth, { + id: 'nextcloudAuth', + cloudType: 'nextcloud', +}) + +uppy.use(WebDavPublicLink, { + id: 'owncloudPublicLink', + cloudType: 'owncloud', +}) +uppy.use(WebDavPublicLink, { + id: 'nextcloudPublicLink', + cloudType: 'nextcloud', +}) +``` + +## Installation + +```bash +$ npm install @uppy/webdav +``` + +Alternatively, you can also use this plugin in a pre-built bundle from Transloadit’s CDN: Edgly. In that case `Uppy` will attach itself to the global `window.Uppy` object. See the [main Uppy documentation](https://uppy.io/docs/#Installation) for instructions. + +## Documentation + +Documentation for this plugin can be found on the [Uppy website](https://uppy.io/docs/webdav). + +## License + +[The MIT License](./LICENSE). diff --git a/packages/@uppy/webdav/package.json b/packages/@uppy/webdav/package.json new file mode 100644 index 0000000000..d753114830 --- /dev/null +++ b/packages/@uppy/webdav/package.json @@ -0,0 +1,35 @@ +{ + "name": "@uppy/webdav", + "description": "Import files from WebDAV into Uppy.", + "version": "3.1.1", + "license": "MIT", + "main": "lib/index.js", + "types": "types/index.d.ts", + "type": "module", + "keywords": [ + "file uploader", + "uppy", + "uppy-plugin", + "instagram", + "provider", + "photos", + "videos" + ], + "homepage": "https://uppy.io", + "bugs": { + "url": "https://github.com/transloadit/uppy/issues" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/transloadit/uppy.git" + }, + "dependencies": { + "@uppy/companion-client": "workspace:^", + "@uppy/provider-views": "workspace:^", + "@uppy/utils": "workspace:^", + "preact": "^10.5.13" + }, + "peerDependencies": { + "@uppy/core": "workspace:^" + } +} diff --git a/packages/@uppy/webdav/src/auth/WebdavAuth.jsx b/packages/@uppy/webdav/src/auth/WebdavAuth.jsx new file mode 100644 index 0000000000..396efeb5cd --- /dev/null +++ b/packages/@uppy/webdav/src/auth/WebdavAuth.jsx @@ -0,0 +1,79 @@ +import { UIPlugin } from '@uppy/core' +import { Provider } from '@uppy/companion-client' +import { ProviderViews } from '@uppy/provider-views' + +import packageJson from '../../package.json' +import locale from '../locale.js' +import * as cloudTypes from '../cloudTypes/index.js' + +export default class WebdavAuth extends UIPlugin { + static VERSION = packageJson.version + + constructor (uppy, opts) { + super(uppy, opts) + this.id = this.opts.id || `webdavAuth${this.opts.cloudType || ''}` + Provider.initPlugin(this, opts) + this.cloudType = cloudTypes[this.opts.cloudType] + this.icon = this.cloudType?.icon + this.defaultLocale = locale + + this.i18nInit() + this.title = this.cloudType?.displayName || this.i18n('pluginNameWebdavAuth') + + this.provider = new Provider(uppy, { + companionUrl: this.opts.companionUrl, + companionHeaders: this.opts.companionHeaders, + companionKeysParams: this.opts.companionKeysParams, + companionCookiesRule: this.opts.companionCookiesRule, + provider: 'webdavAuth', + pluginId: this.id, + }) + + this.onFirstRender = this.onFirstRender.bind(this) + this.render = this.render.bind(this) + } + + install () { + this.view = new ProviderViews(this, { + provider: this.provider, + viewType: 'list', + showTitles: true, + showFilter: true, + showBreadcrumbs: true, + authInputs: [ + { + name: 'subdomain', + label: 'WebDAV URL', + description: 'Please provide a URL to a server.', + serialize: (value) => new URL(value).host || value, + }, + { + name: 'cloudType', + type: 'hidden', + defaultValue: 'nextcloud', + }, + ], + }) + + const { target } = this.opts + if (target) { + this.mount(target, this) + } + } + + uninstall () { + this.view.tearDown() + this.unmount() + } + + onFirstRender () { + return Promise.all([ + this.provider.fetchPreAuthToken(), + this.view.getFolder(), + ]) + } + + render (state) { + return this.view.render(state) + } +} diff --git a/packages/@uppy/webdav/src/auth/index.js b/packages/@uppy/webdav/src/auth/index.js new file mode 100644 index 0000000000..768782f5e4 --- /dev/null +++ b/packages/@uppy/webdav/src/auth/index.js @@ -0,0 +1 @@ +export { default } from './WebdavAuth.jsx' diff --git a/packages/@uppy/webdav/src/cloudTypes/index.js b/packages/@uppy/webdav/src/cloudTypes/index.js new file mode 100644 index 0000000000..cd00411952 --- /dev/null +++ b/packages/@uppy/webdav/src/cloudTypes/index.js @@ -0,0 +1,2 @@ +export * as nextcloud from './nextcloud.jsx' +export * as owncloud from './owncloud.jsx' diff --git a/packages/@uppy/webdav/src/cloudTypes/nextcloud.jsx b/packages/@uppy/webdav/src/cloudTypes/nextcloud.jsx new file mode 100644 index 0000000000..b3f7803d83 --- /dev/null +++ b/packages/@uppy/webdav/src/cloudTypes/nextcloud.jsx @@ -0,0 +1,10 @@ +import { h } from 'preact' + +export const displayName = 'Nextcloud' + +export const icon = () => ( + + + + +) diff --git a/packages/@uppy/webdav/src/cloudTypes/owncloud.jsx b/packages/@uppy/webdav/src/cloudTypes/owncloud.jsx new file mode 100644 index 0000000000..9477d56972 --- /dev/null +++ b/packages/@uppy/webdav/src/cloudTypes/owncloud.jsx @@ -0,0 +1,29 @@ +import { h } from 'preact' + +export const displayName = 'ownCloud' + +export const icon = () => ( + + + + + + + + + + + + + + + + + + + + + + + +) diff --git a/packages/@uppy/webdav/src/index.js b/packages/@uppy/webdav/src/index.js new file mode 100644 index 0000000000..99d4898184 --- /dev/null +++ b/packages/@uppy/webdav/src/index.js @@ -0,0 +1,2 @@ +export { default as WebdavAuth } from './auth/index.js' +export { default as WebdavPublicLink } from './publicLink/index.js' diff --git a/packages/@uppy/webdav/src/locale.js b/packages/@uppy/webdav/src/locale.js new file mode 100644 index 0000000000..9faf16a969 --- /dev/null +++ b/packages/@uppy/webdav/src/locale.js @@ -0,0 +1,8 @@ +export default { + strings: { + pluginNameWebdavAuth: 'WebDAV', + pluginNameWebdavPublicLink: 'Public Link', + publicLinkURLLabel: 'Public Link URL', + publicLinkURLDescription: 'Please provide a URL to a public link without password protection.', + }, +} diff --git a/packages/@uppy/webdav/src/publicLink/WebdavPublicLink.jsx b/packages/@uppy/webdav/src/publicLink/WebdavPublicLink.jsx new file mode 100644 index 0000000000..2dc69d6174 --- /dev/null +++ b/packages/@uppy/webdav/src/publicLink/WebdavPublicLink.jsx @@ -0,0 +1,79 @@ +import { UIPlugin } from '@uppy/core' +import { Provider } from '@uppy/companion-client' +import { ProviderViews } from '@uppy/provider-views' + +import packageJson from '../../package.json' +import locale from '../locale.js' +import * as cloudTypes from '../cloudTypes/index.js' + +export default class WebdavPublicLink extends UIPlugin { + static VERSION = packageJson.version + + constructor (uppy, opts) { + super(uppy, opts) + this.id = this.opts.id || `webdavPublicLink${this.opts.cloudType || ''}` + Provider.initPlugin(this, opts) + this.cloudType = cloudTypes[this.opts.cloudType] + this.icon = this.cloudType?.icon + + this.defaultLocale = locale + this.i18nInit() + + this.title = this.i18n('pluginNameWebdavPublicLink') + + this.provider = new Provider(uppy, { + companionUrl: this.opts.companionUrl, + companionHeaders: this.opts.companionHeaders, + companionKeysParams: this.opts.companionKeysParams, + companionCookiesRule: this.opts.companionCookiesRule, + provider: 'webdavPublicLink', + pluginId: this.id, + }) + this.provider.login = async () => { + } + this.provider.logout = async () => { + return { ok: true, revoked: true } + } + + this.onFirstRender = this.onFirstRender.bind(this) + this.render = this.render.bind(this) + } + + install () { + this.view = new ProviderViews(this, { + provider: this.provider, + viewType: 'list', + showTitles: true, + showFilter: true, + showBreadcrumbs: true, + authInputs: [ + { + name: 'publicLinkURL', + label: this.i18n('publicLinkURLLabel'), + description: this.i18n('publicLinkURLDescription'), + }, + ], + }) + + const { target } = this.opts + if (target) { + this.mount(target, this) + } + } + + uninstall () { + this.view.tearDown() + this.unmount() + } + + onFirstRender () { + if (!this.provider.cutomQueryParams?.publicLinkURL) { + return true + } + return this.view.getFolder() + } + + render (state) { + return this.view.render(state) + } +} diff --git a/packages/@uppy/webdav/src/publicLink/index.js b/packages/@uppy/webdav/src/publicLink/index.js new file mode 100644 index 0000000000..a66eeb43cb --- /dev/null +++ b/packages/@uppy/webdav/src/publicLink/index.js @@ -0,0 +1 @@ +export { default } from './WebdavPublicLink.jsx' diff --git a/packages/@uppy/webdav/types/auth/index.d.ts b/packages/@uppy/webdav/types/auth/index.d.ts new file mode 100644 index 0000000000..4abf14a65f --- /dev/null +++ b/packages/@uppy/webdav/types/auth/index.d.ts @@ -0,0 +1,12 @@ +import type { PluginOptions, UIPlugin, PluginTarget } from '@uppy/core' +import type { PublicProviderOptions, TokenStorage } from '@uppy/companion-client' + +export interface WebdavAuthOptions extends PluginOptions, PublicProviderOptions { + target?: PluginTarget + title?: string + storage?: TokenStorage +} + +declare class WebdavAuth extends UIPlugin {} + +export default WebdavAuth diff --git a/packages/@uppy/webdav/types/publicLink/index.d.ts b/packages/@uppy/webdav/types/publicLink/index.d.ts new file mode 100644 index 0000000000..2258677246 --- /dev/null +++ b/packages/@uppy/webdav/types/publicLink/index.d.ts @@ -0,0 +1,12 @@ +import type { PluginOptions, UIPlugin, PluginTarget } from '@uppy/core' +import type { PublicProviderOptions, TokenStorage } from '@uppy/companion-client' + +export interface WebdavPublicLinkOptions extends PluginOptions, PublicProviderOptions { + target?: PluginTarget + title?: string + storage?: TokenStorage +} + +declare class WebdavPublicLink extends UIPlugin {} + +export default WebdavPublicLink diff --git a/private/dev/Dashboard.js b/private/dev/Dashboard.js index ed6933b9c5..989088d6c2 100644 --- a/private/dev/Dashboard.js +++ b/private/dev/Dashboard.js @@ -4,6 +4,7 @@ import Uppy, { debugLogger } from '@uppy/core' import Dashboard from '@uppy/dashboard' import RemoteSources from '@uppy/remote-sources' import Webcam from '@uppy/webcam' +import { WebdavAuth, WebdavPublicLink } from '@uppy/webdav' import ScreenCapture from '@uppy/screen-capture' import GoldenRetriever from '@uppy/golden-retriever' import Tus from '@uppy/tus' @@ -81,6 +82,14 @@ export default () => { note: `${JSON.stringify(restrictions)}`, }) // .use(GoogleDrive, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts }) + + .use(WebdavAuth, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts }) + .use(WebdavPublicLink, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts }) + .use(WebdavAuth, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts, cloudType: 'owncloud' }) + .use(WebdavPublicLink, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts, cloudType: 'owncloud' }) + .use(WebdavAuth, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts, cloudType: 'nextcloud' }) + .use(WebdavPublicLink, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts, cloudType: 'nextcloud' }) + // .use(Instagram, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts }) // .use(Dropbox, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts }) // .use(Box, { target: Dashboard, companionUrl: COMPANION_URL, companionAllowedHosts }) diff --git a/yarn.lock b/yarn.lock index 717947c1e9..50e29bc7dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4241,6 +4241,18 @@ __metadata: languageName: node linkType: hard +"@buttercup/fetch@npm:^0.1.1": + version: 0.1.1 + resolution: "@buttercup/fetch@npm:0.1.1" + dependencies: + node-fetch: ^3.3.0 + dependenciesMeta: + node-fetch: + optional: true + checksum: 1b1aebc32d9e001f15102eadfa6fc1598900e2873a6493024c7d46d200cd8283f1461464e4e90ef7e86abb7524fad28fd224620152c01fe8fc7e6dd00ef227fd + languageName: node + linkType: hard + "@cnakazawa/watch@npm:^1.0.3": version: 1.0.4 resolution: "@cnakazawa/watch@npm:1.0.4" @@ -5165,6 +5177,13 @@ __metadata: languageName: node linkType: hard +"@ioredis/commands@npm:^1.1.1": + version: 1.2.0 + resolution: "@ioredis/commands@npm:1.2.0" + checksum: 9b20225ba36ef3e5caf69b3c0720597c3016cc9b1e157f519ea388f621dd9037177f84cfe7e25c4c32dad7dd90c70ff9123cd411f747e053cf292193c9c461e2 + languageName: node + linkType: hard + "@istanbuljs/load-nyc-config@npm:^1.0.0": version: 1.1.0 resolution: "@istanbuljs/load-nyc-config@npm:1.1.0" @@ -7578,62 +7597,6 @@ __metadata: languageName: node linkType: hard -"@redis/bloom@npm:1.0.2": - version: 1.0.2 - resolution: "@redis/bloom@npm:1.0.2" - peerDependencies: - "@redis/client": ^1.0.0 - checksum: 4872e7e5e4ff03d63349ca88d3164d487f62805651ada91924de2592995993401c98a01cb93bff8d71e9a2e54985b2485b6cb0e084a7e8b1283e2ebb8bc4b833 - languageName: node - linkType: hard - -"@redis/client@npm:1.2.0": - version: 1.2.0 - resolution: "@redis/client@npm:1.2.0" - dependencies: - cluster-key-slot: 1.1.0 - generic-pool: 3.8.2 - yallist: 4.0.0 - checksum: 098a550a6728d9d3babb432a1c32f1678cbda5ad7fa3d59316fc334be42ef05d778fc4cf2e3d873a34817447870a5fc0486e62793698a279404b3b086ba7422e - languageName: node - linkType: hard - -"@redis/graph@npm:1.0.1": - version: 1.0.1 - resolution: "@redis/graph@npm:1.0.1" - peerDependencies: - "@redis/client": ^1.0.0 - checksum: 72e485efa416bdff10420f6e13c9cb4e1e5c70752e5172717adf62fc1d4d9ba12e708229fd87876f3a93270ff74c4bcd4d916987438dc36a94f7f12c9785fa44 - languageName: node - linkType: hard - -"@redis/json@npm:1.0.3": - version: 1.0.3 - resolution: "@redis/json@npm:1.0.3" - peerDependencies: - "@redis/client": ^1.0.0 - checksum: 26a7003c2fbacfa5998671e3a301cb2285432bf90f237adedcf76c0be0d379528e6710d469a8ea93c04bbd22951f9c2f41d460dbd79e85856f199248c4a250d5 - languageName: node - linkType: hard - -"@redis/search@npm:1.0.6": - version: 1.0.6 - resolution: "@redis/search@npm:1.0.6" - peerDependencies: - "@redis/client": ^1.0.0 - checksum: 5c776143520b11ae2e49a05e7fe3df514a01460f2be90759b15e4f097bf4a985784c48c2184ac2c275ced3ec5a0c77b208a4d46a50161d1ad6025e3ab2990aa7 - languageName: node - linkType: hard - -"@redis/time-series@npm:1.0.3": - version: 1.0.3 - resolution: "@redis/time-series@npm:1.0.3" - peerDependencies: - "@redis/client": ^1.0.0 - checksum: 4d11518185dd15f31c5b4a433902e53a3ebc24614a0221080ab12abf4f6fc60b3db00a71a83de7b4b10f11077de611dc1c273274573646d63481d40ca246f82d - languageName: node - linkType: hard - "@reduxjs/toolkit@npm:^1.9.3": version: 1.9.3 resolution: "@reduxjs/toolkit@npm:1.9.3" @@ -11067,11 +11030,13 @@ __metadata: express-prom-bundle: 6.5.0 express-request-id: 1.4.1 express-session: 1.17.3 + fast-xml-parser: ^4.2.5 form-data: ^3.0.0 got: 11 grant: 5.4.21 helmet: ^4.6.0 into-stream: ^6.0.0 + ioredis: 5.3.2 ipaddr.js: ^2.0.1 jest: ^29.0.0 jsonwebtoken: 8.5.1 @@ -11084,7 +11049,6 @@ __metadata: nock: ^13.1.3 node-schedule: 2.1.0 prom-client: 14.0.1 - redis: 4.2.0 semver: 7.5.3 serialize-error: ^2.1.0 serialize-javascript: ^6.0.0 @@ -11092,6 +11056,7 @@ __metadata: tus-js-client: ^3.0.0 typescript: ~4.8 validator: ^13.0.0 + webdav: ^5.2.2 ws: 8.8.1 bin: companion: ./bin/companion @@ -11585,6 +11550,19 @@ __metadata: languageName: unknown linkType: soft +"@uppy/webdav@workspace:packages/@uppy/webdav": + version: 0.0.0-use.local + resolution: "@uppy/webdav@workspace:packages/@uppy/webdav" + dependencies: + "@uppy/companion-client": "workspace:^" + "@uppy/provider-views": "workspace:^" + "@uppy/utils": "workspace:^" + preact: ^10.5.13 + peerDependencies: + "@uppy/core": "workspace:^" + languageName: unknown + linkType: soft + "@uppy/xhr-upload@workspace:*, @uppy/xhr-upload@workspace:^, @uppy/xhr-upload@workspace:packages/@uppy/xhr-upload": version: 0.0.0-use.local resolution: "@uppy/xhr-upload@workspace:packages/@uppy/xhr-upload" @@ -13800,6 +13778,13 @@ __metadata: languageName: node linkType: hard +"base-64@npm:^1.0.0": + version: 1.0.0 + resolution: "base-64@npm:1.0.0" + checksum: d10b64a1fc9b2c5a5f39f1ce1e6c9d1c5b249222bbfa3a0604c592d90623caf74419983feadd8a170f27dc0c3389704f72faafa3e645aeb56bfc030c93ff074a + languageName: node + linkType: hard + "base-x@npm:^3.0.8": version: 3.0.9 resolution: "base-x@npm:3.0.9" @@ -14429,6 +14414,13 @@ __metadata: languageName: node linkType: hard +"byte-length@npm:^1.0.2": + version: 1.0.2 + resolution: "byte-length@npm:1.0.2" + checksum: 69e2b00a14a81f675ea9946135c42ee1a1d9f689d5ba1327eb6700fcde2ccacbd09b42f7e514de1d2b763960251d8c790b3d7304a5a1a27b1457e34c129be8c7 + languageName: node + linkType: hard + "bytes@npm:3.0.0": version: 3.0.0 resolution: "bytes@npm:3.0.0" @@ -14860,6 +14852,13 @@ __metadata: languageName: node linkType: hard +"charenc@npm:0.0.2": + version: 0.0.2 + resolution: "charenc@npm:0.0.2" + checksum: 81dcadbe57e861d527faf6dd3855dc857395a1c4d6781f4847288ab23cffb7b3ee80d57c15bba7252ffe3e5e8019db767757ee7975663ad2ca0939bb8fcaf2e5 + languageName: node + linkType: hard + "check-more-types@npm:2.24.0, check-more-types@npm:^2.24.0": version: 2.24.0 resolution: "check-more-types@npm:2.24.0" @@ -15192,10 +15191,10 @@ __metadata: languageName: node linkType: hard -"cluster-key-slot@npm:1.1.0": - version: 1.1.0 - resolution: "cluster-key-slot@npm:1.1.0" - checksum: fc953c75209b1ef9088081bab4e40a0b2586491c974ab93460569c014515ca5a2e31c043f185285e177007162fc353d07836d98f570c171dbe055775430e495b +"cluster-key-slot@npm:^1.1.0": + version: 1.1.2 + resolution: "cluster-key-slot@npm:1.1.2" + checksum: be0ad2d262502adc998597e83f9ded1b80f827f0452127c5a37b22dfca36bab8edf393f7b25bb626006fb9fb2436106939ede6d2d6ecf4229b96a47f27edd681 languageName: node linkType: hard @@ -16002,6 +16001,13 @@ __metadata: languageName: node linkType: hard +"crypt@npm:0.0.2": + version: 0.0.2 + resolution: "crypt@npm:0.0.2" + checksum: baf4c7bbe05df656ec230018af8cf7dbe8c14b36b98726939cef008d473f6fe7a4fad906cfea4062c93af516f1550a3f43ceb4d6615329612c6511378ed9fe34 + languageName: node + linkType: hard + "crypto-browserify@npm:^3.11.0": version: 3.12.0 resolution: "crypto-browserify@npm:3.12.0" @@ -16822,6 +16828,13 @@ __metadata: languageName: node linkType: hard +"denque@npm:^2.1.0": + version: 2.1.0 + resolution: "denque@npm:2.1.0" + checksum: 1d4ae1d05e59ac3a3481e7b478293f4b4c813819342273f3d5b826c7ffa9753c520919ba264f377e09108d24ec6cf0ec0ac729a5686cbb8f32d797126c5dae74 + languageName: node + linkType: hard + "depd@npm:2.0.0, depd@npm:~2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" @@ -19741,7 +19754,7 @@ __metadata: languageName: node linkType: hard -"fast-xml-parser@npm:4.2.5": +"fast-xml-parser@npm:4.2.5, fast-xml-parser@npm:^4.2.4, fast-xml-parser@npm:^4.2.5": version: 4.2.5 resolution: "fast-xml-parser@npm:4.2.5" dependencies: @@ -20534,13 +20547,6 @@ __metadata: languageName: node linkType: hard -"generic-pool@npm:3.8.2": - version: 3.8.2 - resolution: "generic-pool@npm:3.8.2" - checksum: f549077d90265e5e4d32a2410205b357ec61cf73d17861f1013637984390e09fe7bf537129a2c6ed30ae57662a57c8d54194f80046408d3349836330f422dbde - languageName: node - linkType: hard - "gensync@npm:^1.0.0-beta.1, gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -21373,6 +21379,13 @@ __metadata: languageName: node linkType: hard +"hot-patcher@npm:^2.0.0": + version: 2.0.0 + resolution: "hot-patcher@npm:2.0.0" + checksum: 0a013fc4dd654ec11578e9e5a54c59c58879dd0eb0cf2917bef52c959fba4d429aa392ea970fa2d1dbb765c3b5d4874339bbd53166c438894a4eb5538675f4f6 + languageName: node + linkType: hard + "hpack.js@npm:^2.1.6": version: 2.1.6 resolution: "hpack.js@npm:2.1.6" @@ -22150,6 +22163,23 @@ __metadata: languageName: node linkType: hard +"ioredis@npm:5.3.2": + version: 5.3.2 + resolution: "ioredis@npm:5.3.2" + dependencies: + "@ioredis/commands": ^1.1.1 + cluster-key-slot: ^1.1.0 + debug: ^4.3.4 + denque: ^2.1.0 + lodash.defaults: ^4.2.0 + lodash.isarguments: ^3.1.0 + redis-errors: ^1.2.0 + redis-parser: ^3.0.0 + standard-as-callback: ^2.1.0 + checksum: 9a23559133e862a768778301efb68ae8c2af3c33562174b54a4c2d6574b976e85c75a4c34857991af733e35c48faf4c356e7daa8fb0a3543d85ff1768c8754bc + languageName: node + linkType: hard + "ip@npm:^1.1.5": version: 1.1.8 resolution: "ip@npm:1.1.8" @@ -22299,7 +22329,7 @@ __metadata: languageName: node linkType: hard -"is-buffer@npm:^1.1.5": +"is-buffer@npm:^1.1.5, is-buffer@npm:~1.1.6": version: 1.1.6 resolution: "is-buffer@npm:1.1.6" checksum: 4a186d995d8bbf9153b4bd9ff9fd04ae75068fe695d29025d25e592d9488911eeece84eefbd8fa41b8ddcc0711058a71d4c466dcf6f1f6e1d83830052d8ca707 @@ -24364,6 +24394,13 @@ __metadata: languageName: node linkType: hard +"layerr@npm:^0.1.2": + version: 0.1.2 + resolution: "layerr@npm:0.1.2" + checksum: db34bae003a8f289c858a7657c260104e40c4fdb6df8d35badbc42dc3ffe7d361c6dbf54f0c91cdaf6f364788b8af432131448dd9a240b82b8b2502b917fa052 + languageName: node + linkType: hard + "lazy-ass@npm:1.6.0, lazy-ass@npm:^1.6.0": version: 1.6.0 resolution: "lazy-ass@npm:1.6.0" @@ -24909,6 +24946,13 @@ __metadata: languageName: node linkType: hard +"lodash.defaults@npm:^4.2.0": + version: 4.2.0 + resolution: "lodash.defaults@npm:4.2.0" + checksum: 84923258235592c8886e29de5491946ff8c2ae5c82a7ac5cddd2e3cb697e6fbdfbbb6efcca015795c86eec2bb953a5a2ee4016e3735a3f02720428a40efbb8f1 + languageName: node + linkType: hard + "lodash.frompairs@npm:^4.0.1": version: 4.0.1 resolution: "lodash.frompairs@npm:4.0.1" @@ -24923,6 +24967,13 @@ __metadata: languageName: node linkType: hard +"lodash.isarguments@npm:^3.1.0": + version: 3.1.0 + resolution: "lodash.isarguments@npm:3.1.0" + checksum: ae1526f3eb5c61c77944b101b1f655f846ecbedcb9e6b073526eba6890dc0f13f09f72e11ffbf6540b602caee319af9ac363d6cdd6be41f4ee453436f04f13b5 + languageName: node + linkType: hard + "lodash.isboolean@npm:^3.0.3": version: 3.0.3 resolution: "lodash.isboolean@npm:3.0.3" @@ -25469,6 +25520,17 @@ __metadata: languageName: node linkType: hard +"md5@npm:^2.3.0": + version: 2.3.0 + resolution: "md5@npm:2.3.0" + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + is-buffer: ~1.1.6 + checksum: a63cacf4018dc9dee08c36e6f924a64ced735b37826116c905717c41cebeb41a522f7a526ba6ad578f9c80f02cb365033ccd67fe186ffbcc1a1faeb75daa9b6e + languageName: node + linkType: hard + "mdast-comment-marker@npm:^2.0.0, mdast-comment-marker@npm:^2.1.0": version: 2.1.0 resolution: "mdast-comment-marker@npm:2.1.0" @@ -26709,6 +26771,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^5.1.0": + version: 5.1.6 + resolution: "minimatch@npm:5.1.6" + dependencies: + brace-expansion: ^2.0.1 + checksum: 7564208ef81d7065a370f788d337cd80a689e981042cb9a1d0e6580b6c6a8c9279eba80010516e258835a988363f99f54a6f711a315089b8b42694f5da9d0d77 + languageName: node + linkType: hard + "minimatch@npm:~3.0.2, minimatch@npm:~3.0.4": version: 3.0.8 resolution: "minimatch@npm:3.0.8" @@ -27173,6 +27244,13 @@ __metadata: languageName: node linkType: hard +"nested-property@npm:^4.0.0": + version: 4.0.0 + resolution: "nested-property@npm:4.0.0" + checksum: 9c86f2c722429e167876d5becf276139a6aa4b8732b6d9e32de9aa44dfd017702b60614cc87aec961dea47ae50dae0951d5b5f66fc30288f18bf581c16e42ca2 + languageName: node + linkType: hard + "next-tick@npm:^1.1.0": version: 1.1.0 resolution: "next-tick@npm:1.1.0" @@ -27388,6 +27466,17 @@ __metadata: languageName: node linkType: hard +"node-fetch@npm:^3.3.0": + version: 3.3.1 + resolution: "node-fetch@npm:3.3.1" + dependencies: + data-uri-to-buffer: ^4.0.0 + fetch-blob: ^3.1.4 + formdata-polyfill: ^4.0.10 + checksum: 62145fd3ba4770a76110bc31fdc0054ab2f5442b5ce96e9c4b39fc9e94a3d305560eec76e1165d9259eab866e02a8eecf9301062bb5dfc9f08a4d08b69d223dd + languageName: node + linkType: hard + "node-forge@npm:^1": version: 1.3.1 resolution: "node-forge@npm:1.3.1" @@ -28851,6 +28940,13 @@ __metadata: languageName: node linkType: hard +"path-posix@npm:^1.0.0": + version: 1.0.0 + resolution: "path-posix@npm:1.0.0" + checksum: 4f64ad212de6ad8d0dbfa440cac8b924303c25c30301769ad0501e29e83a5b9d469e8133753f999ad37482c9c8d3511129e4d83db55d2e4b1555b183c9749ae8 + languageName: node + linkType: hard + "path-to-regexp@npm:0.1.7": version: 0.1.7 resolution: "path-to-regexp@npm:0.1.7" @@ -31205,17 +31301,19 @@ __metadata: languageName: node linkType: hard -"redis@npm:4.2.0": - version: 4.2.0 - resolution: "redis@npm:4.2.0" +"redis-errors@npm:^1.0.0, redis-errors@npm:^1.2.0": + version: 1.2.0 + resolution: "redis-errors@npm:1.2.0" + checksum: f28ac2692113f6f9c222670735aa58aeae413464fd58ccf3fce3f700cae7262606300840c802c64f2b53f19f65993da24dc918afc277e9e33ac1ff09edb394f4 + languageName: node + linkType: hard + +"redis-parser@npm:^3.0.0": + version: 3.0.0 + resolution: "redis-parser@npm:3.0.0" dependencies: - "@redis/bloom": 1.0.2 - "@redis/client": 1.2.0 - "@redis/graph": 1.0.1 - "@redis/json": 1.0.3 - "@redis/search": 1.0.6 - "@redis/time-series": 1.0.3 - checksum: 6c35b56c6b685e82973c5698c5736c07ccbc59f3bc18d8de61e45ead2df4b8fc82062e5618452f4d3f8c23f02ff8c0b847c8d6a681f909c403a0fb96adcc2b98 + redis-errors: ^1.0.0 + checksum: 89290ae530332f2ae37577647fa18208d10308a1a6ba750b9d9a093e7398f5e5253f19855b64c98757f7129cccce958e4af2573fdc33bad41405f87f1943459a languageName: node linkType: hard @@ -31750,14 +31848,14 @@ __metadata: linkType: hard "remark@npm:^14.0.0": - version: 14.0.2 - resolution: "remark@npm:14.0.2" + version: 14.0.3 + resolution: "remark@npm:14.0.3" dependencies: "@types/mdast": ^3.0.0 remark-parse: ^10.0.0 remark-stringify: ^10.0.0 unified: ^10.0.0 - checksum: a534b6c6bb8a0db7e22580c73d820af9c2468af24d434eb0d54c9b50577d609ec5383a90e81c921f334c1426494c5cf7460ae68b5027123f159bbf1ca8a5a85f + checksum: 36eec9668c5f5e497507fa5d396c79183265a5f7dd204a608e7f031a4f61b48f7bb5cfaec212f5614ccd1266cc4a9f8d7a59a45e95aed9876986b4c453b191be languageName: node linkType: hard @@ -33679,6 +33777,13 @@ __metadata: languageName: node linkType: hard +"standard-as-callback@npm:^2.1.0": + version: 2.1.0 + resolution: "standard-as-callback@npm:2.1.0" + checksum: 88bec83ee220687c72d94fd86a98d5272c91d37ec64b66d830dbc0d79b62bfa6e47f53b71646011835fc9ce7fae62739545d13124262b53be4fbb3e2ebad551c + languageName: node + linkType: hard + "start-server-and-test@npm:1.14.0": version: 1.14.0 resolution: "start-server-and-test@npm:1.14.0" @@ -36240,6 +36345,13 @@ __metadata: languageName: node linkType: hard +"url-join@npm:^4.0.1": + version: 4.0.1 + resolution: "url-join@npm:4.0.1" + checksum: f74e868bf25dbc8be6a8d7237d4c36bb5b6c62c72e594d5ab1347fe91d6af7ccd9eb5d621e30152e4da45c2e9a26bec21390e911ab54a62d4d82e76028374ee5 + languageName: node + linkType: hard + "url-loader@npm:^4.1.1": version: 4.1.1 resolution: "url-loader@npm:4.1.1" @@ -36257,7 +36369,7 @@ __metadata: languageName: node linkType: hard -"url-parse@npm:^1.4.4, url-parse@npm:^1.5.3, url-parse@npm:^1.5.7": +"url-parse@npm:^1.4.4, url-parse@npm:^1.5.10, url-parse@npm:^1.5.3, url-parse@npm:^1.5.7": version: 1.5.10 resolution: "url-parse@npm:1.5.10" dependencies: @@ -36911,6 +37023,48 @@ __metadata: languageName: node linkType: hard +"webdav@npm:5.2.2": + version: 5.2.2 + resolution: "webdav@npm:5.2.2" + dependencies: + "@buttercup/fetch": ^0.1.1 + base-64: ^1.0.0 + byte-length: ^1.0.2 + fast-xml-parser: ^4.2.4 + he: ^1.2.0 + hot-patcher: ^2.0.0 + layerr: ^0.1.2 + md5: ^2.3.0 + minimatch: ^5.1.0 + nested-property: ^4.0.0 + path-posix: ^1.0.0 + url-join: ^4.0.1 + url-parse: ^1.5.10 + checksum: a5621a150ecb81d906fa61703f0ea5a9584ac11a9cb1fc5566c800055fe19b596c729c6f57a7f817b67e2121386220810c1ffff54102b224802a23a068c0ddd2 + languageName: node + linkType: hard + +"webdav@patch:webdav@npm%3A5.2.2#./.yarn/patches/webdav-npm-5.2.2-791e72c3de.patch::locator=%40uppy-dev%2Fbuild%40workspace%3A.": + version: 5.2.2 + resolution: "webdav@patch:webdav@npm%3A5.2.2#./.yarn/patches/webdav-npm-5.2.2-791e72c3de.patch::version=5.2.2&hash=b1e9cf&locator=%40uppy-dev%2Fbuild%40workspace%3A." + dependencies: + "@buttercup/fetch": ^0.1.1 + base-64: ^1.0.0 + byte-length: ^1.0.2 + fast-xml-parser: ^4.2.4 + he: ^1.2.0 + hot-patcher: ^2.0.0 + layerr: ^0.1.2 + md5: ^2.3.0 + minimatch: ^5.1.0 + nested-property: ^4.0.0 + path-posix: ^1.0.0 + url-join: ^4.0.1 + url-parse: ^1.5.10 + checksum: 8dada8867e9b55a3a5949b32df5f5bd4add7486141c513a2b49ca218b0e1f887a7d42014bdb15e398100de9da68f69228f6ad3d492356d5467ca0de50852f7d4 + languageName: node + linkType: hard + "webidl-conversions@npm:^3.0.0": version: 3.0.1 resolution: "webidl-conversions@npm:3.0.1" @@ -37690,13 +37844,6 @@ __metadata: languageName: node linkType: hard -"yallist@npm:4.0.0, yallist@npm:^4.0.0": - version: 4.0.0 - resolution: "yallist@npm:4.0.0" - checksum: 343617202af32df2a15a3be36a5a8c0c8545208f3d3dfbc6bb7c3e3b7e8c6f8e7485432e4f3b88da3031a6e20afa7c711eded32ddfb122896ac5d914e75848d5 - languageName: node - linkType: hard - "yallist@npm:^2.1.2": version: 2.1.2 resolution: "yallist@npm:2.1.2" @@ -37711,6 +37858,13 @@ __metadata: languageName: node linkType: hard +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 343617202af32df2a15a3be36a5a8c0c8545208f3d3dfbc6bb7c3e3b7e8c6f8e7485432e4f3b88da3031a6e20afa7c711eded32ddfb122896ac5d914e75848d5 + languageName: node + linkType: hard + "yaml@npm:^1.10.0, yaml@npm:^1.10.2, yaml@npm:^1.7.2": version: 1.10.2 resolution: "yaml@npm:1.10.2"