Feature(custom): add log for cloud delete

This commit is contained in:
Kuingsmile
2025-08-22 15:02:34 +08:00
parent b74ab0e6b8
commit 3e98fd4325
13 changed files with 177 additions and 231 deletions

View File

@@ -15,10 +15,8 @@ interface IConfigMap {
}
export default class AlistApi {
static async delete(configMap: IConfigMap): Promise<boolean> {
const { fileName, config } = configMap
static async delete({ fileName, config: { version, url, uploadPath, token } }: IConfigMap): Promise<boolean> {
try {
const { version, url, uploadPath, token } = config
if (String(version) === '2') {
deleteLog(fileName, 'Alist', false, 'Alist version 2 is not supported, deletion is skipped')
return true
@@ -35,12 +33,9 @@ export default class AlistApi {
names: [path.basename(fileName)]
}
})
if (result.data.code === 200) {
deleteLog(fileName, 'Alist')
return true
}
deleteLog(fileName, 'Alist', false)
return false
const ok = result.data.code === 200
deleteLog(fileName, 'Alist', ok)
return ok
} catch (error: any) {
deleteFailedLog(fileName, 'Alist', error)
return false

View File

@@ -50,12 +50,9 @@ export default class AListplistApi {
names: [path.basename(fileName)]
}
})
if (result.data.code === 200) {
deleteLog(fileName, 'Alist')
return true
}
deleteLog(fileName, 'Alist', false)
return false
const ok = result.data.code === 200
deleteLog(fileName, 'Alist', ok)
return ok
} catch (error: any) {
deleteFailedLog(fileName, 'Alist', error)
return false

View File

@@ -19,12 +19,9 @@ export default class AliyunApi {
const client = new OSS({ ...config, region: config.area })
const key = AliyunApi.#getKey(fileName, config.path)
const result = await client.delete(key)
if (result.res.status === 204) {
deleteLog(fileName, 'Aliyun')
return true
}
deleteLog(fileName, 'Aliyun', false)
return false
const ok = result.res.status === 204
deleteLog(fileName, 'Aliyun', ok)
return ok
} catch (error: any) {
deleteFailedLog(fileName, 'Aliyun', error)
return false

View File

@@ -39,12 +39,9 @@ export default class GithubApi {
sha: hash,
branch
})
if (status === 200) {
deleteLog(fileName, 'GitHub')
return true
}
deleteLog(fileName, 'GitHub', false)
return false
const ok = status === 200
deleteLog(fileName, 'GitHub', ok)
return ok
} catch (error: any) {
deleteFailedLog(fileName, 'GitHub', error)
return false

View File

@@ -30,12 +30,9 @@ export default class ImgurApi {
headers: { Authorization },
timeout: 30000
})
if (response.status === 200) {
deleteLog(hash, 'Imgur')
return true
}
deleteLog(hash, 'Imgur', false)
return false
const ok = response.status === 200
deleteLog(hash, 'Imgur', ok)
return ok
} catch (error: any) {
deleteFailedLog(hash, 'Imgur', error)
return false

View File

@@ -33,12 +33,9 @@ export default class LskyplistApi {
timeout: 30000,
httpsAgent: requestAgent
})
if (response.status === 200 && response.data.status === true) {
deleteLog(hash, 'Lskyplist')
return true
}
deleteLog(hash, 'Lskyplist', false)
return false
const ok = response.status === 200 && response.data.status === true
deleteLog(hash, 'Lskyplist', ok)
return ok
} catch (error: any) {
deleteFailedLog(hash, 'Lskyplist', error)
return false

View File

@@ -20,12 +20,9 @@ export default class PiclistApi {
const response: AxiosResponse = await axios.post(url, {
list: [fullResult]
})
if (response.status === 200 && response.data?.success) {
deleteLog(fullResult, 'Piclist')
return true
}
deleteLog(fullResult, 'Piclist', false)
return false
const ok = response.status === 200 && response.data?.success
deleteLog(fullResult, 'Piclist', ok)
return ok
} catch (error: any) {
deleteFailedLog(fullResult, 'Piclist', error)
return false

View File

@@ -31,12 +31,9 @@ export default class QiniuApi {
}
})
})) as any
if (res?.respInfo?.statusCode === 200) {
deleteLog(fileName, 'Qiniu')
return true
}
deleteLog(fileName, 'Qiniu', false)
return false
const ok = res?.respInfo?.statusCode === 200
deleteLog(fileName, 'Qiniu', ok)
return ok
} catch (error: any) {
deleteFailedLog(fileName, 'Qiniu', error)
return false

View File

@@ -31,12 +31,9 @@ export default class SmmsApi {
},
timeout: 30000
})
if (response.status === 200) {
deleteLog(hash, 'Smms')
return true
}
deleteLog(hash, 'Smms', false)
return false
const ok = response.status === 200
deleteLog(hash, 'Smms', ok)
return ok
} catch (error: any) {
deleteFailedLog(hash, 'Smms', error)
return false

View File

@@ -32,12 +32,9 @@ export default class TcyunApi {
Region: area,
Key: key
})
if (result.statusCode === 204) {
deleteLog(fileName, 'Tcyun')
return true
}
deleteLog(fileName, 'Tcyun', false)
return false
const ok = result.statusCode === 204
deleteLog(fileName, 'Tcyun', ok)
return ok
} catch (error: any) {
deleteFailedLog(fileName, 'Tcyun', error)
return false

View File

@@ -24,12 +24,8 @@ export default class UpyunApi {
key = `${path.replace(/^\/+|\/+$/, '')}/${fileName}`
}
const result = await client.deleteFile(key)
if (result) {
deleteLog(fileName, 'Upyun')
return true
}
deleteLog(fileName, 'Upyun', false)
return false
deleteLog(fileName, 'Upyun', !!result)
return !!result
} catch (error: any) {
deleteFailedLog(fileName, 'Upyun', error)
return false

View File

@@ -89,144 +89,134 @@ async function getDogeToken(accessKey: string, secretKey: string): Promise<IObj
}
export async function removeFileFromS3InMain(configMap: IStringKeyMap, dogeMode: boolean = false) {
try {
const {
url: rawUrl,
type,
config: {
accessKeyID,
secretAccessKey,
bucketName,
endpoint,
pathStyleAccess,
rejectUnauthorized,
proxy,
urlPrefix
}
} = configMap
let {
imgUrl,
config: { region }
} = configMap
if (type === 'aws-s3' || type === 'aws-s3-plist') {
imgUrl = rawUrl || imgUrl || ''
const {
url: rawUrl,
type,
config: {
accessKeyID,
secretAccessKey,
bucketName,
endpoint,
pathStyleAccess,
rejectUnauthorized,
proxy,
urlPrefix
}
let fileKey
if (urlPrefix && imgUrl.startsWith(urlPrefix)) {
const urlPrefixObj = new URL(urlPrefix)
const imgUrlObj = new URL(imgUrl)
if (imgUrlObj.pathname.startsWith(urlPrefixObj.pathname)) {
fileKey = imgUrlObj.pathname.substring(urlPrefixObj.pathname.length).replace(/^\/+/, '')
} else {
fileKey = imgUrlObj.pathname.replace(/^\/+/, '')
}
} else {
const url = new URL(!/^https?:\/\//.test(imgUrl) ? `http://${imgUrl}` : imgUrl)
fileKey = url.pathname.replace(/^\/+/, '')
if (pathStyleAccess) {
fileKey = fileKey.replace(/^[^/]+\//, '')
}
}
const endpointUrl: string | undefined = endpoint
? /^https?:\/\//.test(endpoint)
? endpoint
: `http://${endpoint}`
: undefined
if (endpointUrl && endpointUrl.includes('cloudflarestorage')) {
region = region || 'auto'
}
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
}
if (dogeMode) {
s3Options.credentials = {
accessKeyId: configMap.config.accessKeyID,
secretAccessKey: configMap.config.secretAccessKey,
sessionToken: configMap.config.sessionToken
}
}
let result: any
try {
fileKey = decodeURIComponent(fileKey)
} catch (err: any) {}
try {
const client = new S3Client(s3Options)
const command = new DeleteObjectCommand({
Bucket: bucketName,
Key: fileKey
})
result = await client.send(command)
} catch (err: any) {
s3Options.region = 'us-east-1'
const client = new S3Client(s3Options)
const command = new DeleteObjectCommand({
Bucket: bucketName,
Key: fileKey
})
result = await client.send(command)
}
return result.$metadata.httpStatusCode === 204
} catch (err: any) {
logger.error(err)
return false
} = configMap
let {
imgUrl,
config: { region }
} = configMap
if (type === 'aws-s3' || type === 'aws-s3-plist') {
imgUrl = rawUrl || imgUrl || ''
}
let fileKey
if (urlPrefix && imgUrl.startsWith(urlPrefix)) {
const urlPrefixObj = new URL(urlPrefix)
const imgUrlObj = new URL(imgUrl)
if (imgUrlObj.pathname.startsWith(urlPrefixObj.pathname)) {
fileKey = imgUrlObj.pathname.substring(urlPrefixObj.pathname.length).replace(/^\/+/, '')
} else {
fileKey = imgUrlObj.pathname.replace(/^\/+/, '')
}
} else {
const url = new URL(!/^https?:\/\//.test(imgUrl) ? `http://${imgUrl}` : imgUrl)
fileKey = url.pathname.replace(/^\/+/, '')
if (pathStyleAccess) {
fileKey = fileKey.replace(/^[^/]+\//, '')
}
}
const endpointUrl: string | undefined = endpoint
? /^https?:\/\//.test(endpoint)
? endpoint
: `http://${endpoint}`
: undefined
if (endpointUrl && endpointUrl.includes('cloudflarestorage')) {
region = region || 'auto'
}
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
}
if (dogeMode) {
s3Options.credentials = {
accessKeyId: configMap.config.accessKeyID,
secretAccessKey: configMap.config.secretAccessKey,
sessionToken: configMap.config.sessionToken
}
}
let result: any
try {
fileKey = decodeURIComponent(fileKey)
} catch (err: any) {}
try {
const client = new S3Client(s3Options)
const command = new DeleteObjectCommand({
Bucket: bucketName,
Key: fileKey
})
result = await client.send(command)
} catch (err: any) {
s3Options.region = 'us-east-1'
const client = new S3Client(s3Options)
const command = new DeleteObjectCommand({
Bucket: bucketName,
Key: fileKey
})
result = await client.send(command)
}
return result.$metadata.httpStatusCode === 204
}
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 = { ...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) {
logger.error(err)
return false
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 = { ...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)
}
function createHuaweiAuthorization(
@@ -250,35 +240,25 @@ export async function removeFileFromHuaweiInMain(configMap: IStringKeyMap) {
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: any) {
logger.error(error)
return false
}
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
}
export async function removeFileFromSFTPInMain(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.deleteFileSFTP(config, remote)
client.close()
return deleteResult
} catch (err: any) {
logger.error(err)
return false
}
const client = SSHClient.instance
await client.connect(config)
const uploadPath = `/${config.uploadPath || ''}/`.replace(/\/+/g, '/')
const remote = path.join(uploadPath, fileName)
const deleteResult = await client.deleteFileSFTP(config, remote)
client.close()
return deleteResult
}

View File

@@ -1,8 +1,10 @@
import logger from '@core/picgo/logger'
export const deleteLog = (fileName?: string, type?: string, isSuccess = true, msg?: string) => {
console.log(`Delete ${fileName} on ${type} ${isSuccess ? 'success' : 'failed'}, message: ${msg || ''}`)
logger.info(`Delete ${fileName} on ${type} ${isSuccess ? 'successfully' : 'failed'} ${msg ? 'msg: ' + msg : ''}`)
}
export const deleteFailedLog = (fileName: string, type: string, error: any) => {
deleteLog(fileName, type, false)
console.error(error)
logger.error(error)
}