diff --git a/src/main/apis/app/remoteNotice/index.ts b/src/main/apis/app/remoteNotice/index.ts index 9040024d..cdf373ab 100644 --- a/src/main/apis/app/remoteNotice/index.ts +++ b/src/main/apis/app/remoteNotice/index.ts @@ -47,7 +47,6 @@ class RemoteNoticeHandler { const localCountStorage: IRemoteNoticeLocalCountStorage = fs.readJSONSync(REMOTE_NOTICE_LOCAL_STORAGE_PATH, 'utf8') this.remoteNoticeLocalCountStorage = localCountStorage } catch (e) { - console.log(e) this.remoteNoticeLocalCountStorage = localCountStorage } } diff --git a/src/main/manage/apis/aliyun.ts b/src/main/manage/apis/aliyun.ts index 31d1f12a..8d772e41 100644 --- a/src/main/manage/apis/aliyun.ts +++ b/src/main/manage/apis/aliyun.ts @@ -1,18 +1,37 @@ +// Axios import axios from 'axios' + +// 加密函数、获取文件 MIME 类型、错误格式化函数、新的下载器、并发异步任务池 import { hmacSha1Base64, getFileMimeType, formatError, NewDownloader, ConcurrencyPromisePool } from '../utils/common' + +// Electron 相关 import { ipcMain, IpcMainEvent } from 'electron' + +// 快速 XML 解析器 import { XMLParser } from 'fast-xml-parser' + +// 阿里云 OSS 客户端库 import OSS from 'ali-oss' + +// 路径处理库 import path from 'path' + +// 是否为图片的判断函数 import { isImage } from '~/renderer/manage/utils/common' + +// 窗口管理器 import windowManager from 'apis/app/window/windowManager' + +// 枚举类型声明 import { IWindowList } from '#/types/enum' -import UpDownTaskQueue, -{ - uploadTaskSpecialStatus, - commonTaskStatus -} from '../datastore/upDownTaskQueue' + +// 上传下载任务队列 +import UpDownTaskQueue, { uploadTaskSpecialStatus, commonTaskStatus } from '../datastore/upDownTaskQueue' + +// 日志记录器 import { ManageLogger } from '../utils/logger' + +// 取消下载任务的加载文件列表、刷新下载文件传输列表 import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' // 坑爹阿里云 返回数据类型标注和实际各种不一致 @@ -49,22 +68,20 @@ class AliyunApi { } formatFile (item: OSS.ObjectMeta, slicedPrefix: string, urlPrefix: string): any { - const result = { + const fileName = item.name.replace(slicedPrefix, '') + return { ...item, key: item.name, - rawUrl: `${urlPrefix}/${item.name}`, - fileName: item.name.replace(slicedPrefix, ''), + fileName, fileSize: item.size, formatedTime: new Date(item.lastModified).toLocaleString(), isDir: false, checked: false, match: false, - isImage: isImage(item.name.replace(slicedPrefix, '')) + isImage: isImage(fileName), + rawUrl: item.url, + url: `${urlPrefix}/${item.name}` } - const temp = result.rawUrl - result.rawUrl = result.url - result.url = temp - return result } getCanonicalizedOSSHeaders (headers: IStringKeyMap) { @@ -100,47 +117,30 @@ class AliyunApi { * 获取存储桶列表 */ async getBucketList (): Promise { - const formatItem = (item: OSS.Bucket) => { - return { + const getBuckets = async (marker?: string) => { + const res = await this.ctx.listBuckets({ + marker, + 'max-keys': 1000 + }) as IStringKeyMap + if (res.res.statusCode !== 200 || !res.buckets) return { result: [], isTruncated: false } + const formattedBuckets = res.buckets.map((item: OSS.Bucket) => ({ Name: item.name, Location: item.region, CreationDate: item.creationDate - } - } - const res = await this.ctx.listBuckets({ - 'max-keys': 1000 - }) as IStringKeyMap - const result = [] as IStringKeyMap[] - let NextMarker = '' - if (res.res.statusCode === 200) { - if (res.buckets) { - result.push(...res.buckets.map((item: OSS.Bucket) => formatItem(item))) - let isTruncated = res.isTruncated - NextMarker = res.nextMarker - while (isTruncated) { - const res = await this.ctx.listBuckets({ - marker: NextMarker, - 'max-keys': 1000 - }) as IStringKeyMap - if (res.res.statusCode === 200) { - if (res.buckets) { - result.push(...res.buckets.map((item: OSS.Bucket) => formatItem(item))) - isTruncated = res.isTruncated - NextMarker = res.nextMarker - } else { - isTruncated = false - } - } else { - isTruncated = false - } - } - return result - } else { - return [] - } - } else { - return [] + })) + return { result: formattedBuckets, isTruncated: res.isTruncated, nextMarker: res.nextMarker } } + const result: IStringKeyMap[] = [] + let NextMarker: string | undefined + let isTruncated: boolean + do { + const { result: buckets, isTruncated: truncated, nextMarker } = await getBuckets(NextMarker) + result.push(...buckets) + NextMarker = nextMarker + isTruncated = truncated + } while (isTruncated) + + return result } /** @@ -151,6 +151,7 @@ class AliyunApi { Date: new Date().toUTCString() } const authorization = this.authorization('GET', `/${param.bucketName}/?cname`, headers, '', '') + const res = await axios({ url: `https://${param.bucketName}.${param.region}.aliyuncs.com/?cname`, method: 'GET', @@ -159,25 +160,22 @@ class AliyunApi { Authorization: authorization } }) + if (res.status === 200) { const parser = new XMLParser() const result = parser.parse(res.data) - if (result.ListCnameResult && result.ListCnameResult.Cname) { - if (Array.isArray(result.ListCnameResult.Cname)) { - const cnameList = [] as string[] - result.ListCnameResult.Cname.forEach((item: IStringKeyMap) => { - item.Status === 'Enabled' && cnameList.push(item.Domain) - }) - return cnameList - } else { - return result.ListCnameResult.Cname.Status === 'Enabled' ? [result.ListCnameResult.Cname.Domain] : [] - } - } else { - return [] + + if (result.ListCnameResult?.Cname) { + const cnames = Array.isArray(result.ListCnameResult.Cname) + ? result.ListCnameResult.Cname + : [result.ListCnameResult.Cname] + + return cnames + .filter((item: IStringKeyMap) => item.Status === 'Enabled') + .map((item: IStringKeyMap) => item.Domain) } - } else { - return [] } + return [] } /** @@ -329,16 +327,10 @@ class AliyunApi { const { bucketName: bucket, bucketConfig: { Location: region }, prefix, marker, itemsPerPage } = configMap const slicedPrefix = prefix.slice(1) const urlPrefix = configMap.customUrl || `https://${bucket}.${region}.aliyuncs.com` - let res = {} as any - const result = { - fullList: [], - isTruncated: false, - nextMarker: '', - success: false - } + const client = this.getNewCtx(region, bucket) - res = await client.listV2({ - prefix: slicedPrefix === '' ? undefined : slicedPrefix, + const res = await client.listV2({ + prefix: slicedPrefix || undefined, delimiter: '/', 'max-keys': itemsPerPage.toString(), 'continuation-token': marker @@ -347,18 +339,24 @@ class AliyunApi { }) as any // prefixes can be null // objects will be [] when no file - if (res && res.res.statusCode === 200) { - res.prefixes && res.prefixes.forEach((item: string) => { - result.fullList.push(this.formatFolder(item, slicedPrefix)) - }) - res.objects && res.objects.forEach((item: OSS.ObjectMeta) => { - item.size !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) - }) - result.isTruncated = res.isTruncated - result.nextMarker = res.nextContinuationToken || '' - result.success = true + if (res?.res.statusCode !== 200) { + return { + fullList: [], + isTruncated: false, + nextMarker: '', + success: false + } + } + const fullList = [ + ...(res.prefixes?.map((item: string) => this.formatFolder(item, slicedPrefix)) || []), + ...(res.objects?.filter((item: OSS.ObjectMeta) => item.size !== 0).map((item: OSS.ObjectMeta) => this.formatFile(item, slicedPrefix, urlPrefix)) || []) + ] + return { + fullList, + isTruncated: res.isTruncated, + nextMarker: res.nextContinuationToken || '', + success: true } - return result } /** @@ -374,16 +372,15 @@ class AliyunApi { async renameBucketFile (configMap: IStringKeyMap): Promise { const { bucketName, region, oldKey, newKey } = configMap const client = this.getNewCtx(region, bucketName) - const res = await client.copy( + const copyRes = await client.copy( newKey, oldKey ) as any - if (res && res.res.statusCode === 200) { - const res2 = await client.delete(oldKey) as any - return res2 && res2.res.statusCode === 204 - } else { - return false + if (copyRes?.res.statusCode === 200) { + const deleteRes = await client.delete(oldKey) as any + return deleteRes?.res.statusCode === 204 } + return false } /** @@ -399,7 +396,7 @@ class AliyunApi { const { bucketName, region, key } = configMap const client = this.getNewCtx(region, bucketName) const res = await client.delete(key) as any - return res && res.res.statusCode === 204 + return res?.res.statusCode === 204 } /** @@ -415,62 +412,39 @@ class AliyunApi { CommonPrefixes: [] as any[], Contents: [] as any[] } - let res = await client.listV2({ - prefix: key, - delimiter: '/', - 'max-keys': '1000' - }, { - timeout: this.timeOut - }) as any - if (res && res.res.statusCode === 200) { + do { + const res = await client.listV2({ + prefix: key, + delimiter: '/', + 'max-keys': '1000', + 'continuation-token': marker + }, { + timeout: this.timeOut + }) as any + if (res?.res.statusCode !== 200) return false + res.prefixes !== null && allFileList.CommonPrefixes.push(...res.prefixes) - res.objects.length > 0 && allFileList.Contents.push(...res.objects) + res.objects?.length > 0 && allFileList.Contents.push(...res.objects) isTruncated = res.isTruncated marker = res.nextContinuationToken - while (isTruncated) { - res = await client.listV2({ - prefix: key, - delimiter: '/', - 'max-keys': '1000', - 'continuation-token': marker - }, { - timeout: this.timeOut - }) as any - if (res && res.res.statusCode === 200) { - res.prefixes !== null && allFileList.CommonPrefixes.push(...res.prefixes) - res.objects.length > 0 && allFileList.Contents.push(...res.objects) - isTruncated = res.isTruncated - marker = res.nextContinuationToken - } else { - return false - } - } - } else { - return false - } + } while (isTruncated) + if (allFileList.CommonPrefixes.length > 0) { for (const item of allFileList.CommonPrefixes) { - res = await this.deleteBucketFolder({ + const successfully = await this.deleteBucketFolder({ bucketName, region, key: item }) - if (!res) { - return false - } + if (!successfully) return false } } if (allFileList.Contents.length > 0) { const cycle = Math.ceil(allFileList.Contents.length / 1000) for (let i = 0; i < cycle; i++) { - res = await client.deleteMulti( - allFileList.Contents.slice(i * 1000, (i + 1) * 1000).map((item: any) => { - return item.name - }) - ) as any - if (!(res && res.res.statusCode === 200)) { - return false - } + const deleteRes = await client.deleteMulti( + allFileList.Contents.slice(i * 1000, (i + 1) * 1000).map((item: any) => item.name)) as any + if (deleteRes?.res.statusCode !== 200) return false } } return true diff --git a/src/main/manage/apis/api.ts b/src/main/manage/apis/api.ts index 4a7f3bc2..e1a63b4b 100644 --- a/src/main/manage/apis/api.ts +++ b/src/main/manage/apis/api.ts @@ -9,13 +9,13 @@ import UpyunApi from './upyun' import WebdavplistApi from './webdavplist' export default { - TcyunApi, AliyunApi, - QiniuApi, - UpyunApi, - SmmsApi, GithubApi, ImgurApi, + QiniuApi, S3plistApi, + SmmsApi, + TcyunApi, + UpyunApi, WebdavplistApi } diff --git a/src/main/manage/apis/github.ts b/src/main/manage/apis/github.ts index 33afb640..1b414ce2 100644 --- a/src/main/manage/apis/github.ts +++ b/src/main/manage/apis/github.ts @@ -1,16 +1,34 @@ +// HTTP 请求库 import got from 'got' + +// 日志记录器 import { ManageLogger } from '../utils/logger' + +// HTTP 代理格式化函数、是否为图片的判断函数 import { formatHttpProxy, isImage } from '~/renderer/manage/utils/common' + +// 窗口管理器 import windowManager from 'apis/app/window/windowManager' + +// 枚举类型声明 import { IWindowList } from '#/types/enum' + +// Electron 相关 import { ipcMain, IpcMainEvent } from 'electron' + +// got 上传函数、路径处理函数、新的下载器、获取请求代理、获取请求选项、并发异步任务池、错误格式化函数 import { gotUpload, trimPath, NewDownloader, getAgent, getOptions, ConcurrencyPromisePool, formatError } from '../utils/common' -import UpDownTaskQueue, -{ - commonTaskStatus -} from '../datastore/upDownTaskQueue' + +// 上传下载任务队列 +import UpDownTaskQueue, { commonTaskStatus } from '../datastore/upDownTaskQueue' + +// 文件系统库 import fs from 'fs-extra' + +// 路径处理库 import path from 'path' + +// 取消下载任务的加载文件列表、刷新下载文件传输列表 import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' class GithubApi { @@ -252,34 +270,32 @@ class GithubApi { */ async deleteBucketFolder (configMap: IStringKeyMap): Promise { const { bucketName: repo, githubBranch: branch, key } = configMap + // get sha of the branch const refRes = await got( `${this.baseUrl}/repos/${this.username}/${repo}/git/refs/heads/${branch}`, getOptions('GET', this.commonHeaders, undefined, 'json', undefined, undefined, this.proxy) ) as any - if (refRes.statusCode !== 200) { - return false - } + if (refRes.statusCode !== 200) return false const refSha = refRes.body.object.sha + // get sha of the root tree const rootRes = await got( `${this.baseUrl}/repos/${this.username}/${repo}/branches/${branch}`, getOptions('GET', undefined, undefined, 'json', undefined, undefined, this.proxy) ) as any - if (rootRes.statusCode !== 200) { - return false - } + if (rootRes.statusCode !== 200) return false const rootSha = rootRes.body.commit.commit.tree.sha // TODO: if there are more than 10000 files in the folder, it will be truncated // Rare cases, not considered for now + // get sha of the folder tree const treeRes = await got( `${this.baseUrl}/repos/${this.username}/${repo}/git/trees/${branch}:${key.replace(/(^\/+|\/+$)/g, '')}`, getOptions('GET', this.commonHeaders, { recursive: true }, 'json', undefined, undefined, this.proxy) ) as any - if (treeRes.statusCode !== 200) { - return false - } + if (treeRes.statusCode !== 200) return false const oldTree = treeRes.body.tree + // create a new tree const newTree = oldTree.filter((item: any) => item.type === 'blob') .map((item:any) => ({ path: `${key.replace(/(^\/+|\/+$)/g, '')}/${item.path}`, @@ -294,10 +310,9 @@ class GithubApi { tree: newTree }), undefined, this.proxy) ) as any - if (newTreeShaRes.statusCode !== 201) { - return false - } + if (newTreeShaRes.statusCode !== 201) return false const newTreeSha = newTreeShaRes.body.sha + // create a new commit const commitRes = await got( `${this.baseUrl}/repos/${this.username}/${repo}/git/commits`, getOptions('POST', this.commonHeaders, undefined, 'json', JSON.stringify({ @@ -306,10 +321,9 @@ class GithubApi { parents: [refSha] }), undefined, this.proxy) ) as any - if (commitRes.statusCode !== 201) { - return false - } + if (commitRes.statusCode !== 201) return false const commitSha = commitRes.body.sha + // update the branch const updateRefRes = await got( `${this.baseUrl}/repos/${this.username}/${repo}/git/refs/heads/${branch}`, getOptions('PATCH', this.commonHeaders, undefined, 'json', JSON.stringify({ diff --git a/src/main/manage/apis/imgur.ts b/src/main/manage/apis/imgur.ts index 73fa3c9d..ae9e9914 100644 --- a/src/main/manage/apis/imgur.ts +++ b/src/main/manage/apis/imgur.ts @@ -81,16 +81,12 @@ class ImgurApi { result.push(...res.body.data) initPage++ } while (res.body.data.length > 0) - const finalResult = [] as any[] - for (let i = 0; i < result.length; i++) { - const item = result[i] - finalResult.push({ - ...item, - Name: item.title, - Location: item.id, - CreationDate: item.datetime - }) - } + const finalResult = result.map((item: any) => ({ + ...item, + Name: item.title, + Location: item.id, + CreationDate: item.datetime + })) as any[] finalResult.push({ Name: '全部', Location: 'unclassified', diff --git a/src/main/manage/apis/qiniu.ts b/src/main/manage/apis/qiniu.ts index e6f51e47..4ea5a39d 100644 --- a/src/main/manage/apis/qiniu.ts +++ b/src/main/manage/apis/qiniu.ts @@ -1,17 +1,34 @@ +// Axios import axios from 'axios' + +// 加密函数、获取文件 MIME 类型、新的下载器、错误格式化函数、并发异步任务池 import { hmacSha1Base64, getFileMimeType, NewDownloader, formatError, ConcurrencyPromisePool } from '../utils/common' + +// 七牛云客户端库 import qiniu from 'qiniu/index' + +// 路径处理库 import path from 'path' + +// 是否为图片的判断函数 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' -import UpDownTaskQueue, -{ - uploadTaskSpecialStatus, - commonTaskStatus -} from '../datastore/upDownTaskQueue' + +// 上传下载任务队列 +import UpDownTaskQueue, { uploadTaskSpecialStatus, commonTaskStatus } from '../datastore/upDownTaskQueue' + +// 日志记录器 import { ManageLogger } from '../utils/logger' + +// 取消下载任务的加载文件列表、刷新下载文件传输列表 import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' class QiniuApi { @@ -49,16 +66,17 @@ class QiniuApi { } formatFile (item: any, slicedPrefix: string, urlPrefix: string) { + const fileName = item.key.replace(slicedPrefix, '') return { ...item, - fileName: item.key.replace(slicedPrefix, ''), + fileName, url: `${urlPrefix}/${item.key}`, fileSize: item.fsize, formatedTime: new Date(parseInt(item.putTime.toString().slice(0, -4), 10)).toLocaleString(), isDir: false, checked: false, match: false, - isImage: isImage(item.key.replace(slicedPrefix, '')) + isImage: isImage(fileName) } } @@ -71,22 +89,20 @@ class QiniuApi { contentType: string, xQiniuHeaders?: IStringKeyMap ) { - let signStr = `${method.toUpperCase()} ${urlPath}` - query && (signStr += `?${query}`) - signStr += `\nHost: ${host}` + let signStr = `${method.toUpperCase()} ${urlPath}${query ? `?${query}` : ''}\nHost: ${host}` + contentType && (signStr += `\nContent-Type: ${contentType}`) - let xQiniuHeaderStr = '' if (xQiniuHeaders) { - const xQiniuHeaderKeys = Object.keys(xQiniuHeaders).sort() - xQiniuHeaderKeys.forEach((key) => { - xQiniuHeaderStr += `\n${key}:${xQiniuHeaders[key]}` - }) + const xQiniuHeaderStr = Object.keys(xQiniuHeaders) + .sort() + .map((key) => `\n${key}:${xQiniuHeaders[key]}`) + .join('') signStr += xQiniuHeaderStr } + signStr += '\n\n' - if (contentType !== 'application/octet-stream' && body) { - signStr += body - } + + if (contentType !== 'application/octet-stream' && body) signStr += body return `Qiniu ${this.accessKey}:${hmacSha1Base64(this.secretKey, signStr).replace(/\+/g, '-').replace(/\//g, '_')}` } @@ -103,28 +119,21 @@ class QiniuApi { }, timeout: this.timeout }) - if (res && res.status === 200) { - if (res.data && res.data.length) { - const result = [] as any[] - for (let i = 0; i < res.data.length; i++) { - const info = await this.getBucketInfo({ bucketName: res.data[i] }) - if (!info.success) { - return [] - } - result.push({ - Name: res.data[i], - Location: info.zone, - CreationDate: new Date().toISOString(), - Private: info.private - }) - } - return result - } else { - return [] + if (res?.status === 200 && res?.data?.length) { + const result = [] as any[] + for (let i = 0; i < res.data.length; i++) { + const info = await this.getBucketInfo({ bucketName: res.data[i] }) + if (!info.success) return [] + result.push({ + Name: res.data[i], + Location: info.zone, + CreationDate: new Date().toISOString(), + Private: info.private + }) } - } else { - return [] + return result } + return [] } /** @@ -148,17 +157,15 @@ class QiniuApi { }, timeout: this.timeout }) - if (res && res.status === 200) { - return { + return res?.status === 200 + ? { success: true, private: res.data.private, zone: res.data.zone } - } else { - return { + : { success: false } - } } /** @@ -178,11 +185,7 @@ class QiniuApi { }, timeout: this.timeout }) - if (res && res.status === 200) { - return res.data && res.data.length ? res.data : [] - } else { - return [] - } + return res?.status === 200 && res?.data?.length ? res.data : [] } /** @@ -209,7 +212,7 @@ class QiniuApi { }, timeout: this.timeout }) - return res && res.status === 200 + return res?.status === 200 } /** @@ -222,8 +225,7 @@ class QiniuApi { * } */ async createBucket (configMap: IStringKeyMap): Promise { - const { BucketName, region } = configMap - const { acl } = configMap + const { BucketName, region, acl } = configMap const urlPath = `/mkbucketv3/${BucketName}/region/${region}` const authorization = this.authorization('POST', urlPath, this.host, '', '', 'application/json') const res = await axios({ @@ -236,15 +238,12 @@ class QiniuApi { }, timeout: this.timeout }) - if (res && res.status === 200) { - const changeAclRes = await this.setBucketAclPolicy({ + return res?.status === 200 + ? await this.setBucketAclPolicy({ bucketName: BucketName, isPrivate: !acl }) - return changeAclRes - } else { - return false - } + : false } async getBucketListRecursively (configMap: IStringKeyMap): Promise { @@ -407,19 +406,19 @@ class QiniuApi { } }) }) - if (res && res.respInfo.statusCode === 200) { - if (res.respBody && res.respBody.commonPrefixes) { + if (res?.respInfo?.statusCode === 200) { + if (res.respBody?.commonPrefixes) { res.respBody.commonPrefixes.forEach((item: string) => { result.fullList.push(this.formatFolder(item, slicedPrefix)) }) } - if (res.respBody && res.respBody.items) { + if (res.respBody?.items) { res.respBody.items.forEach((item: any) => { item.fsize !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) }) } - result.isTruncated = !!(res.respBody && res.respBody.marker) - result.nextMarker = res.respBody && res.respBody.marker ? res.respBody.marker : '' + result.isTruncated = !!(res.respBody?.marker) + result.nextMarker = res.respBody?.marker ? res.respBody.marker : '' result.success = true } return result @@ -450,11 +449,7 @@ class QiniuApi { } }) }) as any - if (res && res.respInfo.statusCode === 200) { - return true - } else { - return false - } + return res?.respInfo?.statusCode === 200 } /** @@ -487,12 +482,12 @@ class QiniuApi { } }) }) as any - if (res && res.respInfo.statusCode === 200) { - if (res.respBody && res.respBody.items) { + if (res?.respInfo?.statusCode === 200) { + if (res.respBody?.items) { allFileList.Contents = allFileList.Contents.concat(res.respBody.items) } - isTruncated = !!(res.respBody && res.respBody.marker) - marker = res.respBody && res.respBody.marker ? res.respBody.marker : '' + isTruncated = !!(res.respBody?.marker) + marker = res.respBody?.marker ? res.respBody.marker : '' } else { return false } @@ -514,9 +509,7 @@ class QiniuApi { } }) }) as any - if (!(res && res.respInfo.statusCode === 200)) { - return false - } + if (res?.respInfo?.statusCode !== 200) return false } return true } @@ -549,7 +542,7 @@ class QiniuApi { } }) }) as any - return res && res.respInfo.statusCode === 200 + return res?.respInfo?.statusCode === 200 } /** @@ -671,11 +664,7 @@ class QiniuApi { } }) }) as any - if (res && res.respInfo.statusCode === 200) { - return true - } else { - return false - } + return res?.respInfo?.statusCode === 200 } /** diff --git a/src/main/manage/apis/s3plist.ts b/src/main/manage/apis/s3plist.ts index 3b3c33e3..f1f2ed17 100644 --- a/src/main/manage/apis/s3plist.ts +++ b/src/main/manage/apis/s3plist.ts @@ -1,3 +1,4 @@ +// AWS S3 相关 import { S3Client, ListBucketsCommand, @@ -13,24 +14,48 @@ import { DeleteObjectsCommand, PutObjectCommand } from '@aws-sdk/client-s3' + +// AWS S3 上传和进度 import { Upload, Progress } from '@aws-sdk/lib-storage' + +// AWS S3 请求签名 import { getSignedUrl } from '@aws-sdk/s3-request-presigner' + +// HTTP 和 HTTPS 模块 import https from 'https' import http from 'http' + +// 日志记录器 import { ManageLogger } from '../utils/logger' + +// 端点地址格式化函数、错误格式化函数、获取请求代理、获取文件 MIME 类型、新的下载器、并发异步任务池 import { formatEndpoint, formatError, getAgent, getFileMimeType, NewDownloader, ConcurrencyPromisePool } from '../utils/common' + +// 是否为图片的判断函数、HTTP 代理格式化函数 import { isImage, formatHttpProxy } from '@/manage/utils/common' + +// HTTP 和 HTTPS 代理库 import { HttpsProxyAgent, HttpProxyAgent } from 'hpagent' + +// 窗口管理器 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' interface S3plistApiOptions { @@ -79,18 +104,14 @@ class S3plistApi { } setAgent (proxy: string | undefined, sslEnabled: boolean) : HttpProxyAgent | HttpsProxyAgent | undefined { - if (sslEnabled) { - const agent = getAgent(proxy, true).https - return agent ?? new https.Agent({ - keepAlive: true, - rejectUnauthorized: false - }) - } else { - const agent = getAgent(proxy, false).http - return agent ?? new http.Agent({ - keepAlive: true - }) - } + const protocol = sslEnabled ? 'https' : 'http' + const agent = getAgent(proxy, sslEnabled)[protocol] + const commonOptions = { keepAlive: true } + const extraOptions = sslEnabled ? { rejectUnauthorized: false } : {} + return agent ?? new (sslEnabled ? https.Agent : http.Agent)({ + ...commonOptions, + ...extraOptions + }) } logParam = (error:any, method: string) => @@ -111,17 +132,18 @@ class S3plistApi { } formatFile (item: _Object, slicedPrefix: string, urlPrefix: string): any { + const fileName = item.Key?.replace(slicedPrefix, '') return { ...item, key: item.Key, url: `${urlPrefix}/${item.Key}`, - fileName: item.Key?.replace(slicedPrefix, ''), + fileName, fileSize: item.Size, formatedTime: new Date(item.LastModified!).toLocaleString(), isDir: false, checked: false, match: false, - isImage: isImage(item.Key?.replace(slicedPrefix, '') || '') + isImage: isImage(fileName || '') } } @@ -130,50 +152,43 @@ class S3plistApi { */ async getBucketList (): Promise { const options = Object.assign({}, this.baseOptions) as S3ClientConfig - const result = [] as IStringKeyMap[] - const endpoint = options.endpoint as string || '' as string - options.region = endpoint.indexOf('cloudflarestorage') !== -1 ? 'auto' : 'us-east-1' + const result: IStringKeyMap[] = [] + const endpoint = options.endpoint as string || '' + options.region = endpoint.includes('cloudflarestorage') ? 'auto' : 'us-east-1' try { const client = new S3Client(options) - const command = new ListBucketsCommand({}) - const data = await client.send(command) - if (data.$metadata.httpStatusCode === 200) { - if (data.Buckets) { - if (endpoint.indexOf('cloudflarestorage') !== -1) { - data.Buckets.forEach((bucket) => { - result.push({ - Name: bucket.Name, - CreationDate: bucket.CreationDate, - Location: 'auto' - }) + const data = await client.send(new ListBucketsCommand({})) + + if (data.$metadata.httpStatusCode !== 200) { + this.logParam(data, 'getBucketList') + return result + } + + if (data.Buckets) { + if (endpoint.includes('cloudflarestorage')) { + result.push(...data.Buckets.map(bucket => ({ + Name: bucket.Name, + CreationDate: bucket.CreationDate, + Location: 'auto' + }))) + } else { + for (const bucket of data.Buckets) { + const bucketName = bucket.Name + const bucketConfig = await client.send(new GetBucketLocationCommand({ + Bucket: bucketName + })) + result.push({ + Name: bucketName, + CreationDate: bucket.CreationDate, + Location: bucketConfig.$metadata.httpStatusCode === 200 + ? bucketConfig.LocationConstraint?.toLowerCase() || 'us-east-1' + : 'us-east-1' }) - } else { - for (let i = 0; i < data.Buckets.length; i++) { - const bucket = data.Buckets[i] - const bucketName = bucket.Name - const command = new GetBucketLocationCommand({ - Bucket: bucketName - }) - const bucketConfig = await client.send(command) - if (bucketConfig.$metadata.httpStatusCode === 200) { - result.push({ - Name: bucketName, - CreationDate: bucket.CreationDate, - Location: bucketConfig.LocationConstraint?.toLowerCase() || 'us-east-1' - }) - } else { - this.logParam(bucketConfig, 'getBucketList') - result.push({ - Name: bucketName, - CreationDate: bucket.CreationDate, - Location: 'us-east-1' - }) - } + if (bucketConfig.$metadata.httpStatusCode !== 200) { + this.logParam(bucketConfig, 'getBucketList') } } } - } else { - this.logParam(data, 'getBucketList') } } catch (error) { this.logParam(error, 'getBucketList') @@ -312,8 +327,7 @@ class S3plistApi { success: false } try { - const options = Object.assign({}, this.baseOptions) as S3ClientConfig - options.region = region || 'us-east-1' + const options = Object.assign({}, { ...this.baseOptions, region: region || 'us-east-1' }) as S3ClientConfig const client = new S3Client(options) const command = new ListObjectsV2Command({ Bucket: bucket, @@ -324,12 +338,10 @@ class S3plistApi { }) const data = await client.send(command) if (data.$metadata.httpStatusCode === 200) { - data.CommonPrefixes && data.CommonPrefixes.forEach((item: CommonPrefix) => { - result.fullList.push(this.formatFolder(item, slicedPrefix)) - }) - data.Contents && data.Contents.forEach((item: _Object) => { - result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) - }) + result.fullList = [ + ...(data.CommonPrefixes?.map(item => this.formatFolder(item, slicedPrefix)) || []), + ...(data.Contents?.map(item => this.formatFile(item, slicedPrefix, urlPrefix)) || []) + ] result.isTruncated = data.IsTruncated || false result.nextMarker = data.NextContinuationToken || '' result.success = true @@ -354,8 +366,7 @@ class S3plistApi { const { bucketName, region, oldKey, newKey } = configMap let result = false try { - const options = Object.assign({}, this.baseOptions) as S3ClientConfig - options.region = region || 'us-east-1' + const options = Object.assign({}, { ...this.baseOptions, region: region || 'us-east-1' }) as S3ClientConfig const client = new S3Client(options) const command = new CopyObjectCommand({ Bucket: bucketName, diff --git a/src/main/manage/apis/smms.ts b/src/main/manage/apis/smms.ts index b1763872..3abc5bf6 100644 --- a/src/main/manage/apis/smms.ts +++ b/src/main/manage/apis/smms.ts @@ -1,13 +1,34 @@ +// 是否为图片的判断函数 import { isImage } from '@/manage/utils/common' + +// Axios 和 Axios 实例类型声明 import axios, { AxiosInstance } from 'axios' + +// 窗口管理器 import windowManager from 'apis/app/window/windowManager' + +// 枚举类型声明 import { IWindowList } from '#/types/enum' + +// Electron 相关 import { ipcMain, IpcMainEvent } from 'electron' + +// 表单数据库 import FormData from 'form-data' + +// 文件系统库 import fs from 'fs-extra' + +// 获取文件 MIME 类型、got 上传函数、新的下载器、并发异步任务池、错误格式化函数 import { getFileMimeType, gotUpload, NewDownloader, ConcurrencyPromisePool, formatError } from '../utils/common' + +// 路径处理库 import path from 'path' + +// 上传下载任务队列 import UpDownTaskQueue, { commonTaskStatus } from '../datastore/upDownTaskQueue' + +// 日志记录器 import { ManageLogger } from '../utils/logger' class SmmsApi { @@ -99,7 +120,7 @@ class SmmsApi { return } marker++ - } while (!cancelTask[0] && res && res.status === 200 && res.data && res.data.success && res.data.CurrentPage < res.data.TotalPages) + } while (!cancelTask[0] && res?.status === 200 && res?.data?.success && res.data.CurrentPage < res.data.TotalPages) result.success = !cancelTask[0] result.finished = true window.webContents.send('refreshFileTransferList', result) @@ -121,16 +142,14 @@ class SmmsApi { * customUrl: string * } */ - async getBucketFileList (configMap: IStringKeyMap): Promise { - const { currentPage } = configMap - let res = {} as any + async getBucketFileList ({ currentPage }: IStringKeyMap): Promise { const result = { fullList: [], isTruncated: false, nextMarker: '', success: false } - res = await this.axiosInstance( + const res = await this.axiosInstance( '/upload_history', { method: 'GET', @@ -142,21 +161,17 @@ class SmmsApi { } } ) - if (res && res.status === 200 && res.data && res.data.success) { - if (res.data.Count === 0) { - result.success = true - return result - } - res.data.data.forEach((item: any) => { - result.fullList.push(this.formatFile(item)) - }) - result.isTruncated = res.data.CurrentPage < res.data.TotalPages - result.nextMarker = res.data.CurrentPage + 1 - result.success = true - return result - } else { - return result - } + if (res?.status !== 200 || !res?.data?.success) return result + + if (res.data.Count === 0) return { ...result, success: true } + + res.data.data.forEach((item: any) => { + result.fullList.push(this.formatFile(item)) + }) + result.isTruncated = res.data.CurrentPage < res.data.TotalPages + result.nextMarker = res.data.CurrentPage + 1 + result.success = true + return result } /** @@ -169,20 +184,18 @@ class SmmsApi { * DeleteHash: string * } */ - async deleteBucketFile (configMap: IStringKeyMap): Promise { - const { DeleteHash } = configMap - const params = { - hash: DeleteHash, - format: 'json' - } + async deleteBucketFile ({ DeleteHash }: IStringKeyMap): Promise { const res = await this.axiosInstance( `/delete/${DeleteHash}`, { method: 'GET', - params + params: { + hash: DeleteHash, + format: 'json' + } } ) - return res && res.status === 200 && res.data && res.data.success + return res?.status === 200 && res?.data?.success } /**