🔨 Refactor: refactored of some manage api

This commit is contained in:
萌萌哒赫萝
2023-08-07 07:58:43 -07:00
parent cb131d5250
commit ecf42fab96
8 changed files with 338 additions and 342 deletions

View File

@@ -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
}
}

View File

@@ -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<any> {
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: <any>[],
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<boolean> {
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

View File

@@ -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
}

View File

@@ -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<boolean> {
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({

View File

@@ -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',

View File

@@ -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<boolean> {
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<any> {
@@ -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
}
/**

View File

@@ -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<any> {
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,

View File

@@ -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<any> {
const { currentPage } = configMap
let res = {} as any
async getBucketFileList ({ currentPage }: IStringKeyMap): Promise<any> {
const result = {
fullList: <any>[],
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<boolean> {
const { DeleteHash } = configMap
const params = {
hash: DeleteHash,
format: 'json'
}
async deleteBucketFile ({ DeleteHash }: IStringKeyMap): Promise<boolean> {
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
}
/**