feat: backup support s3

Signed-off-by: Innei <i@innei.in>
This commit is contained in:
Innei
2024-04-20 14:17:20 +08:00
parent bbfc5574c5
commit 9dc3fbd15a
8 changed files with 1170 additions and 27 deletions

View File

@@ -49,6 +49,7 @@
},
"dependencies": {
"@algolia/client-search": "^4.22.1",
"@aws-sdk/client-s3": "3.556.0",
"@babel/core": "7.24.4",
"@babel/plugin-transform-modules-commonjs": "7.24.1",
"@babel/plugin-transform-typescript": "7.24.4",

View File

@@ -161,7 +161,7 @@ export const DEBUG_MODE = {
const ENCRYPT_KEY = argv.encrypt_key || MX_ENCRYPT_KEY
export const ENCRYPT = {
key: ENCRYPT_KEY || machineIdSync(),
enable: parseBooleanishValue(argv.encrypt_enable) ? !!ENCRYPT_KEY : false,
enable: parseBooleanishValue(argv.encrypt_enable) ?? !!ENCRYPT_KEY,
algorithm: argv.encrypt_algorithm || 'aes-256-ecb',
}

View File

@@ -7,6 +7,7 @@ import v4_6_0__1 from './version/v4.6.0-1'
import v4_6_1 from './version/v4.6.2'
import v5_0_0__1 from './version/v5.0.0-1'
import v5_1_1 from './version/v5.1.1'
import v5_6_0 from './version/v5.6.0'
export default [
v200Alpha1,
@@ -18,4 +19,5 @@ export default [
v4_6_1,
v5_0_0__1,
v5_1_1,
v5_6_0,
]

View File

@@ -0,0 +1,30 @@
// patch for version lower than v2.0.0-alpha.1
import type { Db } from 'mongodb'
export default (async function v0560lpha1(db: Db) {
const backupOptions = await db.collection('options').findOne({
name: 'backupOptions',
})
if (!backupOptions) {
return
}
if (!backupOptions.value) {
return
}
if (backupOptions.value.endpoint) {
return
}
const region = backupOptions.value.region
backupOptions.value.endpoint = `https://cos.${region}.myqcloud.com`
backupOptions.value.region = 'auto'
await db
.collection('options')
.updateOne(
{ name: 'backupOptions' },
{ $set: { value: backupOptions.value } },
)
})

View File

@@ -4,6 +4,7 @@ import { join, resolve } from 'path'
import { flatten } from 'lodash'
import { mkdirp } from 'mkdirp'
import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3'
import {
BadRequestException,
Injectable,
@@ -26,7 +27,6 @@ import { migrateDatabase } from '~/migration/migrate'
import { EventManagerService } from '~/processors/helper/helper.event.service'
import { CacheService } from '~/processors/redis/cache.service'
import { getMediumDateTime, scheduleManager } from '~/utils'
import { uploadFileToCOS } from '~/utils/cos.util'
import { getFolderSize, installPKG } from '~/utils/system.util'
import { ConfigsService } from '../configs/configs.service'
@@ -290,38 +290,39 @@ export class BackupService {
this.logger.log('没有开启备份')
return
}
// 开始上传 COS
scheduleManager.schedule(async () => {
const { backupOptions } = await this.configs.waitForConfigReady()
if (
!backupOptions.bucket ||
!backupOptions.region ||
!backupOptions.secretId ||
!backupOptions.secretKey
) {
const { endpoint, bucket, region, secretId, secretKey } =
backupOptions || {}
if (!endpoint || !bucket || !region || !secretId || !secretKey) {
return
}
this.logger.log('--> 开始上传到 COS')
await uploadFileToCOS(
backup.buffer,
backup.path.slice(backup.path.lastIndexOf('/') + 1),
{
bucket: backupOptions.bucket,
region: backupOptions.region,
secretId: backupOptions.secretId,
secretKey: backupOptions.secretKey,
const s3 = new S3Client({
region: 'auto',
endpoint,
credentials: {
accessKeyId: secretId,
secretAccessKey: secretKey,
},
)
.then(() => {
this.logger.log('--> 上传成功')
})
.catch((err) => {
this.logger.error('--> 上传失败了')
throw err
})
})
const remoteFileKey = backup.path.slice(backup.path.lastIndexOf('/') + 1)
const command = new PutObjectCommand({
Bucket: bucket,
Key: remoteFileKey,
Body: backup.buffer,
ContentType: 'application/zip',
})
this.logger.log('--> 开始上传到 S3')
await s3.send(command).catch((err) => {
this.logger.error('--> 上传失败了')
throw err
})
this.logger.log('--> 上传成功')
})
}
}

View File

@@ -43,6 +43,7 @@ export const generateDefaultConfig: () => IConfig = () => ({
friendLinkOptions: { allowApply: true },
backupOptions: {
enable: DEMO_MODE ? false : true,
endpoint: null!,
region: null!,
bucket: null!,
secretId: null!,

View File

@@ -159,6 +159,11 @@ export class BackupOptionsDto {
})
enable: boolean
@IsString()
@IsOptional()
@JSONSchemaPlainField('S3 服务端点')
endpoint?: string
@IsString()
@IsOptional()
@JSONSchemaHalfGirdPlainField('SecretId')

1103
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff