From 76e588b2ef99e0beac54de4f9fe6153f8fca9348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=8C=E8=90=8C=E5=93=92=E8=B5=AB=E8=90=9D?= Date: Mon, 7 Aug 2023 23:36:05 -0700 Subject: [PATCH] :hammer: Refactor: rewrite some apis --- src/main/manage/apis/tcyun.ts | 246 ++++++++++++---------------- src/main/manage/apis/upyun.ts | 62 ++++--- src/main/manage/apis/webdavplist.ts | 45 +++-- 3 files changed, 178 insertions(+), 175 deletions(-) diff --git a/src/main/manage/apis/tcyun.ts b/src/main/manage/apis/tcyun.ts index 2455db5..3699a8f 100644 --- a/src/main/manage/apis/tcyun.ts +++ b/src/main/manage/apis/tcyun.ts @@ -1,19 +1,37 @@ +// 腾讯云 COS SDK import COS from 'cos-nodejs-sdk-v5' + +// 文件系统库 import fs from 'fs-extra' + +// 路径处理库 import path from 'path' + +// 是否为图片的判断函数 import { isImage } from '~/renderer/manage/utils/common' + +// URL 编码处理函数 import { handleUrlEncode } from '~/universal/utils/common' + +// 窗口管理器 import windowManager from 'apis/app/window/windowManager' + +// 枚举类型声明 import { IWindowList } from '#/types/enum' + +// Electron 相关 import { ipcMain, IpcMainEvent } from 'electron' + +// 错误格式化函数、获取文件 MIME 类型 import { formatError, getFileMimeType } from '../utils/common' -import UpDownTaskQueue, -{ - uploadTaskSpecialStatus, - commonTaskStatus, - downloadTaskSpecialStatus -} from '../datastore/upDownTaskQueue' + +// 上传下载任务队列 +import UpDownTaskQueue, { uploadTaskSpecialStatus, commonTaskStatus, downloadTaskSpecialStatus } from '../datastore/upDownTaskQueue' + +// 日志记录器 import { ManageLogger } from '../utils/logger' + +// 取消下载任务的加载文件列表、刷新下载文件传输列表 import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' class TcyunApi { @@ -62,7 +80,7 @@ class TcyunApi { */ async getBucketList (): Promise { const res = await this.ctx.getService({}) - return res && res.Buckets ? res.Buckets : [] + return res?.Buckets || [] } /** @@ -74,21 +92,8 @@ class TcyunApi { Bucket: bucketName, Region: region }) - const result = [] as string[] - if (res && res.statusCode === 200) { - if (res.DomainRule && res.DomainRule.length > 0) { - res.DomainRule.forEach((item: any) => { - if (item.Status === 'ENABLED') { - result.push(item.Name) - } - }) - return result - } else { - return [] - } - } else { - return [] - } + if (res?.statusCode !== 200 || !res?.DomainRule?.length) return [] + return res.DomainRule.filter((item: any) => item.Status === 'ENABLED').map(item => item.Name) } /** @@ -113,31 +118,29 @@ class TcyunApi { Bucket: configMap.BucketName, Region: configMap.region }) - return res && res.statusCode === 200 + return res?.statusCode === 200 } async getBucketListRecursively (configMap: IStringKeyMap): Promise { const window = windowManager.get(IWindowList.SETTING_WINDOW)! - const bucket = configMap.bucketName - const region = configMap.bucketConfig.Location - const prefix = configMap.prefix as string + const { bucketName: bucket, bucketConfig: { Location: region }, prefix, customUrl, cancelToken } = configMap const slicedPrefix = prefix.slice(1, prefix.length) - const urlPrefix = configMap.customUrl || `https://${bucket}.cos.${region}.myqcloud.com` - let marker - const cancelToken = configMap.cancelToken as string + const urlPrefix = customUrl || `https://${bucket}.cos.${region}.myqcloud.com` const cancelTask = [false] + let marker + ipcMain.on(cancelDownloadLoadingFileList, (_evt: IpcMainEvent, token: string) => { if (token === cancelToken) { cancelTask[0] = true ipcMain.removeAllListeners(cancelDownloadLoadingFileList) } }) - let res = {} as COS.GetBucketResult const result = { fullList: [], success: false, finished: false } + let res = {} as COS.GetBucketResult do { res = await this.ctx.getBucket({ Bucket: bucket, @@ -145,9 +148,9 @@ class TcyunApi { Prefix: slicedPrefix === '' ? undefined : slicedPrefix, Marker: marker }) - if (res && res.statusCode === 200) { - res.Contents.forEach((item: COS.CosObject) => - parseInt(item.Size) !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))) + if (res?.statusCode === 200) { + result.fullList.push(...res.Contents.filter(item => parseInt(item.Size) !== 0) + .map(item => this.formatFile(item, slicedPrefix, urlPrefix))) window.webContents.send(refreshDownloadFileTransferList, result) } else { result.finished = true @@ -165,14 +168,12 @@ class TcyunApi { async getBucketListBackstage (configMap: IStringKeyMap): Promise < any > { const window = windowManager.get(IWindowList.SETTING_WINDOW)! - const bucket = configMap.bucketName - const region = configMap.bucketConfig.Location - const prefix = configMap.prefix as string + const { bucketName: bucket, bucketConfig: { Location: region }, prefix, customUrl, cancelToken } = configMap const slicedPrefix = prefix.slice(1, prefix.length) - const urlPrefix = configMap.customUrl || `https://${bucket}.cos.${region}.myqcloud.com` - let marker - const cancelToken = configMap.cancelToken as string + const urlPrefix = customUrl || `https://${bucket}.cos.${region}.myqcloud.com` const cancelTask = [false] + let marker + ipcMain.on('cancelLoadingFileList', (_evt: IpcMainEvent, token: string) => { if (token === cancelToken) { cancelTask[0] = true @@ -193,11 +194,12 @@ class TcyunApi { Delimiter: '/', Marker: marker }) - if (res && res.statusCode === 200) { - res.CommonPrefixes.forEach((item: { Prefix: string}) => - result.fullList.push(this.formatFolder(item, slicedPrefix))) - res.Contents.forEach((item: COS.CosObject) => - parseInt(item.Size) !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))) + if (res?.statusCode === 200) { + result.fullList.push( + ...res.CommonPrefixes.map(item => this.formatFolder(item, slicedPrefix)), + ...res.Contents.filter(item => parseInt(item.Size) !== 0) + .map(item => this.formatFile(item, slicedPrefix, urlPrefix)) + ) window.webContents.send('refreshFileTransferList', result) } else { result.finished = true @@ -229,36 +231,34 @@ class TcyunApi { * } */ async getBucketFileList (configMap: IStringKeyMap): Promise { - const bucket = configMap.bucketName - const region = configMap.bucketConfig.Location - const prefix = configMap.prefix as string + const { bucketName: bucket, bucketConfig: { Location: region }, prefix, customUrl, marker, itemsPerPage } = configMap const slicedPrefix = prefix.slice(1) - const urlPrefix = configMap.customUrl || `https://${bucket}.cos.${region}.myqcloud.com` - const marker = configMap.marker as string - const itemsPerPage = configMap.itemsPerPage as number - let res = {} as COS.GetBucketResult - const result = { - fullList: [], - isTruncated: false, - nextMarker: '', - success: false - } - res = await this.ctx.getBucket({ + const urlPrefix = customUrl || `https://${bucket}.cos.${region}.myqcloud.com` + const res = await this.ctx.getBucket({ Bucket: bucket, Region: region, Prefix: slicedPrefix === '' ? undefined : slicedPrefix, Delimiter: '/', Marker: marker, MaxKeys: itemsPerPage - }) - if (res && res.statusCode === 200) { - res.CommonPrefixes.forEach((item: { Prefix: string}) => - result.fullList.push(this.formatFolder(item, slicedPrefix))) - res.Contents.forEach((item: COS.CosObject) => - parseInt(item.Size) !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))) - result.isTruncated = res.IsTruncated === 'true' - result.nextMarker = res.NextMarker || '' - result.success = true + }) as COS.GetBucketResult + if (res?.statusCode !== 200) { + return { + fullList: [], + isTruncated: false, + nextMarker: '', + success: false + } + } + const result = { + fullList: [ + ...res.CommonPrefixes.map(item => this.formatFolder(item, slicedPrefix)), + ...res.Contents.filter(item => parseInt(item.Size) !== 0) + .map(item => this.formatFile(item, slicedPrefix, urlPrefix)) + ], + isTruncated: res.IsTruncated === 'true', + nextMarker: res.NextMarker || '', + success: true } return result } @@ -275,22 +275,22 @@ class TcyunApi { */ async renameBucketFile (configMap: IStringKeyMap): Promise { const { bucketName, region, oldKey, newKey } = configMap - const res = await this.ctx.putObjectCopy({ + const copyRes = await this.ctx.putObjectCopy({ Bucket: bucketName, Region: region, Key: newKey, CopySource: handleUrlEncode(`${bucketName}.cos.${region}.myqcloud.com/${oldKey}`) }) - if (res && res.statusCode === 200) { - const res2 = await this.ctx.deleteObject({ - Bucket: bucketName, - Region: region, - Key: oldKey - }) - return res2 && res2.statusCode === 204 - } else { - return false - } + + if (copyRes?.statusCode !== 200) return false + + const deleteRes = await this.ctx.deleteObject({ + Bucket: bucketName, + Region: region, + Key: oldKey + }) + + return deleteRes?.statusCode === 204 } /** @@ -309,7 +309,7 @@ class TcyunApi { Region: region, Key: key }) - return res && res.statusCode === 204 + return res?.statusCode === 204 } /** @@ -319,72 +319,38 @@ class TcyunApi { async deleteBucketFolder (configMap: IStringKeyMap): Promise { const { bucketName, region, key } = configMap let marker - let isTruncated + let res: any const allFileList = { CommonPrefixes: [] as any[], Contents: [] as any[] } - let res = await this.ctx.getBucket({ - Bucket: bucketName, - Region: region, - Prefix: key, - Delimiter: '/', - MaxKeys: 1000 - }) - if (res && res.statusCode === 200) { - res.CommonPrefixes.length > 0 && allFileList.CommonPrefixes.push(...res.CommonPrefixes) - res.Contents.length > 0 && allFileList.Contents.push(...res.Contents) - isTruncated = res.IsTruncated + do { + res = await this.ctx.getBucket({ + Bucket: bucketName, + Region: region, + Prefix: key, + Delimiter: '/', + MaxKeys: 1000, + Marker: marker + }) + + if (res?.statusCode !== 200) return false + + allFileList.CommonPrefixes.push(...res.CommonPrefixes) + allFileList.Contents.push(...res.Contents) marker = res.NextMarker - while (isTruncated === 'true') { - res = await this.ctx.getBucket({ - Bucket: bucketName, - Region: region, - Prefix: key, - Delimiter: '/', - Marker: marker, - MaxKeys: 1000 - }) as any - if (res && res.statusCode === 200) { - res.CommonPrefixes.length > 0 && allFileList.CommonPrefixes.push(...res.CommonPrefixes) - res.Contents.length > 0 && allFileList.Contents.push(...res.Contents) - isTruncated = res.IsTruncated - marker = res.NextMarker - } else { - return false - } - } - } else { - return false + } while (res.IsTruncated === 'true') + for (const item of allFileList.CommonPrefixes) { + if (!(await this.deleteBucketFolder({ bucketName, region, key: item.Prefix }))) return false } - if (allFileList.CommonPrefixes.length > 0) { - for (const item of allFileList.CommonPrefixes) { - res = await this.deleteBucketFolder({ - bucketName, - region, - key: item.Prefix - }) as any - if (!res) { - return false - } - } - } - if (allFileList.Contents.length > 0) { - const cycle = Math.ceil(allFileList.Contents.length / 1000) - for (let i = 0; i < cycle; i++) { - res = await this.ctx.deleteMultipleObject({ - Bucket: bucketName, - Region: region, - Objects: allFileList.Contents.slice(i * 1000, (i + 1) * 1000).map((item: any) => { - return { - Key: item.Key - } - }) - }) as any - if (!(res && res.statusCode === 200)) { - return false - } - } + const cycles = Math.ceil(allFileList.Contents.length / 1000) + for (let i = 0; i < cycles; i++) { + const res = await this.ctx.deleteMultipleObject({ + Bucket: bucketName, + Region: region, + Objects: allFileList.Contents.slice(i * 1000, (i + 1) * 1000).map((item: any) => ({ Key: item.Key })) + }) + if (res?.statusCode !== 200) return false } return true } @@ -500,7 +466,7 @@ class TcyunApi { Key: key, Body: '' }) - return res && res.statusCode === 200 + return res?.statusCode === 200 } /** diff --git a/src/main/manage/apis/upyun.ts b/src/main/manage/apis/upyun.ts index 9cdfd43..1d1ee37 100644 --- a/src/main/manage/apis/upyun.ts +++ b/src/main/manage/apis/upyun.ts @@ -1,19 +1,41 @@ +// 忽略 TypeScript 错误 // @ts-ignore import Upyun from 'upyun' + +// 加密函数、获取文件 MIME 类型、新的下载器、got 上传函数、并发异步任务池、错误格式化函数 import { md5, hmacSha1Base64, getFileMimeType, NewDownloader, gotUpload, ConcurrencyPromisePool, formatError } from '../utils/common' + +// 是否为图片的判断函数 import { isImage } from '~/renderer/manage/utils/common' + +// 窗口管理器 import windowManager from 'apis/app/window/windowManager' + +// 枚举类型声明 import { IWindowList } from '#/types/enum' + +// Electron 相关 import { ipcMain, IpcMainEvent } from 'electron' + +// Axios import axios from 'axios' + +// 表单数据库 import FormData from 'form-data' + +// 文件系统库 import fs from 'fs-extra' + +// 路径处理库 import path from 'path' -import UpDownTaskQueue, -{ - commonTaskStatus -} from '../datastore/upDownTaskQueue' + +// 上传下载任务队列 +import UpDownTaskQueue, { commonTaskStatus } from '../datastore/upDownTaskQueue' + +// 日志记录器 import { ManageLogger } from '../utils/logger' + +// 取消下载任务的加载文件列表、刷新下载文件传输列表 import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' class UpyunApi { @@ -35,9 +57,10 @@ class UpyunApi { } formatFolder (item: any, slicedPrefix: string) { + const key = `${slicedPrefix}${item.name}/` return { ...item, - key: `${slicedPrefix}${item.name}/`, + key, fileSize: 0, formatedTime: '', fileName: item.name, @@ -45,11 +68,12 @@ class UpyunApi { checked: false, isImage: false, match: false, - Key: `${slicedPrefix}${item.name}/` + Key: key } } formatFile (item: any, slicedPrefix: string, urlPrefix: string) { + const key = `${slicedPrefix}${item.name}` return { ...item, fileName: item.name, @@ -59,8 +83,8 @@ class UpyunApi { checked: false, match: false, isImage: isImage(item.name), - url: `${urlPrefix}/${slicedPrefix}${item.name}`, - key: `${slicedPrefix}${item.name}` + url: `${urlPrefix}/${key}`, + key } } @@ -71,18 +95,10 @@ class UpyunApi { operator: string, password: string ) { - const passwordMd5 = md5(password, 'hex') - const date = new Date().toUTCString() - const upperMethod = method.toUpperCase() - let stringToSign = '' - const codedUri = encodeURI(uri) - if (contentMd5 === '') { - stringToSign = `${upperMethod}&${codedUri}&${date}` - } else { - stringToSign = `${upperMethod}&${codedUri}&${date}&${contentMd5}` - } - const signature = hmacSha1Base64(passwordMd5, stringToSign) - return `UPYUN ${operator}:${signature}` + return `UPYUN ${operator}:${hmacSha1Base64( + md5(password, 'hex'), + `${method.toUpperCase()}&${encodeURI(uri)}&${new Date().toUTCString()}${contentMd5 ? `&${contentMd5}` : ''}` + )}` } /** @@ -120,7 +136,7 @@ class UpyunApi { iter: marker }) if (res) { - res.files && res.files.forEach((item: any) => { + res.files?.forEach((item: any) => { item.type === 'F' && folderQueue.push(`${slicedPrefix}${item.name}/`) item.type === 'N' && result.fullList.push(this.formatFile(item, folder, urlPrefix)) }) @@ -169,7 +185,7 @@ class UpyunApi { iter: marker }) if (res) { - res.files && res.files.forEach((item: any) => { + res.files?.forEach((item: any) => { item.type === 'N' && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) item.type === 'F' && result.fullList.push(this.formatFolder(item, slicedPrefix)) }) @@ -219,7 +235,7 @@ class UpyunApi { iter: marker || '' }) if (res) { - res.files && res.files.forEach((item: any) => { + res.files?.forEach((item: any) => { item.type === 'N' && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) item.type === 'F' && result.fullList.push(this.formatFolder(item, slicedPrefix)) }) diff --git a/src/main/manage/apis/webdavplist.ts b/src/main/manage/apis/webdavplist.ts index 6a78657..56aa356 100644 --- a/src/main/manage/apis/webdavplist.ts +++ b/src/main/manage/apis/webdavplist.ts @@ -1,19 +1,38 @@ +// 日志记录器 import ManageLogger from '../utils/logger' + +// WebDAV 客户端库 import { createClient, WebDAVClient, FileStat, ProgressEvent } from 'webdav' + +// 错误格式化函数、端点地址格式化函数、获取内部代理、新的下载器、并发异步任务池 import { formatError, formatEndpoint, getInnerAgent, NewDownloader, ConcurrencyPromisePool } from '../utils/common' + +// HTTP 代理格式化函数、是否为图片的判断函数 import { formatHttpProxy, isImage } from '@/manage/utils/common' + +// HTTP 和 HTTPS 模块 import http from 'http' import https from 'https' + +// 窗口管理器 import windowManager from 'apis/app/window/windowManager' + +// 枚举类型声明 import { IWindowList } from '#/types/enum' + +// Electron 相关 import { ipcMain, IpcMainEvent } from 'electron' -import UpDownTaskQueue, -{ - uploadTaskSpecialStatus, - commonTaskStatus -} from '../datastore/upDownTaskQueue' + +// 上传下载任务队列 +import UpDownTaskQueue, { uploadTaskSpecialStatus, commonTaskStatus } from '../datastore/upDownTaskQueue' + +// 文件系统库 import fs from 'fs-extra' + +// 路径处理库 import path from 'path' + +// 取消下载任务的加载文件列表、刷新下载文件传输列表 import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' class WebdavplistApi { @@ -53,12 +72,13 @@ class WebdavplistApi { this.logger.error(formatError(error, { class: 'WebdavplistApi', method })) formatFolder (item: FileStat, urlPrefix: string, isWebPath = false) { + const key = item.filename.replace(/^\/+/, '') return { ...item, - key: item.filename.replace(/^\/+/, ''), + key, fileName: item.basename, fileSize: 0, - Key: item.filename.replace(/^\/+/, ''), + Key: key, formatedTime: '', isDir: true, checked: false, @@ -69,12 +89,13 @@ class WebdavplistApi { } formatFile (item: FileStat, urlPrefix: string, isWebPath = false) { + const key = item.filename.replace(/^\/+/, '') return { ...item, - key: item.filename.replace(/^\/+/, ''), + key, fileName: item.basename, fileSize: item.size, - Key: item.filename.replace(/^\/+/, ''), + Key: key, formatedTime: new Date(item.lastmod).toLocaleString(), isDir: false, checked: false, @@ -109,7 +130,7 @@ class WebdavplistApi { details: true }) if (this.isRequestSuccess(res.status)) { - if (res.data && res.data.length) { + if (res.data?.length) { res.data.forEach((item: FileStat) => { if (item.type !== 'directory') { result.fullList.push(this.formatFile(item, urlPrefix)) @@ -142,7 +163,7 @@ class WebdavplistApi { urlPrefix = urlPrefix.replace(/\/+$/, '') let webPath = configMap.webPath || '' if (webPath && customUrl && webPath !== '/') { - webPath = webPath.replace(/^\/+/, '').replace(/\/+$/, '') + webPath = webPath.replace(/^\/+|\/+$/, '') } const cancelTask = [false] ipcMain.on('cancelLoadingFileList', (_evt: IpcMainEvent, token: string) => { @@ -163,7 +184,7 @@ class WebdavplistApi { details: true }) if (this.isRequestSuccess(res.status)) { - if (res.data && res.data.length) { + if (res.data?.length) { res.data.forEach((item: FileStat) => { const relativePath = path.relative(baseDir, item.filename) const relative = webPath && urlPrefix + `/${path.join(webPath, relativePath)}`.replace(/\\/g, '/').replace(/\/+/g, '/')