perf: 数据库备份支持oss

This commit is contained in:
xiaojunnuo
2025-04-25 01:26:04 +08:00
parent 50a5fa15bb
commit 308d4600ef
24 changed files with 470 additions and 192 deletions
@@ -0,0 +1,54 @@
import { BaseOssClient, OssFileItem } from "../api.js";
import { AliossAccess, AliossClient, AliyunAccess } from "../../aliyun/index.js";
import dayjs from "dayjs";
export default class AliOssClientImpl extends BaseOssClient<AliossAccess> {
client: AliossClient;
join(...strs: string[]) {
const str = super.join(...strs);
if (str.startsWith("/")) {
return str.substring(1);
}
return str;
}
async init() {
const aliyunAccess = await this.ctx.accessService.getById<AliyunAccess>(this.access.accessId);
const client = new AliossClient({
access: aliyunAccess,
bucket: this.access.bucket,
region: this.access.region,
});
await client.init();
this.client = client;
}
async download(filePath: string, savePath: string): Promise<void> {
const key = this.join(this.rootDir, filePath);
await this.client.downloadFile(key, savePath);
}
async listDir(dir: string): Promise<OssFileItem[]> {
const dirKey = this.join(this.rootDir, dir) + "/";
const list = await this.client.listDir(dirKey);
this.logger.info(`列出目录: ${dirKey},文件数:${list.length}`);
return list.map(item => {
return {
path: item.name,
lastModified: dayjs(item.lastModified).valueOf(),
size: item.size,
};
});
}
async upload(filePath: string, fileContent: Buffer) {
const key = this.join(this.rootDir, filePath);
this.logger.info(`开始上传文件: ${key}`);
await this.client.uploadFile(key, fileContent);
this.logger.info(`文件上传成功: ${filePath}`);
}
async remove(filePath: string) {
const key = this.join(this.rootDir, filePath);
// remove file from alioss
await this.client.removeFile(key);
this.logger.info(`文件删除成功: ${key}`);
}
}
@@ -0,0 +1,46 @@
import { BaseOssClient } from "../api.js";
import path from "path";
import os from "os";
import fs from "fs";
import { FtpAccess, FtpClient } from "../../ftp/index.js";
export default class FtpOssClientImpl extends BaseOssClient<FtpAccess> {
async download(fileName: string, savePath: string) {}
async listDir(dir: string) {
return [];
}
async upload(filePath: string, fileContent: Buffer) {
const client = this.getFtpClient();
await client.connect(async client => {
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
const dir = path.dirname(tmpFilePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(tmpFilePath, fileContent);
try {
// Write file to temp path
const path = this.join(this.rootDir, filePath);
await client.upload(path, tmpFilePath);
} finally {
// Remove temp file
fs.unlinkSync(tmpFilePath);
}
});
}
private getFtpClient() {
return new FtpClient({
access: this.access,
logger: this.logger,
});
}
async remove(filePath: string) {
const client = this.getFtpClient();
await client.connect(async client => {
const path = this.join(this.rootDir, filePath);
await client.client.remove(path);
});
}
}
@@ -0,0 +1,33 @@
import { QiniuAccess, QiniuClient, QiniuOssAccess } from "../../qiniu/index.js";
import { BaseOssClient, OssClientRemoveByOpts, OssFileItem } from "../api.js";
export default class QiniuOssClientImpl extends BaseOssClient<QiniuOssAccess> {
client: QiniuClient;
async init() {
const qiniuAccess = await this.ctx.accessService.getById<QiniuAccess>(this.access.accessId);
this.client = new QiniuClient({
access: qiniuAccess,
logger: this.logger,
http: this.ctx.utils.http,
});
}
download(fileName: string, savePath: string): Promise<void> {
throw new Error("Method not implemented.");
}
removeBy(removeByOpts: OssClientRemoveByOpts): Promise<void> {
throw new Error("Method not implemented.");
}
listDir(dir: string): Promise<OssFileItem[]> {
throw new Error("Method not implemented.");
}
async upload(filePath: string, fileContent: Buffer) {
const path = this.join(this.rootDir, filePath);
await this.client.uploadFile(this.access.bucket, path, fileContent);
}
async remove(filePath: string) {
const path = this.join(this.rootDir, filePath);
await this.client.removeFile(this.access.bucket, path);
}
}
@@ -0,0 +1,55 @@
import { BaseOssClient, OssClientRemoveByOpts, OssFileItem } from "../api.js";
import path from "node:path";
import { S3Access } from "../../s3/access.js";
export default class S3OssClientImpl extends BaseOssClient<S3Access> {
client: any;
async init() {
// import { S3Client } from "@aws-sdk/client-s3";
const { S3Client } = await import("@aws-sdk/client-s3");
this.client = new S3Client({
forcePathStyle: true,
credentials: {
accessKeyId: this.access.accessKeyId, // 默认 MinIO 访问密钥
secretAccessKey: this.access.secretAccessKey, // 默认 MinIO 秘密密钥
},
region: "us-east-1",
endpoint: this.access.endpoint,
});
}
download(fileName: string, savePath: string): Promise<void> {
throw new Error("Method not implemented.");
}
removeBy(removeByOpts: OssClientRemoveByOpts): Promise<void> {
throw new Error("Method not implemented.");
}
listDir(dir: string): Promise<OssFileItem[]> {
throw new Error("Method not implemented.");
}
async upload(filePath: string, fileContent: Buffer) {
const { PutObjectCommand } = await import("@aws-sdk/client-s3");
const key = path.join(this.rootDir, filePath);
this.logger.info(`开始上传文件: ${key}`);
const params = {
Bucket: this.access.bucket, // The name of the bucket. For example, 'sample_bucket_101'.
Key: key, // The name of the object. For example, 'sample_upload.txt'.
};
await this.client.send(new PutObjectCommand({ Body: fileContent, ...params }));
this.logger.info(`文件上传成功: ${filePath}`);
}
async remove(filePath: string) {
const key = path.join(this.rootDir, filePath);
const { DeleteObjectCommand } = await import("@aws-sdk/client-s3");
await this.client.send(
new DeleteObjectCommand({
Bucket: this.access.bucket,
Key: key,
})
);
this.logger.info(`文件删除成功: ${key}`);
}
}
@@ -0,0 +1,59 @@
import { BaseOssClient, OssClientRemoveByOpts, OssFileItem } from "../api.js";
import path from "path";
import os from "os";
import fs from "fs";
import { SftpAccess, SshAccess, SshClient } from "../../ssh/index.js";
export default class SftpOssClientImpl extends BaseOssClient<SftpAccess> {
download(fileName: string, savePath: string): Promise<void> {
throw new Error("Method not implemented.");
}
removeBy(removeByOpts: OssClientRemoveByOpts): Promise<void> {
throw new Error("Method not implemented.");
}
listDir(dir: string): Promise<OssFileItem[]> {
throw new Error("Method not implemented.");
}
async upload(filePath: string, fileContent: Buffer) {
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
// Write file to temp path
const dir = path.dirname(tmpFilePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(tmpFilePath, fileContent);
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
const key = this.rootDir + filePath;
try {
const client = new SshClient(this.logger);
await client.uploadFiles({
connectConf: access,
mkdirs: true,
transports: [
{
localPath: tmpFilePath,
remotePath: key,
},
],
opts: {
mode: this.access?.fileMode ?? undefined,
},
});
} finally {
// Remove temp file
fs.unlinkSync(tmpFilePath);
}
}
async remove(filePath: string) {
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
const client = new SshClient(this.logger);
const key = this.rootDir + filePath;
await client.removeFiles({
connectConf: access,
files: [key],
});
}
}
@@ -0,0 +1,54 @@
import { BaseOssClient, OssClientRemoveByOpts, OssFileItem } from "../api.js";
import path from "path";
import os from "os";
import fs from "fs";
import { SshAccess, SshClient } from "../../ssh/index.js";
export default class SshOssClientImpl extends BaseOssClient<SshAccess> {
download(fileName: string, savePath: string): Promise<void> {
throw new Error("Method not implemented.");
}
removeBy(removeByOpts: OssClientRemoveByOpts): Promise<void> {
throw new Error("Method not implemented.");
}
listDir(dir: string): Promise<OssFileItem[]> {
throw new Error("Method not implemented.");
}
async upload(filePath: string, fileContent: Buffer) {
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
// Write file to temp path
const dir = path.dirname(tmpFilePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(tmpFilePath, fileContent);
const key = this.rootDir + filePath;
try {
const client = new SshClient(this.logger);
await client.uploadFiles({
connectConf: this.access,
mkdirs: true,
transports: [
{
localPath: tmpFilePath,
remotePath: key,
},
],
});
} finally {
// Remove temp file
fs.unlinkSync(tmpFilePath);
}
}
async remove(filePath: string) {
const client = new SshClient(this.logger);
const key = this.rootDir + filePath;
await client.removeFiles({
connectConf: this.access,
files: [key],
});
}
}
@@ -0,0 +1,37 @@
import { TencentAccess, TencentCosAccess, TencentCosClient } from "../../tencent/index.js";
import { BaseOssClient, OssClientRemoveByOpts, OssFileItem } from "../api.js";
export default class TencentOssClientImpl extends BaseOssClient<TencentCosAccess> {
download(fileName: string, savePath: string): Promise<void> {
throw new Error("Method not implemented.");
}
removeBy(removeByOpts: OssClientRemoveByOpts): Promise<void> {
throw new Error("Method not implemented.");
}
listDir(dir: string): Promise<OssFileItem[]> {
throw new Error("Method not implemented.");
}
async upload(filePath: string, fileContent: Buffer) {
const access = await this.ctx.accessService.getById<TencentAccess>(this.access.accessId);
const client = new TencentCosClient({
access: access,
logger: this.logger,
region: this.access.region,
bucket: this.access.bucket,
});
const key = this.rootDir + filePath;
await client.uploadFile(key, fileContent);
}
async remove(filePath: string) {
const access = await this.ctx.accessService.getById<TencentAccess>(this.access.accessId);
const client = new TencentCosClient({
access: access,
logger: this.logger,
region: this.access.region,
bucket: this.access.bucket,
});
const key = this.rootDir + filePath;
await client.removeFile(key);
}
}