mirror of
https://github.com/Kuingsmile/PicList.git
synced 2025-01-23 06:38:13 -05:00
✨ Feature: add remote delete support for huawei obs and doge cloud
This commit is contained in:
parent
78be49d57b
commit
746360b486
@ -88,7 +88,7 @@ import SSHClient from '../utils/sshClient'
|
|||||||
// Sftp 配置类型声明
|
// Sftp 配置类型声明
|
||||||
import { ISftpPlistConfig } from 'piclist'
|
import { ISftpPlistConfig } from 'piclist'
|
||||||
|
|
||||||
import { removeFileFromS3InMain } from '~/main/utils/deleteFunc'
|
import { removeFileFromS3InMain, removeFileFromDogeInMain, removeFileFromHuaweiInMain } from '~/main/utils/deleteFunc'
|
||||||
|
|
||||||
const STORE_PATH = app.getPath('userData')
|
const STORE_PATH = app.getPath('userData')
|
||||||
|
|
||||||
@ -188,6 +188,16 @@ export default {
|
|||||||
return result
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('delete-doge-file', async (_evt: IpcMainInvokeEvent, configMap: IStringKeyMap) => {
|
||||||
|
const result = await removeFileFromDogeInMain(configMap)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
ipcMain.handle('delete-huaweicloud-file', async (_evt: IpcMainInvokeEvent, configMap: IStringKeyMap) => {
|
||||||
|
const result = await removeFileFromHuaweiInMain(configMap)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
// migrate from PicGo
|
// migrate from PicGo
|
||||||
|
|
||||||
ipcMain.handle('migrateFromPicGo', async () => {
|
ipcMain.handle('migrateFromPicGo', async () => {
|
||||||
|
@ -54,7 +54,7 @@ export async function getTempToken (accessKey: string, secretKey: string): Promi
|
|||||||
Credentials: {
|
Credentials: {
|
||||||
'doge-token': {
|
'doge-token': {
|
||||||
token,
|
token,
|
||||||
expires: Date.now() + 7200000
|
expires: data.ExpiredAt * 1000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -3,8 +3,75 @@ import { NodeHttpHandler } from '@smithy/node-http-handler'
|
|||||||
import http, { AgentOptions } from 'http'
|
import http, { AgentOptions } from 'http'
|
||||||
import https from 'https'
|
import https from 'https'
|
||||||
import { getAgent } from '../manage/utils/common'
|
import { getAgent } from '../manage/utils/common'
|
||||||
|
import axios from 'axios'
|
||||||
|
import crypto from 'crypto'
|
||||||
|
import querystring from 'querystring'
|
||||||
|
|
||||||
export async function removeFileFromS3InMain (configMap: IStringKeyMap) {
|
interface DogecloudTokenFull {
|
||||||
|
Credentials: {
|
||||||
|
accessKeyId: string
|
||||||
|
secretAccessKey: string
|
||||||
|
sessionToken: string
|
||||||
|
},
|
||||||
|
ExpiredAt: number,
|
||||||
|
Buckets: {
|
||||||
|
name: string
|
||||||
|
s3Bucket: string
|
||||||
|
s3Endpoint: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const dogeRegionMap: IStringKeyMap = {
|
||||||
|
'ap-shanghai': '0',
|
||||||
|
'ap-beijing': '1',
|
||||||
|
'ap-guangzhou': '2',
|
||||||
|
'ap-chengdu': '3'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function dogecloudApi (
|
||||||
|
apiPath: string,
|
||||||
|
data = {},
|
||||||
|
jsonMode: boolean = false,
|
||||||
|
accessKey: string,
|
||||||
|
secretKey: string
|
||||||
|
) {
|
||||||
|
const body = jsonMode ? JSON.stringify(data) : querystring.encode(data)
|
||||||
|
const sign = crypto.createHmac('sha1', secretKey).update(Buffer.from(apiPath + '\n' + body, 'utf8')).digest('hex')
|
||||||
|
const authorization = `TOKEN ${accessKey}:${sign}`
|
||||||
|
try {
|
||||||
|
const res = await axios.request({
|
||||||
|
url: `https://api.dogecloud.com${apiPath}`,
|
||||||
|
method: 'POST',
|
||||||
|
data: body,
|
||||||
|
responseType: 'json',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': jsonMode ? 'application/json' : 'application/x-www-form-urlencoded',
|
||||||
|
Authorization: authorization
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (res.data.code !== 200) {
|
||||||
|
throw new Error('API Error')
|
||||||
|
}
|
||||||
|
return res.data.data
|
||||||
|
} catch (err: any) {
|
||||||
|
throw new Error('API Error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getDogeToken (accessKey: string, secretKey: string): Promise<{} | DogecloudTokenFull> {
|
||||||
|
try {
|
||||||
|
const data = await dogecloudApi('/auth/tmp_token.json', {
|
||||||
|
channel: 'OSS_FULL',
|
||||||
|
scopes: ['*']
|
||||||
|
}, true, accessKey, secretKey)
|
||||||
|
return data
|
||||||
|
} catch (err: any) {
|
||||||
|
console.log(err)
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeFileFromS3InMain (configMap: IStringKeyMap, dogeMode: boolean = false) {
|
||||||
try {
|
try {
|
||||||
const { imgUrl, config: { accessKeyID, secretAccessKey, bucketName, region, endpoint, pathStyleAccess, rejectUnauthorized, proxy } } = configMap
|
const { imgUrl, config: { accessKeyID, secretAccessKey, bucketName, region, endpoint, pathStyleAccess, rejectUnauthorized, proxy } } = configMap
|
||||||
const url = new URL(!/^https?:\/\//.test(imgUrl) ? `http://${imgUrl}` : imgUrl)
|
const url = new URL(!/^https?:\/\//.test(imgUrl) ? `http://${imgUrl}` : imgUrl)
|
||||||
@ -50,6 +117,13 @@ export async function removeFileFromS3InMain (configMap: IStringKeyMap) {
|
|||||||
region,
|
region,
|
||||||
requestHandler: handler
|
requestHandler: handler
|
||||||
}
|
}
|
||||||
|
if (dogeMode) {
|
||||||
|
s3Options.credentials = {
|
||||||
|
accessKeyId: configMap.config.accessKeyID,
|
||||||
|
secretAccessKey: configMap.config.secretAccessKey,
|
||||||
|
sessionToken: configMap.config.sessionToken
|
||||||
|
}
|
||||||
|
}
|
||||||
const client = new S3Client(s3Options)
|
const client = new S3Client(s3Options)
|
||||||
const command = new DeleteObjectCommand({
|
const command = new DeleteObjectCommand({
|
||||||
Bucket: bucketName,
|
Bucket: bucketName,
|
||||||
@ -62,3 +136,64 @@ export async function removeFileFromS3InMain (configMap: IStringKeyMap) {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function removeFileFromDogeInMain (configMap: IStringKeyMap) {
|
||||||
|
try {
|
||||||
|
const { config: { bucketName, AccessKey, SecretKey } } = configMap
|
||||||
|
const token = await getDogeToken(AccessKey, SecretKey) as DogecloudTokenFull
|
||||||
|
const bucket = token.Buckets?.find(item => item.name === bucketName || item.s3Bucket === bucketName)
|
||||||
|
const newConfigMap = Object.assign({}, configMap)
|
||||||
|
newConfigMap.config = {
|
||||||
|
...newConfigMap.config,
|
||||||
|
accessKeyID: token.Credentials?.accessKeyId,
|
||||||
|
secretAccessKey: token.Credentials?.secretAccessKey,
|
||||||
|
sessionToken: token.Credentials?.sessionToken,
|
||||||
|
endpoint: bucket?.s3Endpoint,
|
||||||
|
region: dogeRegionMap[bucket?.s3Endpoint?.split('.')[1] || 'ap-shanghai'],
|
||||||
|
bucketName: bucket?.s3Bucket
|
||||||
|
}
|
||||||
|
return await removeFileFromS3InMain(newConfigMap, true)
|
||||||
|
} catch (err: any) {
|
||||||
|
console.log(err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createHuaweiAuthorization (
|
||||||
|
bucketName: string,
|
||||||
|
path: string,
|
||||||
|
fileName: string,
|
||||||
|
accessKey: string,
|
||||||
|
secretKey: string,
|
||||||
|
date: string = new Date().toUTCString()
|
||||||
|
) {
|
||||||
|
const strToSign = `DELETE\n\n\n${date}\n/${bucketName}${path}/${fileName}`
|
||||||
|
const singature = crypto.createHmac('sha1', secretKey).update(strToSign).digest('base64')
|
||||||
|
return `OBS ${accessKey}:${singature}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function removeFileFromHuaweiInMain (configMap: IStringKeyMap) {
|
||||||
|
const { fileName, config } = configMap
|
||||||
|
const { accessKeyId, accessKeySecret, bucketName, endpoint } = config
|
||||||
|
let path = config.path || '/'
|
||||||
|
path = `/${path.replace(/^\/+|\/+$/, '')}`
|
||||||
|
path = path === '/' ? '' : path
|
||||||
|
const date = new Date().toUTCString()
|
||||||
|
const authorization = createHuaweiAuthorization(bucketName, path, fileName, accessKeyId, accessKeySecret, date)
|
||||||
|
try {
|
||||||
|
const res = await axios.request({
|
||||||
|
url: `https://${bucketName}.${endpoint}${encodeURI(path)}/${encodeURIComponent(fileName)}`,
|
||||||
|
method: 'DELETE',
|
||||||
|
responseType: 'json',
|
||||||
|
headers: {
|
||||||
|
Host: `${bucketName}.${endpoint}`,
|
||||||
|
Date: date,
|
||||||
|
Authorization: authorization
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return res.status === 204
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,6 +9,8 @@ import SmmsApi from './smms'
|
|||||||
import TcyunApi from './tcyun'
|
import TcyunApi from './tcyun'
|
||||||
import UpyunApi from './upyun'
|
import UpyunApi from './upyun'
|
||||||
import WebdavApi from './webdav'
|
import WebdavApi from './webdav'
|
||||||
|
import DogeCloudApi from './dogecloud'
|
||||||
|
import HuaweicloudApi from './huaweiyun'
|
||||||
|
|
||||||
const apiMap: IStringKeyMap = {
|
const apiMap: IStringKeyMap = {
|
||||||
aliyun: AliyunApi,
|
aliyun: AliyunApi,
|
||||||
@ -21,7 +23,9 @@ const apiMap: IStringKeyMap = {
|
|||||||
smms: SmmsApi,
|
smms: SmmsApi,
|
||||||
tcyun: TcyunApi,
|
tcyun: TcyunApi,
|
||||||
upyun: UpyunApi,
|
upyun: UpyunApi,
|
||||||
webdavplist: WebdavApi
|
webdavplist: WebdavApi,
|
||||||
|
dogecloud: DogeCloudApi,
|
||||||
|
'huaweicloud-uploader': HuaweicloudApi
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ALLApi {
|
export default class ALLApi {
|
||||||
|
18
src/renderer/apis/dogecloud.ts
Normal file
18
src/renderer/apis/dogecloud.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { ipcRenderer } from 'electron'
|
||||||
|
import { getRawData } from '~/renderer/utils/common'
|
||||||
|
import { removeFileFromDogeInMain } from '~/main/utils/deleteFunc'
|
||||||
|
|
||||||
|
export default class AwsS3Api {
|
||||||
|
static async delete (configMap: IStringKeyMap): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
return ipcRenderer
|
||||||
|
? await ipcRenderer.invoke('delete-doge-file',
|
||||||
|
getRawData(configMap)
|
||||||
|
)
|
||||||
|
: await removeFileFromDogeInMain(getRawData(configMap))
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
src/renderer/apis/huaweiyun.ts
Normal file
18
src/renderer/apis/huaweiyun.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { ipcRenderer } from 'electron'
|
||||||
|
import { getRawData } from '~/renderer/utils/common'
|
||||||
|
import { removeFileFromHuaweiInMain } from '~/main/utils/deleteFunc'
|
||||||
|
|
||||||
|
export default class HuaweicloudApi {
|
||||||
|
static async delete (configMap: IStringKeyMap): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
return ipcRenderer
|
||||||
|
? await ipcRenderer.invoke('delete-huaweicloud-file',
|
||||||
|
getRawData(configMap)
|
||||||
|
)
|
||||||
|
: await removeFileFromHuaweiInMain(getRawData(configMap))
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -476,7 +476,6 @@ function handleSelectMenu (bucketName: string) {
|
|||||||
webPath: currentPicBedConfig.webPath || ''
|
webPath: currentPicBedConfig.webPath || ''
|
||||||
}
|
}
|
||||||
currentSelectedBucket.value = bucketName
|
currentSelectedBucket.value = bucketName
|
||||||
console.log(configMap)
|
|
||||||
router.push({
|
router.push({
|
||||||
path: '/main-page/manage-main-page/manage-bucket-page',
|
path: '/main-page/manage-main-page/manage-bucket-page',
|
||||||
query: {
|
query: {
|
||||||
|
@ -6,4 +6,4 @@ export const RELEASE_URL_BACKUP = 'https://release.piclist.cn'
|
|||||||
export const STABLE_RELEASE_URL = 'https://github.com/Kuingsmile/PicList/releases/latest'
|
export const STABLE_RELEASE_URL = 'https://github.com/Kuingsmile/PicList/releases/latest'
|
||||||
export const C1 = Buffer.from(C1N, 'base64').toString()
|
export const C1 = Buffer.from(C1N, 'base64').toString()
|
||||||
|
|
||||||
export const picBedsCanbeDeleted = ['aliyun', 'aws-s3', 'github', 'imgur', 'local', 'sftpplist', 'smms', 'qiniu', 'tcyun', 'upyun', 'webdavplist']
|
export const picBedsCanbeDeleted = ['aliyun', 'aws-s3', 'github', 'imgur', 'local', 'sftpplist', 'smms', 'qiniu', 'tcyun', 'upyun', 'webdavplist', 'dogecloud', 'huaweicloud-uploader']
|
||||||
|
1
token.json
Normal file
1
token.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"credentials":{"accessKeyId":"AKIDnom3VMJULIYRTDSAbfViNIwBgO8WT9z8O5tvIUTG4jffEZhnAes5vx9AOYJrIilA","secretAccessKey":"OTl1srVfPKoheYjbjmF+LoOTnL66fZlfmEzPhCllBtw=","sessionToken":"fbliLFsUatpu7Nc77ob0jtfwDB38j56a928f93e916c50c385c0848816f6779acuXXhUtSZWuvvNDn4um8ZU0pMkOnax0HQZQ-dAtVnEWTgA2eBU_oRr7O1l5cypeTh1HeEBL4hwydggwJ6Tuy5kvYVCvgDcCfUgpNwr8rw-mUNXW_rvgFS0CdldipPY4ta2QrQA4IC8SDvC25ruTygBzA3nMqf-uyHs5VmlAM5e8Mab3kKtP6Y5Q1PSySSDYFq7LMZ3KbfGtvGtdp6RHSQI_24_3GLUgyIBeRYvN-viZP86oWfWiEVz0DzZa8T2LR5Z0rR01BZfb3k0CMdctojaAFRgeIbRa5aBIvv-r1DahAF80dtsUk-V8KmzpP2l_N6NT4nk2b6MLTzLAY2i0KPyQEOc8t8pdZUvVDwtTVelmHw2Vg6-pHRz8rraW2QiqApZcGKvgQwhAfnYSgbLCT68csZUkgKCME7gOzk87of50_TOP39ppQ14Twvma3vkn55C72x0BG83ExnUn9IeG02UHWabGzgZyjG7dvukfcIP_zzGzW_G-4hD0_w2tBVwkKfog1bVH48QN3asBnpiFXprqoeDuuRjvvIgF3Fp8tG4aHLcSzgwVyBqrQgo93EkkKWb-VafpquG0KGfed29rG63M78XgYnaAqHSFuo0e22UMMyupLwOc-cmwsMJYiEU_0j"},"s3Bucket":"s-gz-7032-piclist2-1258813047","s3Endpoint":"https://cos.ap-guangzhou.myqcloud.com"}
|
Loading…
Reference in New Issue
Block a user