Skip to content

Commit

Permalink
fixup! feat(webdav): initial implementation for a WebDAV simple auth …
Browse files Browse the repository at this point in the history
…provider
  • Loading branch information
dschmidt committed Dec 2, 2024
1 parent 105b2ed commit 1bbe4f8
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 140 deletions.
125 changes: 0 additions & 125 deletions packages/@uppy/companion/src/server/provider/webdav/common.js

This file was deleted.

111 changes: 107 additions & 4 deletions packages/@uppy/companion/src/server/provider/webdav/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

const { validateURL } = require('../../helpers/request')
const WebdavProvider = require('./common')
const Provider = require('../Provider')
const { getProtectedHttpAgent, validateURL } = require('../../helpers/request')
const { ProviderApiError, ProviderAuthError } = require('../error')
const { ProviderUserError } = require('../error')
const logger = require('../../logger')

Expand All @@ -9,7 +10,7 @@ const defaultDirectory = '/'
/**
* Adapter for WebDAV servers that support simple auth (non-OAuth).
*/
class WebdavSimpleAuthProvider extends WebdavProvider {
class WebdavProvider extends Provider {
static get hasSimpleAuth () {
return true
}
Expand All @@ -30,6 +31,9 @@ class WebdavSimpleAuthProvider extends WebdavProvider {
throw new Error('invalid public link url')
}

// dynamic import because Comanion currently uses CommonJS and webdav is shipped as ESM
// can be implemented as regular require as soon as Node 20.17 or 22 is required
// or as regular import when Companion is ported to ESM
const { AuthType } = await import('webdav') // eslint-disable-line import/no-unresolved

// Is this an ownCloud or Nextcloud public link URL? e.g. https://example.com/s/kFy9Lek5sm928xP
Expand Down Expand Up @@ -75,6 +79,105 @@ class WebdavSimpleAuthProvider extends WebdavProvider {
throw err
}
}

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, allowLocalIPs: !allowLocalUrls })

// dynamic import because Comanion currently uses CommonJS and webdav is shipped as ESM
// can be implemented as regular require as soon as Node 20.17 or 22 is required
// or as regular import when Companion is ported to ESM
const { createClient } = await import('webdav')
return createClient(url, {
...options,
[`${protocol}Agent`] : new HttpAgentClass(),
})
}

async list ({ directory, token, providerUserSession }) {
return this.withErrorHandling('provider.webdav.list.error', async () => {
// @ts-ignore
if (!this.isAuthenticated({ providerUserSession })) {
throw new ProviderAuthError()
}

const username = await this.getUsername({ token, providerUserSession })
const data = { username, items: [] }
const client = await this.getClient({ username, token, providerUserSession })

/** @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 FIXME
modifiedDate: item.lastmod, // TODO FIXME: 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, providerUserSession }) {
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, providerUserSession })
const client = await this.getClient({ username, token, providerUserSession })
const stream = client.createReadStream(`/${id}`)
return { stream }
})
}

// eslint-disable-next-line
async thumbnail ({ id, providerUserSession }) {
// 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')
}

// todo fixme implement
// eslint-disable-next-line
async size ({ id, token, providerUserSession }) {
return this.withErrorHandling('provider.webdav.size.error', async () => {
const username = await this.getUsername({ token, providerUserSession })
const client = await this.getClient({ username, token, providerUserSession })

/** @type {any} */
const stat = await client.stat(id)
return stat.size
})
}

// eslint-disable-next-line class-methods-use-this
async withErrorHandling (tag, fn) {
try {
return await fn()
} catch (err) {
let err2 = err
if (err.status === 401) err2 = new ProviderAuthError()
if (err.response) {
err2 = new ProviderApiError('WebDAV API error', err.status) // todo improve (read err?.response?.body readable stream and parse response)
}
logger.error(err2, tag)
throw err2
}
}
}

module.exports = WebdavSimpleAuthProvider
module.exports = WebdavProvider
4 changes: 2 additions & 2 deletions packages/@uppy/webdav/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@uppy/webdav",
"description": "Import files from WebDAV into Uppy.",
"version": "3.1.1",
"version": "0.1.0",
"license": "MIT",
"main": "lib/index.js",
"types": "types/index.d.ts",
Expand All @@ -10,7 +10,7 @@
"file uploader",
"uppy",
"uppy-plugin",
"instagram",
"webdav",
"provider",
"photos",
"videos"
Expand Down
14 changes: 5 additions & 9 deletions packages/@uppy/webdav/src/Webdav.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { h } from 'preact'
import { useCallback, useState } from 'preact/hooks'
import { useState } from 'preact/hooks'

import { UIPlugin } from '@uppy/core'
import { Provider, tokenStorage } from '@uppy/companion-client'
Expand All @@ -22,13 +22,10 @@ class WebdavSimpleAuthProvider extends Provider {
const AuthForm = ({ loading, i18n, onAuth }) => {
const [webdavUrl, setWebdavUrl] = useState('')

const onSubmit = useCallback(
(e) => {
e.preventDefault()
onAuth({ webdavUrl: webdavUrl.trim() })
},
[onAuth, webdavUrl],
)
const onSubmit = (event) => {
event.preventDefault()
onAuth({ webdavUrl: webdavUrl.trim() })
}

return (
<form onSubmit={onSubmit}>
Expand Down Expand Up @@ -65,7 +62,6 @@ export default class Webdav extends UIPlugin {

this.defaultLocale = locale

console.log(locale)
this.i18nInit()

this.title = this.i18n('pluginNameWebdav')
Expand Down

0 comments on commit 1bbe4f8

Please sign in to comment.