From 3040f4bfbb327e28b5ab579383fe0b145a4a7124 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, 21 Aug 2023 08:53:01 -0700 Subject: [PATCH] :sparkles: Feature: add support for dogeCloud manage ISSUES CLOSED: #73 --- public/i18n/en.yml | 2 + public/i18n/zh-CN.yml | 2 + public/i18n/zh-TW.yml | 2 + src/main/manage/apis/s3plist.ts | 84 +++++++++++++++++++++----- src/main/manage/manageApi.ts | 2 +- src/renderer/manage/pages/logIn.vue | 1 + src/renderer/manage/utils/constants.ts | 9 ++- src/universal/types/i18n.d.ts | 2 + 8 files changed, 87 insertions(+), 17 deletions(-) diff --git a/public/i18n/en.yml b/public/i18n/en.yml index 232317b..a50946b 100644 --- a/public/i18n/en.yml +++ b/public/i18n/en.yml @@ -576,6 +576,8 @@ MANAGE_CONSTANT_S3_BUCKET_DESC: Bucket name - Optional MANAGE_CONSTANT_S3_BUCKET_PLACEHOLDER: English comma-separated list, e.g. bucket1,bucket2 MANAGE_CONSTANT_S3_BASE_DIR_DESC: Base directory - Optional MANAGE_CONSTANT_S3_BASE_DIR_PLACEHOLDER: English comma-separated list, e.g. /dir1,/dir2 +MANAGE_CONSTANT_S3_DOGE_CLOUD_SUPPORT_DESC: Enable Doge Cloud API +MANAGE_CONSTANT_S3_DOGE_CLOUD_SUPPORT_TOOLTIP: Support Doge Cloud API MANAGE_CONSTANT_S3_PAGING_DESC: Enable pagination MANAGE_CONSTANT_S3_ITEMS_PAGE_DESC: Items per page MANAGE_CONSTANT_S3_EXPLAIN: When configuring bucket name and base directory, they can be set using English comma separation. The order must be consistent and missing or empty items will use the default value. diff --git a/public/i18n/zh-CN.yml b/public/i18n/zh-CN.yml index 88b8bc2..55a33c7 100644 --- a/public/i18n/zh-CN.yml +++ b/public/i18n/zh-CN.yml @@ -579,6 +579,8 @@ MANAGE_CONSTANT_S3_BUCKET_DESC: 存储桶名-可选 MANAGE_CONSTANT_S3_BUCKET_PLACEHOLDER: 英文逗号分隔,例如:bucket1,bucket2 MANAGE_CONSTANT_S3_BASE_DIR_DESC: 起始目录-可选 MANAGE_CONSTANT_S3_BASE_DIR_PLACEHOLDER: '英文逗号分隔,例如:/dir1,/dir2' +MANAGE_CONSTANT_S3_DOGE_CLOUD_SUPPORT_DESC: 是否使用Doge Cloud +MANAGE_CONSTANT_S3_DOGE_CLOUD_SUPPORT_TOOLTIP: 开启后,将使用Doge Cloud的API MANAGE_CONSTANT_S3_PAGING_DESC: 是否开启分页 MANAGE_CONSTANT_S3_ITEMS_PAGE_DESC: 每页显示数量 MANAGE_CONSTANT_S3_EXPLAIN: 存储桶名和起始目录配置时可通过英文逗号分隔不同存储桶的设置,顺序必须一致,逗号间留空或缺失项使用默认值 diff --git a/public/i18n/zh-TW.yml b/public/i18n/zh-TW.yml index 0ad0e8e..f981fbe 100644 --- a/public/i18n/zh-TW.yml +++ b/public/i18n/zh-TW.yml @@ -576,6 +576,8 @@ MANAGE_CONSTANT_S3_BUCKET_DESC: 存儲桶名-可選 MANAGE_CONSTANT_S3_BUCKET_PLACEHOLDER: 英文逗號分隔,例如:bucket1,bucket2 MANAGE_CONSTANT_S3_BASE_DIR_DESC: 起始目錄-可選 MANAGE_CONSTANT_S3_BASE_DIR_PLACEHOLDER: '英文逗號分隔,例如:/dir1,/dir2' +MANAGE_CONSTANT_S3_DOGE_CLOUD_SUPPORT_DESC: 啟用 Doge Cloud 支援 +MANAGE_CONSTANT_S3_DOGE_CLOUD_SUPPORT_TOOLTIP: 啟用後,將會啟用Doge Cloud API MANAGE_CONSTANT_S3_PAGING_DESC: 是否開啟分頁 MANAGE_CONSTANT_S3_ITEMS_PAGE_DESC: 每頁顯示數量 MANAGE_CONSTANT_S3_EXPLAIN: 存儲桶名和起始目錄配置時可通過英文逗號分隔不同存儲桶的設置,順序必須一致,逗號間留空或缺失項使用默認值 diff --git a/src/main/manage/apis/s3plist.ts b/src/main/manage/apis/s3plist.ts index 8c2e6da..713c7e2 100644 --- a/src/main/manage/apis/s3plist.ts +++ b/src/main/manage/apis/s3plist.ts @@ -56,11 +56,18 @@ import path from 'path' // 取消下载任务的加载文件列表、刷新下载文件传输列表 import { cancelDownloadLoadingFileList, refreshDownloadFileTransferList } from '@/manage/utils/static' +// dogecloudApi +import { dogecloudApi, DogecloudToken, getTempToken } from '../utils/dogeAPI' + class S3plistApi { baseOptions: S3ClientConfig logger: ManageLogger agent: any proxy: string | undefined + dogeCloudSupport: boolean + accessKeyId: string + secretAccessKey: string + bucketName: string constructor ( accessKeyId: string, @@ -69,8 +76,14 @@ class S3plistApi { sslEnabled: boolean, s3ForcePathStyle: boolean, proxy: string | undefined, - logger: ManageLogger + logger: ManageLogger, + dogeCloudSupport: boolean = false, + bucketName: string = '' ) { + this.accessKeyId = accessKeyId + this.secretAccessKey = secretAccessKey + this.dogeCloudSupport = dogeCloudSupport + this.bucketName = bucketName this.baseOptions = { credentials: { accessKeyId, @@ -85,6 +98,19 @@ class S3plistApi { this.proxy = formatHttpProxy(proxy, 'string') as string | undefined } + async getDogeCloudToken () { + if (!this.dogeCloudSupport) return + const token = await getTempToken(this.accessKeyId, this.secretAccessKey) as DogecloudToken + if (Object.keys(token).length === 0) { + throw new Error('manage.setting.dogeCloudTokenError') + } + this.baseOptions.credentials = { + accessKeyId: token.accessKeyId, + secretAccessKey: token.secretAccessKey, + sessionToken: token.sessionToken + } + } + setAgent (proxy: string | undefined, sslEnabled: boolean) : NodeHttpHandler { const agent = getAgent(proxy, sslEnabled) const commonOptions: AgentOptions = { @@ -149,6 +175,26 @@ class S3plistApi { * 获取存储桶列表 */ async getBucketList (): Promise { + if (this.dogeCloudSupport) { + try { + const res = await dogecloudApi('/oss/bucket/list.json', {}, false, this.accessKeyId, this.secretAccessKey) + for (const item of res.buckets) { + if (item.name === this.bucketName || item.s3Bucket === this.bucketName) { + return [ + { + Name: item.s3Bucket, + CreationDate: item.ctime, + Location: item.region + } + ] + } + } + return [] + } catch (error) { + this.logParam(error, 'getBucketList') + } + return [] + } const options = Object.assign({}, this.baseOptions) as S3ClientConfig const result: IStringKeyMap[] = [] const endpoint = options.endpoint as string || '' @@ -216,7 +262,7 @@ class S3plistApi { try { do { const options = Object.assign({}, this.baseOptions) as S3ClientConfig - options.region = region || 'us-east-1' + options.region = String(region) || 'us-east-1' const client = new S3Client(options) const command = new ListObjectsV2Command({ Bucket: bucket, @@ -255,6 +301,7 @@ class S3plistApi { async getBucketListBackstage (configMap: IStringKeyMap): Promise { const window = windowManager.get(IWindowList.SETTING_WINDOW)! const { bucketName: bucket, bucketConfig: { Location: region }, prefix, cancelToken } = configMap + await this.getDogeCloudToken() const slicedPrefix = prefix.slice(1) const urlPrefix = configMap.customUrl || `https://${bucket}.s3.amazonaws.com` let marker @@ -274,7 +321,7 @@ class S3plistApi { try { do { const options = Object.assign({}, this.baseOptions) as S3ClientConfig - options.region = region || 'us-east-1' + options.region = String(region) || 'us-east-1' const client = new S3Client(options) const command = new ListObjectsV2Command({ Bucket: bucket, @@ -316,6 +363,7 @@ class S3plistApi { async getBucketFileList (configMap: IStringKeyMap): Promise { const { bucketName: bucket, bucketConfig: { Location: region }, prefix, marker, itemsPerPage } = configMap + await this.getDogeCloudToken() const slicedPrefix = prefix.slice(1) const urlPrefix = configMap.customUrl || `https://${bucket}.s3.amazonaws.com` const result = { @@ -325,7 +373,7 @@ class S3plistApi { success: false } try { - const options = Object.assign({}, { ...this.baseOptions, region: region || 'us-east-1' }) as S3ClientConfig + const options = Object.assign({}, { ...this.baseOptions, region: String(region) || 'us-east-1' }) as S3ClientConfig const client = new S3Client(options) const command = new ListObjectsV2Command({ Bucket: bucket, @@ -362,9 +410,10 @@ class S3plistApi { */ async renameBucketFile (configMap: IStringKeyMap): Promise { const { bucketName, region, oldKey, newKey } = configMap + await this.getDogeCloudToken() let result = false try { - const options = Object.assign({}, { ...this.baseOptions, region: region || 'us-east-1' }) as S3ClientConfig + const options = Object.assign({}, { ...this.baseOptions, region: String(region) || 'us-east-1' }) as S3ClientConfig const client = new S3Client(options) const command = new CopyObjectCommand({ Bucket: bucketName, @@ -403,10 +452,11 @@ class S3plistApi { */ async deleteBucketFile (configMap: IStringKeyMap): Promise { const { bucketName, region, key } = configMap + await this.getDogeCloudToken() let result = false try { const options = Object.assign({}, this.baseOptions) as S3ClientConfig - options.region = region || 'us-east-1' + options.region = String(region) || 'us-east-1' const client = new S3Client(options) const command = new DeleteObjectCommand({ Bucket: bucketName, @@ -430,6 +480,7 @@ class S3plistApi { */ async deleteBucketFolder (configMap: IStringKeyMap): Promise { const { bucketName, region, key } = configMap + await this.getDogeCloudToken() let marker let result = false let IsTruncated @@ -441,7 +492,7 @@ class S3plistApi { try { do { const options = Object.assign({}, this.baseOptions) as S3ClientConfig - options.region = region || 'us-east-1' + options.region = String(region) || 'us-east-1' const client = new S3Client(options) const command = new ListObjectsV2Command({ Bucket: bucketName, @@ -476,7 +527,7 @@ class S3plistApi { if (allFileList.Contents.length > 0) { const cycle = Math.ceil(allFileList.Contents.length / 1000) const options = Object.assign({}, this.baseOptions) as S3ClientConfig - options.region = region || 'us-east-1' + options.region = String(region) || 'us-east-1' const client = new S3Client(options) for (let i = 0; i < cycle; i++) { const deleteList = allFileList.Contents.slice(i * 1000, (i + 1) * 1000) @@ -518,9 +569,10 @@ class S3plistApi { */ async getPreSignedUrl (configMap: IStringKeyMap): Promise { const { bucketName, region, key, expires } = configMap + await this.getDogeCloudToken() try { const options = Object.assign({}, this.baseOptions) as S3ClientConfig - options.region = region || 'us-east-1' + options.region = String(region) || 'us-east-1' const client = new S3Client(options) const signedUrl = await getSignedUrl(client, new GetObjectCommand({ Bucket: bucketName, @@ -541,10 +593,11 @@ class S3plistApi { */ async createBucketFolder (configMap: IStringKeyMap): Promise { const { bucketName, region, key } = configMap + await this.getDogeCloudToken() let result = false try { const options = Object.assign({}, this.baseOptions) as S3ClientConfig - options.region = region || 'us-east-1' + options.region = String(region) || 'us-east-1' const client = new S3Client(options) const command = new PutObjectCommand({ Bucket: bucketName, @@ -582,10 +635,11 @@ class S3plistApi { const allowedAcl = ['private', 'public-read', 'public-read-write', 'aws-exec-read', 'authenticated-read', 'bucket-owner-read', 'bucket-owner-full-control'] for (const item of fileArray) { const { bucketName, region, key, filePath, fileName, aclForUpload } = item + await this.getDogeCloudToken() const options = Object.assign({}, this.baseOptions) as S3ClientConfig - options.region = region || 'us-east-1' + options.region = String(region) || 'us-east-1' const client = new S3Client(options) - const id = `${bucketName}-${region}-${key}-${filePath}` + const id = `${bucketName}-${String(region)}-${key}-${filePath}` if (instance.getUploadTask(id)) { continue } @@ -598,7 +652,7 @@ class S3plistApi { sourceFilePath: filePath, targetFilePath: key, targetFileBucket: bucketName, - targetFileRegion: region + targetFileRegion: String(region) }) const parallelUploads3 = new Upload({ client, @@ -661,7 +715,7 @@ class S3plistApi { for (const item of fileArray) { const { bucketName, region, key, fileName, customUrl } = item const savedFilePath = path.join(downloadPath, fileName) - const id = `${bucketName}-${region}-${key}-${savedFilePath}` + const id = `${bucketName}-${String(region)}-${key}-${savedFilePath}` if (instance.getDownloadTask(id)) { continue } @@ -674,7 +728,7 @@ class S3plistApi { }) const preSignedUrl = await this.getPreSignedUrl({ bucketName, - region, + region: String(region), key, expires: 36000, customUrl diff --git a/src/main/manage/manageApi.ts b/src/main/manage/manageApi.ts index bcacf38..e9aee55 100644 --- a/src/main/manage/manageApi.ts +++ b/src/main/manage/manageApi.ts @@ -67,7 +67,7 @@ export class ManageApi extends EventEmitter implements ManageApiType { case 'smms': return new API.SmmsApi(this.currentPicBedConfig.token, this.logger) case 's3plist': - return new API.S3plistApi(this.currentPicBedConfig.accessKeyId, this.currentPicBedConfig.secretAccessKey, this.currentPicBedConfig.endpoint, this.currentPicBedConfig.sslEnabled, this.currentPicBedConfig.s3ForcePathStyle, this.currentPicBedConfig.proxy, this.logger) + return new API.S3plistApi(this.currentPicBedConfig.accessKeyId, this.currentPicBedConfig.secretAccessKey, this.currentPicBedConfig.endpoint, this.currentPicBedConfig.sslEnabled, this.currentPicBedConfig.s3ForcePathStyle, this.currentPicBedConfig.proxy, this.logger, this.currentPicBedConfig.dogeCloudSupport || false, this.currentPicBedConfig.bucketName || '') case 'sftp': return new API.SftpApi(this.currentPicBedConfig.host, this.currentPicBedConfig.port, this.currentPicBedConfig.username, this.currentPicBedConfig.password, this.currentPicBedConfig.privateKey, this.currentPicBedConfig.passphrase, this.currentPicBedConfig.fileMode, this.currentPicBedConfig.dirMode, this.logger) case 'tcyun': diff --git a/src/renderer/manage/pages/logIn.vue b/src/renderer/manage/pages/logIn.vue index 2ee4d8d..ee2a088 100644 --- a/src/renderer/manage/pages/logIn.vue +++ b/src/renderer/manage/pages/logIn.vue @@ -790,6 +790,7 @@ async function transUpToManage (config: IUploaderConfigListItem, picBedName: str sslEnabled: config.endpoint ? config.endpoint.startsWith('https') : false, aclForUpload: 'public-read', s3ForcePathStyle: config.pathStyleAccess, + dogeCloudSupport: false, transformedConfig: JSON.stringify( config.urlPrefix ? { diff --git a/src/renderer/manage/utils/constants.ts b/src/renderer/manage/utils/constants.ts index 05261cd..886d0aa 100644 --- a/src/renderer/manage/utils/constants.ts +++ b/src/renderer/manage/utils/constants.ts @@ -642,6 +642,13 @@ export const supportedPicBedList: IStringKeyMap = { default: '/', tooltip: baseDirTooltip }, + dogeCloudSupport: { + required: false, + description: $T('MANAGE_CONSTANT_S3_DOGE_CLOUD_SUPPORT_DESC'), + default: false, + type: 'boolean', + tooltip: $T('MANAGE_CONSTANT_S3_DOGE_CLOUD_SUPPORT_TOOLTIP') + }, paging: { required: true, description: $T('MANAGE_CONSTANT_S3_PAGING_DESC'), @@ -659,7 +666,7 @@ export const supportedPicBedList: IStringKeyMap = { } }, explain: $T('MANAGE_CONSTANT_S3_EXPLAIN'), - options: ['alias', 'accessKeyId', 'secretAccessKey', 'endpoint', 'sslEnabled', 's3ForcePathStyle', 'proxy', 'aclForUpload', 'bucketName', 'baseDir', 'paging', 'itemsPerPage'], + options: ['alias', 'accessKeyId', 'secretAccessKey', 'endpoint', 'sslEnabled', 's3ForcePathStyle', 'proxy', 'aclForUpload', 'bucketName', 'baseDir', 'dogeCloudSupport', 'paging', 'itemsPerPage'], refLink: 'https://github.com/wayjam/picgo-plugin-s3', referenceText: $T('MANAGE_CONSTANT_S3_REFER_TEXT') }, diff --git a/src/universal/types/i18n.d.ts b/src/universal/types/i18n.d.ts index e86ffcd..2fef912 100644 --- a/src/universal/types/i18n.d.ts +++ b/src/universal/types/i18n.d.ts @@ -539,6 +539,8 @@ interface ILocales { MANAGE_CONSTANT_S3_BUCKET_PLACEHOLDER: string MANAGE_CONSTANT_S3_BASE_DIR_DESC: string MANAGE_CONSTANT_S3_BASE_DIR_PLACEHOLDER: string + MANAGE_CONSTANT_S3_DOGE_CLOUD_SUPPORT_DESC: string + MANAGE_CONSTANT_S3_DOGE_CLOUD_SUPPORT_TOOLTIP: string MANAGE_CONSTANT_S3_PAGING_DESC: string MANAGE_CONSTANT_S3_ITEMS_PAGE_DESC: string MANAGE_CONSTANT_S3_EXPLAIN: string