🐛 Fix: add local path picbed into delete api of http server

This commit is contained in:
萌萌哒赫萝 2023-08-10 05:30:46 -07:00
parent 517348886c
commit f585bb4d7d
19 changed files with 228 additions and 262 deletions

View File

@ -19,6 +19,11 @@ import picgo from '@core/picgo'
import GuiApi from '../../gui' import GuiApi from '../../gui'
import uploader from '.' import uploader from '.'
import { IWindowList } from '#/types/enum' import { IWindowList } from '#/types/enum'
import { picBedsCanbeDeleted } from '#/utils/static'
import path from 'path'
import SSHClient from '~/main/utils/sshClient'
import { ISftpPlistConfig } from 'piclist'
import { getRawData } from '~/renderer/utils/common'
const handleClipboardUploading = async (): Promise<false | ImgInfo[]> => { const handleClipboardUploading = async (): Promise<false | ImgInfo[]> => {
const useBuiltinClipboard = db.get('settings.useBuiltinClipboard') === undefined ? true : !!db.get('settings.useBuiltinClipboard') const useBuiltinClipboard = db.get('settings.useBuiltinClipboard') === undefined ? true : !!db.get('settings.useBuiltinClipboard')
@ -125,9 +130,23 @@ export const uploadChoosedFiles = async (webContents: WebContents, files: IFileW
} }
} }
async function deleteWebdavFile (config: ISftpPlistConfig, fileName: string) {
try {
const client = SSHClient.instance
await client.connect(config)
const uploadPath = `/${(config.uploadPath || '')}/`.replace(/\/+/g, '/')
const remote = path.join(uploadPath, fileName)
const deleteResult = await client.deleteFile(remote)
client.close()
return deleteResult
} catch (err: any) {
console.error(err)
return false
}
}
export const deleteChoosedFiles = async (list: ImgInfo[]): Promise<boolean[]> => { export const deleteChoosedFiles = async (list: ImgInfo[]): Promise<boolean[]> => {
const result = [] const result = []
const picBedsCanbeDeleted = ['smms', 'github', 'imgur', 'tcyun', 'aliyun', 'qiniu', 'upyun', 'aws-s3', 'webdavplist']
for (const item of list) { for (const item of list) {
if (item.id) { if (item.id) {
try { try {
@ -135,23 +154,44 @@ export const deleteChoosedFiles = async (list: ImgInfo[]): Promise<boolean[]> =>
const file = await dbStore.removeById(item.id) const file = await dbStore.removeById(item.id)
if (await picgo.getConfig('settings.deleteCloudFile')) { if (await picgo.getConfig('settings.deleteCloudFile')) {
if (item.type !== undefined && picBedsCanbeDeleted.includes(item.type)) { if (item.type !== undefined && picBedsCanbeDeleted.includes(item.type)) {
setTimeout(() => { if (item.type === 'webdavplist') {
ALLApi.delete(item).then((value: boolean) => { const { fileName, config } = item
if (value) { setTimeout(() => {
const notification = new Notification({ deleteWebdavFile(getRawData(config), fileName || '').then((value: boolean) => {
title: T('MANAGE_BUCKET_BATCH_DELETE_ERROR_MSG_MSG2'), if (value) {
body: T('GALLERY_SYNC_DELETE_NOTICE_SUCCEED') const notification = new Notification({
}) title: T('MANAGE_BUCKET_BATCH_DELETE_ERROR_MSG_MSG2'),
notification.show() body: T('GALLERY_SYNC_DELETE_NOTICE_SUCCEED')
} else { })
const notification = new Notification({ notification.show()
title: T('MANAGE_BUCKET_BATCH_DELETE_ERROR_MSG_MSG2'), } else {
body: T('GALLERY_SYNC_DELETE_NOTICE_FAILED') const notification = new Notification({
}) title: T('MANAGE_BUCKET_BATCH_DELETE_ERROR_MSG_MSG2'),
notification.show() body: T('GALLERY_SYNC_DELETE_NOTICE_FAILED')
} })
}) notification.show()
}, 0) }
})
}, 0)
} else {
setTimeout(() => {
ALLApi.delete(item).then((value: boolean) => {
if (value) {
const notification = new Notification({
title: T('MANAGE_BUCKET_BATCH_DELETE_ERROR_MSG_MSG2'),
body: T('GALLERY_SYNC_DELETE_NOTICE_SUCCEED')
})
notification.show()
} else {
const notification = new Notification({
title: T('MANAGE_BUCKET_BATCH_DELETE_ERROR_MSG_MSG2'),
body: T('GALLERY_SYNC_DELETE_NOTICE_FAILED')
})
notification.show()
}
})
}, 0)
}
} }
} }
setTimeout(() => { setTimeout(() => {

View File

@ -88,15 +88,7 @@ import SSHClient from '../utils/sshClient'
// Sftp 配置类型声明 // Sftp 配置类型声明
import { ISftpPlistConfig } from 'piclist' import { ISftpPlistConfig } from 'piclist'
// AWS S3 相关模块 import { removeFileFromS3InMain } from '~/main/utils/deleteFunc'
import { S3Client, DeleteObjectCommand, S3ClientConfig } from '@aws-sdk/client-s3'
import { NodeHttpHandler } from '@smithy/node-http-handler'
import http, { AgentOptions } from 'http'
import https from 'https'
// 工具函数
import { getAgent } from '../manage/utils/common'
import logger from '@core/picgo/logger'
const STORE_PATH = app.getPath('userData') const STORE_PATH = app.getPath('userData')
@ -192,62 +184,8 @@ export default {
}) })
ipcMain.handle('delete-aws-s3-file', async (_evt: IpcMainInvokeEvent, configMap: IStringKeyMap) => { ipcMain.handle('delete-aws-s3-file', async (_evt: IpcMainInvokeEvent, configMap: IStringKeyMap) => {
try { const result = await removeFileFromS3InMain(configMap)
const { imgUrl, config: { accessKeyID, secretAccessKey, bucketName, region, endpoint, pathStyleAccess, rejectUnauthorized, proxy } } = configMap return result
const url = new URL(!/^https?:\/\//.test(imgUrl) ? `http://${imgUrl}` : imgUrl)
const fileKey = url.pathname.replace(/^\/+/, '')
const endpointUrl: string | undefined = endpoint
? /^https?:\/\//.test(endpoint)
? endpoint
: `http://${endpoint}`
: undefined
const sslEnabled = endpointUrl ? endpointUrl.startsWith('https') : true
const agent = getAgent(proxy, sslEnabled)
const commonOptions: AgentOptions = {
keepAlive: true,
keepAliveMsecs: 1000,
scheduling: 'lifo' as 'lifo' | 'fifo' | undefined
}
const extraOptions = sslEnabled ? { rejectUnauthorized: !!rejectUnauthorized } : {}
const handler = sslEnabled
? new NodeHttpHandler({
httpsAgent: agent.https
? agent.https
: new https.Agent({
...commonOptions,
...extraOptions
})
})
: new NodeHttpHandler({
httpAgent: agent.http
? agent.http
: new http.Agent({
...commonOptions,
...extraOptions
})
})
const s3Options: S3ClientConfig = {
credentials: {
accessKeyId: accessKeyID,
secretAccessKey
},
endpoint: endpointUrl,
tls: sslEnabled,
forcePathStyle: pathStyleAccess,
region,
requestHandler: handler
}
const client = new S3Client(s3Options)
const command = new DeleteObjectCommand({
Bucket: bucketName,
Key: fileKey
})
const result = await client.send(command)
return result.$metadata.httpStatusCode === 204
} catch (err: any) {
logger.error(err)
return false
}
}) })
// migrate from PicGo // migrate from PicGo

View File

@ -15,10 +15,6 @@ import ipcList from '~/main/events/ipcList'
import busEventList from '~/main/events/busEventList' import busEventList from '~/main/events/busEventList'
import { IRemoteNoticeTriggerHook, IWindowList } from '#/types/enum' import { IRemoteNoticeTriggerHook, IWindowList } from '#/types/enum'
import windowManager from 'apis/app/window/windowManager' import windowManager from 'apis/app/window/windowManager'
import {
updateShortKeyFromVersion212,
migrateGalleryFromVersion230
} from '~/main/migrate'
import { import {
uploadChoosedFiles, uploadChoosedFiles,
uploadClipboardFiles uploadClipboardFiles
@ -29,7 +25,7 @@ import {
import server from '~/main/server/index' import server from '~/main/server/index'
import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler' import shortKeyHandler from 'apis/app/shortKey/shortKeyHandler'
import { getUploadFiles } from '~/main/utils/handleArgv' import { getUploadFiles } from '~/main/utils/handleArgv'
import db, { GalleryDB } from '~/main/apis/core/datastore' import db from '~/main/apis/core/datastore'
import bus from '@core/bus' import bus from '@core/bus'
import logger from 'apis/core/picgo/logger' import logger from 'apis/core/picgo/logger'
import picgo from 'apis/core/picgo' import picgo from 'apis/core/picgo'
@ -135,8 +131,6 @@ class LifeCycle {
UpDownTaskQueue.getInstance() UpDownTaskQueue.getInstance()
manageIpcList.listen() manageIpcList.listen()
busEventList.listen() busEventList.listen()
updateShortKeyFromVersion212(db, db.get('settings.shortKey'))
await migrateGalleryFromVersion230(db, GalleryDB.getInstance(), picgo)
} }
private onReady () { private onReady () {

View File

@ -122,7 +122,7 @@ class AliyunApi {
marker, marker,
'max-keys': 1000 'max-keys': 1000
}) as IStringKeyMap }) as IStringKeyMap
if (res.res.statusCode !== 200 || !res.buckets) return { result: [], isTruncated: false } if (res?.res?.statusCode !== 200 || !res?.buckets) return { result: [], isTruncated: false }
const formattedBuckets = res.buckets.map((item: OSS.Bucket) => ({ const formattedBuckets = res.buckets.map((item: OSS.Bucket) => ({
Name: item.name, Name: item.name,
Location: item.region, Location: item.region,
@ -161,7 +161,7 @@ class AliyunApi {
} }
}) })
if (res.status === 200) { if (res?.status === 200) {
const parser = new XMLParser() const parser = new XMLParser()
const result = parser.parse(res.data) const result = parser.parse(res.data)
@ -207,7 +207,7 @@ class AliyunApi {
dataRedundancyType: 'LRS', dataRedundancyType: 'LRS',
timeout: this.timeOut timeout: this.timeOut
}) })
return res && res.res.status === 200 return res?.res?.status === 200
} }
async getBucketListRecursively (configMap: IStringKeyMap): Promise<any> { async getBucketListRecursively (configMap: IStringKeyMap): Promise<any> {
@ -238,8 +238,8 @@ class AliyunApi {
}, { }, {
timeout: this.timeOut timeout: this.timeOut
}) })
if (res && res.res.statusCode === 200) { if (res?.res?.statusCode === 200) {
res.objects && res.objects.forEach((item: OSS.ObjectMeta) => { res?.objects?.forEach((item: OSS.ObjectMeta) => {
item.size !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) item.size !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
}) })
window.webContents.send(refreshDownloadFileTransferList, result) window.webContents.send(refreshDownloadFileTransferList, result)
@ -286,11 +286,11 @@ class AliyunApi {
}, { }, {
timeout: this.timeOut timeout: this.timeOut
}) })
if (res && res.res.statusCode === 200) { if (res?.res?.statusCode === 200) {
res.prefixes && res.prefixes.forEach((item: string) => { res?.prefixes?.forEach((item: string) => {
result.fullList.push(this.formatFolder(item, slicedPrefix)) result.fullList.push(this.formatFolder(item, slicedPrefix))
}) })
res.objects && res.objects.forEach((item: OSS.ObjectMeta) => { res?.objects?.forEach((item: OSS.ObjectMeta) => {
item.size !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix)) item.size !== 0 && result.fullList.push(this.formatFile(item, slicedPrefix, urlPrefix))
}) })
window.webContents.send('refreshFileTransferList', result) window.webContents.send('refreshFileTransferList', result)
@ -521,7 +521,7 @@ class AliyunApi {
} }
).then((res: any) => { ).then((res: any) => {
const id = `${bucketName}-${region}-${key}-${filePath}` const id = `${bucketName}-${region}-${key}-${filePath}`
if (res && res.res.statusCode === 200) { if (res?.res?.statusCode === 200) {
instance.updateUploadTask({ instance.updateUploadTask({
id, id,
progress: 100, progress: 100,
@ -561,7 +561,7 @@ class AliyunApi {
const { bucketName, region, key } = configMap const { bucketName, region, key } = configMap
const client = this.getNewCtx(region, bucketName) const client = this.getNewCtx(region, bucketName)
const res = await client.put(key, Buffer.from('')) as any const res = await client.put(key, Buffer.from('')) as any
return res && res.res.statusCode === 200 return res?.res?.statusCode === 200
} }
/** /**

View File

@ -104,9 +104,8 @@ export class ManageApi extends EventEmitter implements ManageApiType {
getConfig<T> (name?: string): T { getConfig<T> (name?: string): T {
if (!name) { if (!name) {
return this._config as unknown as T return this._config as unknown as T
} else {
return get(this._config, name)
} }
return get(this._config, name)
} }
saveConfig (config: IStringKeyMap): void { saveConfig (config: IStringKeyMap): void {

View File

@ -1,32 +1,33 @@
const AliyunAreaCodeName : IStringKeyMap = { const AliyunAreaCodeName : IStringKeyMap = {
'oss-cn-hangzhou': '华东1(杭州)', 'oss-cn-hangzhou': '华东1(杭州)',
'oss-cn-shanghai': '华东2(上海)', 'oss-cn-shanghai': '华东2(上海)',
'oss-cn-nanjing': '华东5(南京本地地域)', 'oss-cn-nanjing': '华东5(南京)',
'oss-cn-fuzhou': '华东6(福州本地地域)', 'oss-cn-fuzhou': '华东6(福州)',
'oss-cn-qingdao': '华北1(青岛)', 'oss-cn-qingdao': '华北1(青岛)',
'oss-cn-beijing': '华北2(北京)', 'oss-cn-beijing': '华北2(北京)',
'oss-cn-zhangjiakou': '华北3(张家口)', 'oss-cn-zhangjiakou': '华北3(张家口)',
'oss-cn-huhehaote': '华北5(呼和浩特)', 'oss-cn-huhehaote': '华北5(呼和浩特)',
'oss-cn-wulanchabu': '华北6(乌兰察布)', 'oss-cn-wulanchabu': '华北6(乌兰察布)',
'oss-cn-shenzhen': '华南1(深圳)', 'oss-cn-shenzhen': '华南1(深圳)',
'oss-cn-heyuan': '华南2(河源)', 'oss-cn-heyuan': '华南2(河源)',
'oss-cn-guangzhou': '华南3(广州)', 'oss-cn-guangzhou': '华南3(广州)',
'oss-cn-chengdu': '西南1(成都)', 'oss-cn-chengdu': '西南1(成都)',
'oss-cn-hongkong': '中国香港', 'oss-cn-hongkong': '中国香港',
'oss-us-west-1': '美国(硅谷)', 'oss-us-west-1': '美国(硅谷)',
'oss-us-east-1': '美国(弗吉尼亚)', 'oss-us-east-1': '美国(弗吉尼亚)',
'oss-ap-northeast-1': '日本(东京)', 'oss-ap-northeast-1': '日本(东京)',
'oss-ap-northeast-2': '韩国(首尔)', 'oss-ap-northeast-2': '韩国(首尔)',
'oss-ap-southeast-1': '新加坡', 'oss-ap-southeast-1': '新加坡',
'oss-ap-southeast-2': '澳大利亚(悉尼)', 'oss-ap-southeast-2': '澳大利亚(悉尼)',
'oss-ap-southeast-3': '马来西亚(吉隆坡)', 'oss-ap-southeast-3': '马来西亚(吉隆坡)',
'oss-ap-southeast-5': '印度尼西亚(雅加达)', 'oss-ap-southeast-5': '印度尼西亚(雅加达)',
'oss-ap-southeast-6': '菲律宾(马尼拉)', 'oss-ap-southeast-6': '菲律宾(马尼拉)',
'oss-ap-southeast-7': '泰国(曼谷)', 'oss-ap-southeast-7': '泰国(曼谷)',
'oss-ap-south-1': '印度(孟买)', 'oss-ap-south-1': '印度(孟买)',
'oss-eu-central-1': '德国(法兰克福)', 'oss-eu-central-1': '德国(法兰克福)',
'oss-eu-west-1': '英国(伦敦)', 'oss-eu-west-1': '英国(伦敦)',
'oss-me-east-1': '阿联酋(迪拜)' 'oss-me-east-1': '阿联酋(迪拜)',
'oss-rg-china-mainland': '无地域属性'
} }
const QiniuAreaCodeName : IStringKeyMap = { const QiniuAreaCodeName : IStringKeyMap = {
@ -61,8 +62,7 @@ const TencentAreaCodeName : IStringKeyMap = {
'na-ashburn': '弗吉尼亚(美东)', 'na-ashburn': '弗吉尼亚(美东)',
'na-toronto': '多伦多', 'na-toronto': '多伦多',
'sa-saopaulo': '圣保罗', 'sa-saopaulo': '圣保罗',
'eu-frankfurt': '法兰克福', 'eu-frankfurt': '法兰克福'
'eu-moscow': '莫斯科'
} }
export { AliyunAreaCodeName, QiniuAreaCodeName, TencentAreaCodeName } export { AliyunAreaCodeName, QiniuAreaCodeName, TencentAreaCodeName }

View File

@ -92,30 +92,9 @@ export class ManageLogger implements ILogger {
): void { ): void {
try { try {
if (this.checkLogLevel(type, this.logLevel)) { if (this.checkLogLevel(type, this.logLevel)) {
let log = `${dayjs().format( let log = `${dayjs().format('YYYY-MM-DD HH:mm:ss')} [PicList ${type.toUpperCase()}] `
'YYYY-MM-DD HH:mm:ss'
)} [PicList ${type.toUpperCase()}] `
msg.forEach((item: ILogArgvTypeWithError) => { msg.forEach((item: ILogArgvTypeWithError) => {
if (item instanceof Error && type === 'error') { log += this.formatLogItem(item, type)
log += `\n------Error Stack Begin------\n${util.format(
item?.stack
)}\n-------Error Stack End------- `
} else {
if (typeof item === 'object') {
if (item?.stack) {
log = log + `\n------Error Stack Begin------\n${util.format(
item.stack
)}\n-------Error Stack End------- `
}
item = JSON.stringify(item, (key, value) => {
if (key === 'stack') {
return undefined
}
return value
}, 2)
}
log += `${item as string} `
}
}) })
log += '\n' log += '\n'
fs.appendFileSync(logPath, log) fs.appendFileSync(logPath, log)
@ -125,6 +104,22 @@ export class ManageLogger implements ILogger {
} }
} }
private formatLogItem (item: ILogArgvTypeWithError, type: string): string {
let result = ''
if (item instanceof Error && type === 'error') {
result += `\n------Error Stack Begin------\n${util.format(item?.stack)}\n-------Error Stack End------- `
} else {
if (typeof item === 'object') {
if (item?.stack) {
result += `\n------Error Stack Begin------\n${util.format(item.stack)}\n-------Error Stack End------- `
}
item = JSON.stringify(item, (key, value) => (key === 'stack' ? undefined : value), 2)
}
result += `${item as string} `
}
return result
}
private checkLogLevel ( private checkLogLevel (
type: string, type: string,
level: undefined | string | string[] level: undefined | string | string[]
@ -134,9 +129,8 @@ export class ManageLogger implements ILogger {
} }
if (Array.isArray(level)) { if (Array.isArray(level)) {
return level.some((item: string) => item === type || item === 'all') return level.some((item: string) => item === type || item === 'all')
} else {
return type === level
} }
return type === level
} }
success (...msq: ILogArgvType[]): void { success (...msq: ILogArgvType[]): void {

View File

@ -1,61 +0,0 @@
import { DBStore } from '@picgo/store'
import ConfigStore from '~/main/apis/core/datastore'
import path from 'path'
import fse from 'fs-extra'
import { PicGo as PicGoCore } from 'piclist'
import { T } from '~/main/i18n'
// from v2.1.2
const updateShortKeyFromVersion212 = (db: typeof ConfigStore, shortKeyConfig: IShortKeyConfigs | IOldShortKeyConfigs) => {
// #557 极端情况可能会出现配置不存在,需要重新写入
if (shortKeyConfig === undefined) {
const defaultShortKeyConfig = {
enable: true,
key: 'CommandOrControl+Shift+P',
name: 'upload',
label: T('QUICK_UPLOAD')
}
db.set('settings.shortKey[picgo:upload]', defaultShortKeyConfig)
return true
}
if (shortKeyConfig.upload) {
// @ts-ignore
shortKeyConfig['picgo:upload'] = {
enable: true,
key: shortKeyConfig.upload,
name: 'upload',
label: T('QUICK_UPLOAD')
}
// @ts-ignore
delete shortKeyConfig.upload
db.set('settings.shortKey', shortKeyConfig)
return true
}
return false
}
const migrateGalleryFromVersion230 = async (configDB: typeof ConfigStore, galleryDB: DBStore, picgo: PicGoCore) => {
const originGallery: ImgInfo[] = picgo.getConfig('uploaded')
// if hasMigrate, we don't need to migrate
const hasMigrate: boolean = configDB.get('__migrateUploaded')
if (hasMigrate) {
return
}
const configPath = configDB.getConfigPath()
const configBakPath = path.join(path.dirname(configPath), 'config.bak.json')
// migrate gallery from config to gallery db
if (originGallery && Array.isArray(originGallery) && originGallery?.length > 0) {
if (fse.existsSync(configBakPath)) {
fse.copyFileSync(configPath, configBakPath)
}
await galleryDB.insertMany(originGallery)
picgo.saveConfig({
uploaded: [],
__migrateUploaded: true
})
}
}
export {
updateShortKeyFromVersion212,
migrateGalleryFromVersion230
}

View File

@ -11,6 +11,7 @@ import axios from 'axios'
class Server { class Server {
private httpServer: http.Server private httpServer: http.Server
private config: IServerConfig private config: IServerConfig
constructor () { constructor () {
let config = picgo.getConfig<IServerConfig>('settings.server') let config = picgo.getConfig<IServerConfig>('settings.server')
const result = this.checkIfConfigIsValid(config) const result = this.checkIfConfigIsValid(config)
@ -31,11 +32,7 @@ class Server {
} }
private checkIfConfigIsValid (config: IObj | undefined) { private checkIfConfigIsValid (config: IObj | undefined) {
if (config && config.port && config.host && (config.enable !== undefined)) { return config && config.port && config.host && (config.enable !== undefined)
return true
} else {
return false
}
} }
private handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => { private handleRequest = (request: http.IncomingMessage, response: http.ServerResponse) => {

View File

@ -7,9 +7,9 @@ import windowManager from 'apis/app/window/windowManager'
import { uploadChoosedFiles, uploadClipboardFiles, deleteChoosedFiles } from 'apis/app/uploader/apis' import { uploadChoosedFiles, uploadClipboardFiles, deleteChoosedFiles } from 'apis/app/uploader/apis'
import path from 'path' import path from 'path'
import { dbPathDir } from 'apis/core/datastore/dbChecker' import { dbPathDir } from 'apis/core/datastore/dbChecker'
const STORE_PATH = dbPathDir() const STORE_PATH = dbPathDir()
const LOG_PATH = path.join(STORE_PATH, 'piclist.log') const LOG_PATH = path.join(STORE_PATH, 'piclist.log')
// import AllAPI from '../../renderer/apis/allApi'
const errorMessage = `upload error. see ${LOG_PATH} for more detail.` const errorMessage = `upload error. see ${LOG_PATH} for more detail.`
const deleteErrorMessage = `delete error. see ${LOG_PATH} for more detail.` const deleteErrorMessage = `delete error. see ${LOG_PATH} for more detail.`

View File

@ -0,0 +1,64 @@
import { S3Client, DeleteObjectCommand, S3ClientConfig } from '@aws-sdk/client-s3'
import { NodeHttpHandler } from '@smithy/node-http-handler'
import http, { AgentOptions } from 'http'
import https from 'https'
import { getAgent } from '../manage/utils/common'
export async function removeFileFromS3InMain (configMap: IStringKeyMap) {
try {
const { imgUrl, config: { accessKeyID, secretAccessKey, bucketName, region, endpoint, pathStyleAccess, rejectUnauthorized, proxy } } = configMap
const url = new URL(!/^https?:\/\//.test(imgUrl) ? `http://${imgUrl}` : imgUrl)
const fileKey = url.pathname.replace(/^\/+/, '')
const endpointUrl: string | undefined = endpoint
? /^https?:\/\//.test(endpoint)
? endpoint
: `http://${endpoint}`
: undefined
const sslEnabled = endpointUrl ? endpointUrl.startsWith('https') : true
const agent = getAgent(proxy, sslEnabled)
const commonOptions: AgentOptions = {
keepAlive: true,
keepAliveMsecs: 1000,
scheduling: 'lifo' as 'lifo' | 'fifo' | undefined
}
const extraOptions = sslEnabled ? { rejectUnauthorized: !!rejectUnauthorized } : {}
const handler = sslEnabled
? new NodeHttpHandler({
httpsAgent: agent.https
? agent.https
: new https.Agent({
...commonOptions,
...extraOptions
})
})
: new NodeHttpHandler({
httpAgent: agent.http
? agent.http
: new http.Agent({
...commonOptions,
...extraOptions
})
})
const s3Options: S3ClientConfig = {
credentials: {
accessKeyId: accessKeyID,
secretAccessKey
},
endpoint: endpointUrl,
tls: sslEnabled,
forcePathStyle: pathStyleAccess,
region,
requestHandler: handler
}
const client = new S3Client(s3Options)
const command = new DeleteObjectCommand({
Bucket: bucketName,
Key: fileKey
})
const result = await client.send(command)
return result.$metadata.httpStatusCode === 204
} catch (err: any) {
console.log(err)
return false
}
}

View File

@ -1,35 +1,32 @@
import AliyunApi from './aliyun'
import AwsS3Api from './awss3'
import GithubApi from './github'
import ImgurApi from './imgur'
import LocalApi from './local'
import QiniuApi from './qiniu'
import SftpPlistApi from './sftpplist'
import SmmsApi from './smms' import SmmsApi from './smms'
import TcyunApi from './tcyun' import TcyunApi from './tcyun'
import AliyunApi from './aliyun'
import QiniuApi from './qiniu'
import ImgurApi from './imgur'
import GithubApi from './github'
import UpyunApi from './upyun' import UpyunApi from './upyun'
import AwsS3Api from './awss3'
import WebdavApi from './webdav' import WebdavApi from './webdav'
import LocalApi from './local'
import SftpPlistApi from './sftpplist'
const apiMap: IStringKeyMap = { const apiMap: IStringKeyMap = {
aliyun: AliyunApi,
'aws-s3': AwsS3Api,
github: GithubApi,
imgur: ImgurApi,
local: LocalApi,
qiniu: QiniuApi,
sftpplist: SftpPlistApi,
smms: SmmsApi, smms: SmmsApi,
tcyun: TcyunApi, tcyun: TcyunApi,
aliyun: AliyunApi,
qiniu: QiniuApi,
imgur: ImgurApi,
github: GithubApi,
upyun: UpyunApi, upyun: UpyunApi,
'aws-s3': AwsS3Api, webdavplist: WebdavApi
webdavplist: WebdavApi,
local: LocalApi,
sftpplist: SftpPlistApi
} }
export default class ALLApi { export default class ALLApi {
static async delete (configMap: IStringKeyMap): Promise<boolean> { static async delete (configMap: IStringKeyMap): Promise<boolean> {
const api = apiMap[configMap.type] const api = apiMap[configMap.type]
if (api) { return api ? await api.delete(configMap) : false
return await api.delete(configMap)
}
return false
} }
} }

View File

@ -1,13 +1,15 @@
import { ipcRenderer } from 'electron' import { ipcRenderer } from 'electron'
import { getRawData } from '~/renderer/utils/common' import { getRawData } from '~/renderer/utils/common'
import { removeFileFromS3InMain } from '~/main/utils/deleteFunc'
export default class AwsS3Api { export default class AwsS3Api {
static async delete (configMap: IStringKeyMap): Promise<boolean> { static async delete (configMap: IStringKeyMap): Promise<boolean> {
try { try {
const deleteResult = await ipcRenderer.invoke('delete-aws-s3-file', return ipcRenderer
getRawData(configMap) ? await ipcRenderer.invoke('delete-aws-s3-file',
) getRawData(configMap)
return deleteResult )
: await removeFileFromS3InMain(getRawData(configMap))
} catch (error) { } catch (error) {
console.log(error) console.log(error)
return false return false

View File

@ -17,6 +17,8 @@ interface IConfig {
} }
export default class ImgurApi { export default class ImgurApi {
static baseUrl: 'https://api.imgur.com/3'
private static async makeRequest ( private static async makeRequest (
method: 'delete', method: 'delete',
url: string, url: string,
@ -35,25 +37,23 @@ export default class ImgurApi {
} }
static async delete (configMap: IConfigMap): Promise<boolean> { static async delete (configMap: IConfigMap): Promise<boolean> {
const { config = {}, hash = '' } = configMap || {} const {
const { clientId = '', username = '', accessToken = '' } = config config: { clientId = '', username = '', accessToken = '' } = {},
const baseUrl = 'https://api.imgur.com/3' hash = ''
let Authorization: string } = configMap
let apiUrl: string let Authorization: string, apiUrl: string
if (username && accessToken) { if (username && accessToken) {
Authorization = `Bearer ${accessToken}` Authorization = `Bearer ${accessToken}`
apiUrl = `${baseUrl}/account/${username}/image/${hash}` apiUrl = `${ImgurApi.baseUrl}/account/${username}/image/${hash}`
} else if (clientId) { } else if (clientId) {
Authorization = `Client-ID ${clientId}` Authorization = `Client-ID ${clientId}`
apiUrl = `${baseUrl}/image/${hash}` apiUrl = `${ImgurApi.baseUrl}/image/${hash}`
} else { } else {
return false return false
} }
const headers = {
Authorization
}
const requestConfig: IConfig = { const requestConfig: IConfig = {
headers, headers: { Authorization },
timeout: 30000 timeout: 30000
} }
return ImgurApi.makeRequest('delete', apiUrl, requestConfig) return ImgurApi.makeRequest('delete', apiUrl, requestConfig)

View File

@ -8,7 +8,7 @@ export default class LocalApi {
static async delete (configMap: IConfigMap): Promise<boolean> { static async delete (configMap: IConfigMap): Promise<boolean> {
const { hash } = configMap const { hash } = configMap
if (!hash) { if (!hash) {
console.error('SmmsApi.delete: invalid params') console.error('Local.delete: invalid params')
return false return false
} }

View File

@ -31,7 +31,7 @@ export default class QiniuApi {
} }
}) })
}) as any }) as any
return res && res.respInfo.statusCode === 200 return res?.respInfo?.statusCode === 200
} catch (error) { } catch (error) {
console.error(error) console.error(error)
return false return false

View File

@ -322,7 +322,7 @@ const choosedPicBedForQRCode: Ref<string[]> = ref([])
const isAlwaysOnTop = ref(false) const isAlwaysOnTop = ref(false)
const keepAlivePages = $router.getRoutes().filter(item => item.meta.keepAlive).map(item => item.name as string) const keepAlivePages = $router.getRoutes().filter(item => item.meta.keepAlive).map(item => item.name as string)
const progressShow = ref(true) const progressShow = ref(false)
const progressPercentage = ref(0) const progressPercentage = ref(0)
onBeforeMount(() => { onBeforeMount(() => {

View File

@ -461,6 +461,7 @@ import ALLApi from '@/apis/allApi'
// //
import { customRenameFormatTable, customStrMatch, customStrReplace } from '../manage/utils/common' import { customRenameFormatTable, customStrMatch, customStrReplace } from '../manage/utils/common'
import { picBedsCanbeDeleted } from '#/utils/static'
const images = ref<ImgInfo[]>([]) const images = ref<ImgInfo[]>([])
const dialogVisible = ref(false) const dialogVisible = ref(false)
@ -688,7 +689,6 @@ function remove (item: ImgInfo) {
type: 'warning' type: 'warning'
}).then(async () => { }).then(async () => {
const file = await $$db.getById(item.id!) const file = await $$db.getById(item.id!)
const picBedsCanbeDeleted = ['smms', 'github', 'imgur', 'tcyun', 'aliyun', 'qiniu', 'upyun', 'aws-s3', 'webdavplist', 'local', 'sftpplist']
if (await getConfig('settings.deleteCloudFile')) { if (await getConfig('settings.deleteCloudFile')) {
if (item.type !== undefined && picBedsCanbeDeleted.includes(item.type)) { if (item.type !== undefined && picBedsCanbeDeleted.includes(item.type)) {
const result = await ALLApi.delete(item) const result = await ALLApi.delete(item)
@ -798,7 +798,6 @@ function multiRemove () {
const files: IResult<ImgInfo>[] = [] const files: IResult<ImgInfo>[] = []
const imageIDList = Object.keys(choosedList) const imageIDList = Object.keys(choosedList)
const isDeleteCloudFile = await getConfig('settings.deleteCloudFile') const isDeleteCloudFile = await getConfig('settings.deleteCloudFile')
const picBedsCanbeDeleted = ['smms', 'github', 'imgur', 'tcyun', 'aliyun', 'qiniu', 'upyun', 'aws-s3', 'webdavplist', 'local', 'sftpplist']
if (isDeleteCloudFile) { if (isDeleteCloudFile) {
for (let i = 0; i < imageIDList.length; i++) { for (let i = 0; i < imageIDList.length; i++) {
const key = imageIDList[i] const key = imageIDList[i]
@ -1161,3 +1160,4 @@ export default {
color #ddd color #ddd
margin-bottom 10px margin-bottom 10px
</style> </style>
@/apis

View File

@ -5,3 +5,5 @@ export const RELEASE_URL = 'https://api.github.com/repos/Kuingsmile/PicList/rele
export const RELEASE_URL_BACKUP = 'https://release.piclist.cn' 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']