diff --git a/package.json b/package.json index 93e586d..6046917 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "@octokit/rest": "^19.0.7", "@picgo/i18n": "^1.0.0", "@picgo/store": "^2.1.0", - "@smithy/node-http-handler": "^2.1.2", + "@smithy/node-http-handler": "^2.1.3", "@types/mime-types": "^2.1.1", "@videojs-player/vue": "^1.0.0", "ali-oss": "^6.18.1", @@ -68,7 +68,7 @@ "mitt": "^3.0.1", "node-ssh-no-cpu-features": "^1.0.1", "nodejs-file-downloader": "^4.12.1", - "piclist": "^1.1.0", + "piclist": "^1.1.1", "pinia": "^2.1.6", "pinia-plugin-persistedstate": "^3.2.0", "qiniu": "^7.9.0", @@ -126,7 +126,7 @@ "eslint-plugin-vue": "^9.17.0", "husky": "^3.1.0", "node-loader": "^2.0.0", - "npm-check-updates": "^16.13.3", + "npm-check-updates": "^16.14.0", "stylus": "^0.59.0", "stylus-loader": "^7.1.3", "typescript": "^4.9.5", diff --git a/public/i18n/en.yml b/public/i18n/en.yml index e6d02b6..720cb2b 100644 --- a/public/i18n/en.yml +++ b/public/i18n/en.yml @@ -617,6 +617,7 @@ MANAGE_CONSTANT_WEBDAV_PROXY_PLACEHOLDER: 'e.g. http://127.0.0.1:1080' MANAGE_CONSTANT_WEBDAV_PROXY_TOOLTIP: If special network environment is required to access, please use proxy MANAGE_CONSTANT_WEBDAV_SSL_DESC: Use HTTPS Connection MANAGE_CONSTANT_WEBDAV_SSL_TOOLTIP: Depending on the configuration of your WebDAV server, if your server does not support HTTPS, please turn off this option +MANAGE_CONSTANT_WEBDAV_AUTH_TYPE_DESC: Authentication Type MANAGE_CONSTANT_WEBDAV_EXPLAIN: 'WebDAV Configuration' MANAGE_CONSTANT_WEBDAV_REFER_TEXT: 'Refer to:' diff --git a/public/i18n/zh-CN.yml b/public/i18n/zh-CN.yml index 47fb6a7..73e0e70 100644 --- a/public/i18n/zh-CN.yml +++ b/public/i18n/zh-CN.yml @@ -620,6 +620,7 @@ MANAGE_CONSTANT_WEBDAV_PROXY_PLACEHOLDER: '例如:http://127.0.0.1:1080' MANAGE_CONSTANT_WEBDAV_PROXY_TOOLTIP: 如果需要特殊网络环境才能访问,请使用代理 MANAGE_CONSTANT_WEBDAV_SSL_DESC: 使用HTTPS连接 MANAGE_CONSTANT_WEBDAV_SSL_TOOLTIP: 根据WebDAV服务器的配置,如果您的服务器不支持HTTPS,请关闭该选项 +MANAGE_CONSTANT_WEBDAV_AUTH_TYPE_DESC: 认证类型 MANAGE_CONSTANT_WEBDAV_EXPLAIN: 'WebDAV配置' MANAGE_CONSTANT_WEBDAV_REFER_TEXT: '配置教程请参考: ' diff --git a/public/i18n/zh-TW.yml b/public/i18n/zh-TW.yml index d5e39ea..848410d 100644 --- a/public/i18n/zh-TW.yml +++ b/public/i18n/zh-TW.yml @@ -617,6 +617,7 @@ MANAGE_CONSTANT_WEBDAV_PROXY_PLACEHOLDER: '例如:http://127.0.0.1:1080' MANAGE_CONSTANT_WEBDAV_PROXY_TOOLTIP: 如果需要特殊網路環境才能訪問,請使用代理 MANAGE_CONSTANT_WEBDAV_SSL_DESC: 使用HTTPS連線 MANAGE_CONSTANT_WEBDAV_SSL_TOOLTIP: 根據WebDAV伺服器的配置,如果您的伺服器不支援HTTPS,請關閉該選項 +MANAGE_CONSTANT_WEBDAV_AUTH_TYPE_DESC: 認證類型 MANAGE_CONSTANT_WEBDAV_EXPLAIN: 'WebDAV配置' MANAGE_CONSTANT_WEBDAV_REFER_TEXT: '配置教程請參考: ' diff --git a/src/main/manage/apis/webdavplist.ts b/src/main/manage/apis/webdavplist.ts index 528b708..b29bf6b 100644 --- a/src/main/manage/apis/webdavplist.ts +++ b/src/main/manage/apis/webdavplist.ts @@ -2,7 +2,7 @@ import ManageLogger from '../utils/logger' // WebDAV 客户端库 -import { createClient, WebDAVClient, FileStat, ProgressEvent } from 'webdav' +import { createClient, WebDAVClient, FileStat, ProgressEvent, AuthType, WebDAVClientOptions } from 'webdav' // 错误格式化函数、端点地址格式化函数、获取内部代理、新的下载器、并发异步任务池 import { formatError, formatEndpoint, getInnerAgent, NewDownloader, ConcurrencyPromisePool } from '../utils/common' @@ -34,6 +34,7 @@ import path from 'path' // 取消下载任务的加载文件列表、刷新下载文件传输列表 import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' +import { getAuthHeader } from '@/manage/utils/digestAuth' class WebdavplistApi { endpoint: string @@ -42,29 +43,35 @@ class WebdavplistApi { sslEnabled: boolean proxy: string | undefined proxyStr: string | undefined + authType: 'basic' | 'digest' | undefined logger: ManageLogger agent: https.Agent | http.Agent ctx: WebDAVClient - constructor (endpoint: string, username: string, password: string, sslEnabled: boolean, proxy: string | undefined, logger: ManageLogger) { + constructor (endpoint: string, username: string, password: string, sslEnabled: boolean, proxy: string | undefined, authType: 'basic' | 'digest' | undefined, logger: ManageLogger) { this.endpoint = formatEndpoint(endpoint, sslEnabled) this.username = username this.password = password this.sslEnabled = sslEnabled this.proxy = proxy this.proxyStr = formatHttpProxy(proxy, 'string') as string | undefined + this.authType = authType || 'basic' this.logger = logger this.agent = getInnerAgent(proxy, sslEnabled).agent + const options: WebDAVClientOptions = { + username: this.username, + password: this.password, + maxBodyLength: 4 * 1024 * 1024 * 1024, + maxContentLength: 4 * 1024 * 1024 * 1024, + httpsAgent: sslEnabled ? this.agent : undefined, + httpAgent: !sslEnabled ? this.agent : undefined + } + if (this.authType === 'digest') { + options.authType = AuthType.Digest + } this.ctx = createClient( this.endpoint, - { - username: this.username, - password: this.password, - maxBodyLength: 4 * 1024 * 1024 * 1024, - maxContentLength: 4 * 1024 * 1024 * 1024, - httpsAgent: sslEnabled ? this.agent : undefined, - httpAgent: !sslEnabled ? this.agent : undefined - } + options ) } @@ -275,7 +282,7 @@ class WebdavplistApi { }) this.ctx.putFileContents( key, - fs.createReadStream(filePath), + this.authType === 'digest' ? fs.readFileSync(filePath) : fs.createReadStream(filePath), { overwrite: true, onUploadProgress: (progressEvent: ProgressEvent) => { @@ -347,12 +354,21 @@ class WebdavplistApi { sourceFileName: fileName, targetFilePath: savedFilePath }) - const preSignedUrl = await this.getPreSignedUrl({ + let preSignedUrl = await this.getPreSignedUrl({ key }) - const base64Str = Buffer.from(`${this.username}:${this.password}`).toString('base64') - const headers = { - Authorization: `Basic ${base64Str}` + let headers = {} as IStringKeyMap + if (this.authType === 'basic' || !this.authType) { + const base64Str = Buffer.from(`${this.username}:${this.password}`).toString('base64') + headers = { + Authorization: `Basic ${base64Str}` + } + } else if (this.authType === 'digest') { + const authHeader = await getAuthHeader('GET', this.endpoint, `/${key.replace(/^\/+/, '')}`, this.username, this.password) + headers = { + Authorization: authHeader + } + preSignedUrl = `${this.endpoint}/${key.replace(/^\/+/, '')}` } promises.push(() => new Promise((resolve, reject) => { NewDownloader(instance, preSignedUrl, id, savedFilePath, this.logger, this.proxyStr, headers) diff --git a/src/main/manage/manageApi.ts b/src/main/manage/manageApi.ts index 4f67b7e..f6fcf7b 100644 --- a/src/main/manage/manageApi.ts +++ b/src/main/manage/manageApi.ts @@ -75,7 +75,7 @@ export class ManageApi extends EventEmitter implements ManageApiType { case 'upyun': return new API.UpyunApi(this.currentPicBedConfig.bucketName, this.currentPicBedConfig.operator, this.currentPicBedConfig.password, this.logger, this.currentPicBedConfig.antiLeechToken, this.currentPicBedConfig.expireTime) case 'webdavplist': - return new API.WebdavplistApi(this.currentPicBedConfig.endpoint, this.currentPicBedConfig.username, this.currentPicBedConfig.password, this.currentPicBedConfig.sslEnabled, this.currentPicBedConfig.proxy, this.logger) + return new API.WebdavplistApi(this.currentPicBedConfig.endpoint, this.currentPicBedConfig.username, this.currentPicBedConfig.password, this.currentPicBedConfig.sslEnabled, this.currentPicBedConfig.proxy, this.currentPicBedConfig.authType, this.logger) default: return {} as any } diff --git a/src/renderer/apis/webdav.ts b/src/renderer/apis/webdav.ts index 1ae8177..4ed3c25 100644 --- a/src/renderer/apis/webdav.ts +++ b/src/renderer/apis/webdav.ts @@ -1,16 +1,20 @@ -import { createClient } from 'webdav' +import { AuthType, WebDAVClientOptions, createClient } from 'webdav' import { formatEndpoint } from '~/main/manage/utils/common' export default class WebdavApi { static async delete (configMap: IStringKeyMap): Promise { - const { fileName, config: { host, username, password, path, sslEnabled } } = configMap + const { fileName, config: { host, username, password, path, sslEnabled, authType } } = configMap const endpoint = formatEndpoint(host, sslEnabled) + const options: WebDAVClientOptions = { + username, + password + } + if (authType === 'digest') { + options.authType = AuthType.Digest + } const ctx = createClient( endpoint, - { - username, - password - } + options ) let key if (path === '/' || !path) { diff --git a/src/renderer/components/ImageWebdav.vue b/src/renderer/components/ImageWebdav.vue index 307ad4a..b3f95fb 100644 --- a/src/renderer/components/ImageWebdav.vue +++ b/src/renderer/components/ImageWebdav.vue @@ -23,6 +23,8 @@ import { ref, onMounted, watch, computed } from 'vue' import { getFileIconPath } from '@/manage/utils/common' import { Loading } from '@element-plus/icons-vue' +import { getAuthHeader } from '@/manage/utils/digestAuth' +import { formatEndpoint } from '~/main/manage/utils/common' const base64Url = ref('') const success = ref(false) @@ -41,7 +43,7 @@ const props = defineProps( type: String, required: true }, - headers: { + config: { type: Object, required: true } @@ -56,9 +58,31 @@ const imageSource = computed(() => { const iconPath = computed(() => require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)) +async function getheaderOfWebdav (key: string) { + let headers = {} as any + if (props.config.authType === 'digest') { + const authHeader = await getAuthHeader( + 'GET', + formatEndpoint(props.config.endpoint, props.config.sslEnabled || false), + `/${key.replace(/^\//, '')}`, + props.config.username, + props.config.password + ) + headers = { + Authorization: authHeader + } + } else { + headers = { + Authorization: 'Basic ' + Buffer.from(`${props.config.username}:${props.config.password}`).toString('base64') + } + } + return headers +} + const fetchImage = async () => { try { - const res = await fetch(props.url, { method: 'GET', headers: props.headers }) + const headers = await getheaderOfWebdav(props.item.key) + const res = await fetch(props.url, { method: 'GET', headers }) if (res.status >= 200 && res.status < 300) { const blob = await res.blob() success.value = true @@ -72,7 +96,7 @@ const fetchImage = async () => { } } -watch(() => [props.url, props.headers], fetchImage, { deep: true }) +watch(() => [props.url, props.item], fetchImage, { deep: true }) onMounted(fetchImage) diff --git a/src/renderer/components/ImageWebdavTsx.tsx b/src/renderer/components/ImageWebdavTsx.tsx new file mode 100644 index 0000000..41ff398 --- /dev/null +++ b/src/renderer/components/ImageWebdavTsx.tsx @@ -0,0 +1,102 @@ +import { defineComponent, ref, onMounted, watch, computed } from 'vue' +import { getFileIconPath } from '@/manage/utils/common' +import { Loading } from '@element-plus/icons-vue' +import { getAuthHeader } from '@/manage/utils/digestAuth' +import { formatEndpoint } from '~/main/manage/utils/common' +import { ElImage, ElIcon } from 'element-plus' + +export default defineComponent({ + props: { + isShowThumbnail: { + type: Boolean, + required: true + }, + item: { + type: Object, + required: true + }, + url: { + type: String, + required: true + }, + config: { + type: Object, + required: true + } + }, + + setup (props) { + const base64Url = ref('') + const success = ref(false) + + const imageSource = computed(() => { + return (props.isShowThumbnail && props.item.isImage && success.value) + ? base64Url.value + : require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`) + }) + const iconPath = computed(() => require(`../manage/pages/assets/icons/${getFileIconPath(props.item.fileName ?? '')}`)) + + async function getheaderOfWebdav (key: string) { + let headers = {} as any + if (props.config.authType === 'digest') { + const authHeader = await getAuthHeader( + 'GET', + formatEndpoint(props.config.endpoint, props.config.sslEnabled || false), + `/${key.replace(/^\//, '')}`, + props.config.username, + props.config.password + ) + headers = { + Authorization: authHeader + } + } else { + headers = { + Authorization: 'Basic ' + Buffer.from(`${props.config.username}:${props.config.password}`).toString('base64') + } + } + return headers + } + + const fetchImage = async () => { + try { + const headers = await getheaderOfWebdav(props.item.key) + const res = await fetch(props.url, { method: 'GET', headers }) + if (res.status >= 200 && res.status < 300) { + const blob = await res.blob() + success.value = true + base64Url.value = URL.createObjectURL(blob) + } else { + throw new Error('Network response was not ok.') + } + } catch (err) { + success.value = false + console.log(err) + } + } + watch(() => [props.url, props.item], fetchImage, { deep: true }) + onMounted(fetchImage) + + return () => ( + + {{ + placeholder: () => ( + + + + ), + error: () => ( + + ) + }} + + ) + } +}) diff --git a/src/renderer/manage/pages/bucketPage.vue b/src/renderer/manage/pages/bucketPage.vue index 943821a..0bed878 100644 --- a/src/renderer/manage/pages/bucketPage.vue +++ b/src/renderer/manage/pages/bucketPage.vue @@ -546,7 +546,7 @@ https://www.baidu.com/img/bd_logo1.png" v-else-if="!item.isDir && currentPicBedName === 'webdavplist' && item.isImage" :is-show-thumbnail="isShowThumbnail" :item="item" - :headers="getBase64ofWebdav()" + :config="handleGetWebdavConfig()" :url="item.url" @click="handleClickFile(item)" /> @@ -718,6 +718,7 @@ https://www.baidu.com/img/bd_logo1.png" :initial-index="getCurrentPreviewIndex" infinite hide-on-click-modal + teleported @close="isShowImagePreview = false" /> (() => manageStore.config.picBed[configMap.alias].picBedName) @@ -1711,6 +1715,10 @@ function stopRefreshUploadTask () { refreshUploadTaskId.value && clearInterval(refreshUploadTaskId.value) } +function handleGetWebdavConfig () { + return manageStore.config.picBed[configMap.alias] +} + // 下载相关函数 function showDownloadDialog () { @@ -1736,13 +1744,6 @@ function handleViewChange (val: 'list' | 'grid') { layoutStyle.value = val } -function getBase64ofWebdav () { - const headers = { - Authorization: 'Basic ' + Buffer.from(`${manageStore.config.picBed[configMap.alias].username}:${manageStore.config.picBed[configMap.alias].password}`).toString('base64') - } - return headers -} - // 上传文件选择相关 function openFileSelectDialog () { @@ -2468,13 +2469,13 @@ async function handleFolderBatchDownload (item: any) { ipcRenderer.on(refreshDownloadFileTransferList, (evt: IpcRendererEvent, data) => { downloadFileTransferStore.refreshDownloadFileTransferList(data) }) - const interval = setInterval(() => { + downloadInterval = setInterval(() => { const currentFileList = downloadFileTransferStore.getDownloadFileTransferList() currentDownloadFileList.length = 0 currentDownloadFileList.push(...currentFileList) - if (downloadFileTransferStore.isFinished()) { - clearInterval(interval) + if (downloadFileTransferStore.isFinished() && downloadInterval) { isLoadingDownloadData.value = false + clearInterval(downloadInterval) if (downloadFileTransferStore.isSuccess()) { ElNotification.success({ title: $T('MANAGE_BUCKET_DOWNLOAD_FOLDER_BOX_TIP'), @@ -2845,7 +2846,7 @@ async function getBucketFileListBackStage () { ipcRenderer.on('refreshFileTransferList', (evt: IpcRendererEvent, data) => { fileTransferStore.refreshFileTransferList(data) }) - const interval = setInterval(() => { + fileTransferInterval = setInterval(() => { const currentFileList = fileTransferStore.getFileTransferList() currentPageFilesInfo.splice(0, currentPageFilesInfo.length, ...currentFileList) const sortType = localStorage.getItem('sortType') as sortTypeList || 'init' @@ -2857,9 +2858,9 @@ async function getBucketFileListBackStage () { fullList: currentPageFilesInfo })) }) - if (fileTransferStore.isFinished()) { - clearInterval(interval) + if (fileTransferStore.isFinished() && fileTransferInterval) { isLoadingData.value = false + clearInterval(fileTransferInterval) if (fileTransferStore.isSuccess()) { ElNotification.success({ title: $T('MANAGE_BUCKET_GET_FILE_BS_NOT_TITLE'), @@ -3411,23 +3412,36 @@ const columns: Column[] = [ {{ reference: () => ( !item.isDir - ? - {{ - placeholder: () => - - , - error: () => - - }} - + ? currentPicBedName.value !== 'webdavplist' + ? + {{ + placeholder: () => + + , + error: () => + + }} + + : item.isImage + ? + : : [] = [ /> ), default: () => ( - - {{ - placeholder: () => ( - - - ), - error: () => ( - - + currentPicBedName.value === 'webdavplist' && item.isImage + ? + : + {{ + placeholder: () => ( + - ) - }} - + ), + error: () => ( + + + + ) + }} + ) }} @@ -3670,6 +3691,10 @@ onBeforeMount(async () => { onBeforeUnmount(() => { document.removeEventListener('keydown', handleDetectShiftKey) document.removeEventListener('keyup', handleDetectShiftKey) + fileTransferInterval && clearInterval(fileTransferInterval) + downloadInterval && clearInterval(downloadInterval) + refreshUploadTaskId.value && clearInterval(refreshUploadTaskId.value) + refreshDownloadTaskId.value && clearInterval(refreshDownloadTaskId.value) if (isLoadingData.value) { ipcRenderer.send('cancelLoadingFileList', cancelToken.value) } diff --git a/src/renderer/manage/pages/logIn.vue b/src/renderer/manage/pages/logIn.vue index 41a6cc2..0b64c94 100644 --- a/src/renderer/manage/pages/logIn.vue +++ b/src/renderer/manage/pages/logIn.vue @@ -576,17 +576,20 @@ async function getCurrentConfigList () { const config = configList[pb] return config?.configList?.length ? config.configList.map((item: any) => ({ ...item, type: pb })) : [] }) - await getAllConfigAliasArray() + const autoImport = await getPicBedsConfig('settings.autoImport') || false - if (!autoImport) return - const autoImportPicBed = initArray(await getPicBedsConfig('settings.autoImportPicBed') || '', []) - await Promise.all(filteredConfigList.flatMap((config) => transUpToManage(config, config.type, autoImportPicBed))) - if (Object.keys(importedNewConfig).length > 0) { - const oldConfig = await getConfig('picBed') - const newConfig = { ...oldConfig, ...importedNewConfig } - saveConfig('picBed', newConfig) - await manageStore.refreshConfig() + if (autoImport) { + const autoImportPicBed = initArray(await getPicBedsConfig('settings.autoImportPicBed') || '', []) + await Promise.all(filteredConfigList.flatMap((config) => transUpToManage(config, config.type, autoImportPicBed))) + if (Object.keys(importedNewConfig).length > 0) { + const oldConfig = await getConfig('picBed') + const newConfig = { ...oldConfig, ...importedNewConfig } + saveConfig('picBed', newConfig) + await manageStore.refreshConfig() + } } + + await getAllConfigAliasArray() } function isImported (alias: string) { @@ -721,6 +724,7 @@ async function transUpToManage (config: IUploaderConfigListItem, picBedName: str webPath: config.webpath || '', customUrl: config.customUrl || '', sslEnabled: !!config.sslEnabled, + authType: config.authType || 'basic', proxy: '', transformedConfig: JSON.stringify({ webdav: { diff --git a/src/renderer/manage/utils/constants.ts b/src/renderer/manage/utils/constants.ts index 38c48d0..cb2ec3d 100644 --- a/src/renderer/manage/utils/constants.ts +++ b/src/renderer/manage/utils/constants.ts @@ -779,10 +779,20 @@ export const supportedPicBedList: IStringKeyMap = { default: true, type: 'boolean', tooltip: $T('MANAGE_CONSTANT_WEBDAV_SSL_TOOLTIP') + }, + authType: { + required: true, + description: $T('MANAGE_CONSTANT_WEBDAV_AUTH_TYPE_DESC'), + default: 'basic', + type: 'select', + selectOptions: { + basic: 'Basic', + digest: 'Digest' + } } }, explain: $T('MANAGE_CONSTANT_WEBDAV_EXPLAIN'), - options: ['alias', 'endpoint', 'username', 'password', 'bucketName', 'baseDir', 'customUrl', 'webPath', 'proxy', 'sslEnabled'], + options: ['alias', 'endpoint', 'username', 'password', 'bucketName', 'baseDir', 'customUrl', 'webPath', 'proxy', 'sslEnabled', 'authType'], refLink: 'https://piclist.cn/manage.html#webdav', referenceText: $T('MANAGE_CONSTANT_WEBDAV_REFER_TEXT') }, diff --git a/src/renderer/manage/utils/digestAuth.ts b/src/renderer/manage/utils/digestAuth.ts new file mode 100644 index 0000000..f1aac77 --- /dev/null +++ b/src/renderer/manage/utils/digestAuth.ts @@ -0,0 +1,74 @@ +import crypto from 'crypto' +import axios from 'axios' + +const AUTH_KEY_VALUE_RE = /(\w+)=["']?([^'"]{1,10000})["']?/ +let NC = 0 +const NC_PAD = '00000000' + +function md5 (text: crypto.BinaryLike) { + return crypto.createHash('md5').update(text).digest('hex') +} + +export function digestAuthHeader (method: string, uri: string, wwwAuthenticate: string, username: string, password: string) { + const parts = wwwAuthenticate.split(',') + const opts = {} as IStringKeyMap + for (let i = 0; i < parts.length; i++) { + const m = AUTH_KEY_VALUE_RE.exec(parts[i]) + if (m) { + opts[m[1]] = m[2].replace(/["']/g, '') + } + } + + if (!opts.realm || !opts.nonce) { + return '' + } + + let qop = opts.qop || '' + + const userpassArray = [username, password] + + let nc = String(++NC) + nc = NC_PAD.substring(nc.length) + nc + const cnonce = crypto.randomBytes(8).toString('hex') + + const ha1 = md5(userpassArray[0] + ':' + opts.realm + ':' + userpassArray[1]) + const ha2 = md5(method.toUpperCase() + ':' + uri) + let s = ha1 + ':' + opts.nonce + if (qop) { + qop = qop.split(',')[0] + s += ':' + nc + ':' + cnonce + ':' + qop + } + s += ':' + ha2 + const response = md5(s) + let authstring = + 'Digest username="' + + userpassArray[0] + + '", realm="' + + opts.realm + + '", nonce="' + + opts.nonce + + '", uri="' + + uri + + '", response="' + + response + + '"' + if (opts.opaque) { + authstring += ', opaque="' + opts.opaque + '"' + } + if (qop) { + authstring += ', qop=' + qop + ', nc=' + nc + ', cnonce="' + cnonce + '"' + } + return authstring +} + +export async function getAuthHeader (method: string, host: string, uri: string, username: string, password: string) { + try { + await axios.get( + `${host}${uri}` + ) + } catch (error: any) { + if (error.response.status === 401 && error.response.headers['www-authenticate']) { + return digestAuthHeader(method, uri, error.response.headers['www-authenticate'], username, password) + } + } +} diff --git a/src/universal/types/i18n.d.ts b/src/universal/types/i18n.d.ts index 877f051..3acb45d 100644 --- a/src/universal/types/i18n.d.ts +++ b/src/universal/types/i18n.d.ts @@ -579,6 +579,7 @@ interface ILocales { MANAGE_CONSTANT_WEBDAV_PROXY_TOOLTIP: string MANAGE_CONSTANT_WEBDAV_SSL_DESC: string MANAGE_CONSTANT_WEBDAV_SSL_TOOLTIP: string + MANAGE_CONSTANT_WEBDAV_AUTH_TYPE_DESC: string MANAGE_CONSTANT_WEBDAV_EXPLAIN: string MANAGE_CONSTANT_WEBDAV_REFER_TEXT: string MANAGE_CONSTANT_LOCAL_NAME: string diff --git a/yarn.lock b/yarn.lock index 7496a02..d48ac1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5006,6 +5006,14 @@ axios@^0.26.1: dependencies: follow-redirects "^1.14.8" +axios@^0.27.2: + version "0.27.2" + resolved "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" + integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== + dependencies: + follow-redirects "^1.14.9" + form-data "^4.0.0" + axios@^1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz#f02e4af823e2e46a9768cfc74691fdd0517ea267" @@ -8475,7 +8483,7 @@ follow-redirects@^1.0.0: resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz#8cfb281bbc035b3c067d6cd975b0f6ade6e855cd" integrity sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A== -follow-redirects@^1.14.8, follow-redirects@^1.15.0, follow-redirects@^1.15.1: +follow-redirects@^1.14.8, follow-redirects@^1.14.9, follow-redirects@^1.15.0, follow-redirects@^1.15.1: version "1.15.2" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== @@ -9440,6 +9448,11 @@ hosted-git-info@^7.0.0: dependencies: lru-cache "^10.0.1" +hot-patcher@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/hot-patcher/-/hot-patcher-1.0.0.tgz#7124d2dc4ca71bcb58b1551603cd13e4fc3fcecd" + integrity sha512-3H8VH0PreeNsKMZw16nTHbUp4YoHCnPlawpsPXGJUR4qENDynl79b6Xk9CIFvLcH1qungBsCuzKcWyzoPPalTw== + hot-patcher@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/hot-patcher/-/hot-patcher-2.0.0.tgz#10a21b5bb4f5757316c41fc98794c11192a0a41e" @@ -10652,6 +10665,11 @@ launch-editor@^2.2.1, launch-editor@^2.3.0: picocolors "^1.0.0" shell-quote "^1.6.1" +layerr@^0.1.2: + version "0.1.2" + resolved "https://registry.npmjs.org/layerr/-/layerr-0.1.2.tgz#16c8e7fb042d3595ab15492bdad088f31d7afd15" + integrity sha512-ob5kTd9H3S4GOG2nVXyQhOu9O8nBgP555XxWPkJI0tR0JeRilfyTp8WtPdIJHLXBmHMSdEq5+KMxiYABeScsIQ== + layerr@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/layerr/-/layerr-2.0.1.tgz#0c98e6f599de4f76b75c7a6522c54b8c6c591ff0" @@ -11314,6 +11332,13 @@ minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^5.1.0: + version "5.1.6" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + minimatch@^7.4.6: version "7.4.6" resolved "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb" @@ -11820,10 +11845,10 @@ npm-bundled@^3.0.0: dependencies: npm-normalize-package-bin "^3.0.0" -npm-check-updates@^16.13.3: - version "16.13.3" - resolved "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.13.3.tgz#136f0e008b82c34be4b758260c3582214a07527e" - integrity sha512-l3FQtm+ZtDwqtK2r27vCuNdtnoDsXzk8D2WczvrAJy2bGPZJvRmuUa/Q9Gv+AbZV0IHSNJD2oHtQqUeqQRhEsw== +npm-check-updates@^16.14.0: + version "16.14.0" + resolved "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-16.14.0.tgz#e34b5af1176be2347b012946ad9bd0133bfed13e" + integrity sha512-0R4S0qsx2FhuSiIYloHc7RQwfZpzO4jdL3rUoYwbOkx5fBc9u77GHHS0FlXYpczHR/kPYmmB/CRkFElOofVeSg== dependencies: chalk "^5.3.0" cli-table3 "^0.6.3" @@ -11853,6 +11878,7 @@ npm-check-updates@^16.13.3: semver-utils "^1.1.4" source-map-support "^0.5.21" spawn-please "^2.0.1" + strip-ansi "^7.1.0" strip-json-comments "^5.0.1" untildify "^4.0.0" update-notifier "^6.0.2" @@ -12554,10 +12580,10 @@ performance-now@^2.1.0: resolved "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== -piclist@^1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/piclist/-/piclist-1.1.0.tgz#49a015de794c4b71eaf492fec724e6c862b875c0" - integrity sha512-G5uywChIS+RcK3yGNMGoXY4RFLIHh6Q+klQHODnz3eTITsZ8lbJy+X9WeAiC1Olx6Cjj2hhZGZVTgHQI2xDNag== +piclist@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/piclist/-/piclist-1.1.1.tgz#b4750a8ce0c381fc1591f2c6434b77973d79f973" + integrity sha512-8uDvjGQJLzr+PZ6vwISR8UrvytFm/SxPkZPVMSEq8+QlKW0117B2d7TICvW8MfpXKX0WMZERM/vabHNjaa9HEQ== dependencies: "@picgo/i18n" "^1.0.0" "@picgo/store" "^2.1.0" @@ -12588,6 +12614,7 @@ piclist@^1.1.0: text-to-svg "^3.1.5" tunnel "^0.0.6" uuid "^9.0.0" + webdav "^4.11.2" picocolors@^0.2.1: version "0.2.1" @@ -14700,7 +14727,7 @@ strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^7.0.1: +strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== @@ -15581,6 +15608,11 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +url-join@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" + integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== + url-join@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/url-join/-/url-join-5.0.0.tgz#c2f1e5cbd95fa91082a93b58a1f42fecb4bdbcf1" @@ -15974,6 +16006,25 @@ web-streams-polyfill@^3.0.3: resolved "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" integrity sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q== +webdav@^4.11.2: + version "4.11.3" + resolved "https://registry.npmjs.org/webdav/-/webdav-4.11.3.tgz#c8bf9b5ed1799d432d58958433925b0fa91db08e" + integrity sha512-NIuREBXYo5xb+zB4zy502snynbhugWjepg5Oke0ByEb7J/Ofmbk+JgFYM+sZdnKG3+XQ87rictDIF+SHUJkwlg== + dependencies: + axios "^0.27.2" + base-64 "^1.0.0" + byte-length "^1.0.2" + fast-xml-parser "^4.2.4" + he "^1.2.0" + hot-patcher "^1.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" + webdav@^5.3.0: version "5.3.0" resolved "https://registry.npmjs.org/webdav/-/webdav-5.3.0.tgz#0be7690003884a250f7595ee42e583c71f01661d"