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

View File

@@ -6,8 +6,8 @@ import { Challenge } from "@certd/acme-client/types/rfc8555";
import { IContext } from "@certd/pipeline";
import { ILogger, utils } from "@certd/basic";
import { IDnsProvider, IDomainParser } from "../../dns-provider/index.js";
import { HttpChallengeUploader } from "./uploads/api.js";
import punycode from "node:punycode";
import { IOssClient } from "@certd/plugin-lib";
export type CnameVerifyPlan = {
type?: string;
domain: string;
@@ -18,7 +18,7 @@ export type CnameVerifyPlan = {
export type HttpVerifyPlan = {
type: string;
domain: string;
httpUploader: HttpChallengeUploader;
httpUploader: IOssClient;
};
export type DomainVerifyPlan = {
@@ -35,7 +35,7 @@ export type DomainsVerifyPlan = {
export type Providers = {
dnsProvider?: IDnsProvider;
domainsVerifyPlan?: DomainsVerifyPlan;
httpUploader?: HttpChallengeUploader;
httpUploader?: IOssClient;
};
export type CertInfo = {
@@ -184,7 +184,7 @@ export class AcmeService {
return authz.challenges.find((c: any) => c.type === type);
};
const doHttpVerify = async (challenge: any, httpUploader: HttpChallengeUploader) => {
const doHttpVerify = async (challenge: any, httpUploader: IOssClient) => {
const keyAuthorization = await keyAuthorizationGetter(challenge);
this.logger.info("http校验");
const filePath = `.well-known/acme-challenge/${challenge.token}`;
@@ -287,7 +287,7 @@ export class AcmeService {
* @returns {Promise}
*/
async challengeRemoveFn(authz: any, challenge: any, keyAuthorization: string, recordReq: any, recordRes: any, dnsProvider?: IDnsProvider, httpUploader?: HttpChallengeUploader) {
async challengeRemoveFn(authz: any, challenge: any, keyAuthorization: string, recordReq: any, recordRes: any, dnsProvider?: IDnsProvider, httpUploader?: IOssClient) {
this.logger.info("执行清理");
/* http-01 */

View File

@@ -9,8 +9,8 @@ import { CertReader } from "./cert-reader.js";
import { CertApplyBasePlugin } from "./base.js";
import { GoogleClient } from "../../libs/google.js";
import { EabAccess } from "../../access";
import { httpChallengeUploaderFactory } from "./uploads/factory.js";
import { DomainParser } from "../../dns-provider/domain-parser.js";
import { ossClientFactory } from "@certd/plugin-lib";
export * from "./base.js";
export type { CertInfo };
export * from "./cert-reader.js";
@@ -115,6 +115,7 @@ HTTP文件验证不支持泛域名需要配置网站文件上传`,
})
dnsProviderType!: string;
// dns解析授权类型,勿删
dnsProviderAccessType!: string;
@TaskInput({
@@ -446,7 +447,7 @@ HTTP文件验证不支持泛域名需要配置网站文件上传`,
rootDir = rootDir + "/";
}
this.logger.info("上传方式", httpRecord.httpUploaderType);
const httpUploader = await httpChallengeUploaderFactory.createUploaderByType(httpRecord.httpUploaderType, {
const httpUploader = await ossClientFactory.createOssClientByType(httpRecord.httpUploaderType, {
access,
rootDir: rootDir,
ctx: httpUploaderContext,

View File

@@ -1,35 +0,0 @@
import { IAccessService } from "@certd/pipeline";
import { ILogger, utils } from "@certd/basic";
export type HttpChallengeUploader = {
upload: (fileName: string, fileContent: Buffer) => Promise<void>;
remove: (fileName: string) => Promise<void>;
};
export type HttpChallengeUploadContext = {
accessService: IAccessService;
logger: ILogger;
utils: typeof utils;
};
export abstract class BaseHttpChallengeUploader<A> implements HttpChallengeUploader {
rootDir: string;
access: A = null;
logger: ILogger;
utils: typeof utils;
ctx: HttpChallengeUploadContext;
protected constructor(opts: { rootDir: string; access: A }) {
this.rootDir = opts.rootDir;
this.access = opts.access;
}
async setCtx(ctx: any) {
// set context
this.ctx = ctx;
this.logger = ctx.logger;
this.utils = ctx.utils;
}
abstract remove(fileName: string): Promise<void>;
abstract upload(fileName: string, fileContent: Buffer): Promise<void>;
}

View File

@@ -1,38 +0,0 @@
import { HttpChallengeUploadContext } from "./api";
export class HttpChallengeUploaderFactory {
async getClassByType(type: string) {
if (type === "alioss") {
const module = await import("./impls/alioss.js");
return module.AliossHttpChallengeUploader;
} else if (type === "ssh") {
const module = await import("./impls/ssh.js");
return module.SshHttpChallengeUploader;
} else if (type === "sftp") {
const module = await import("./impls/sftp.js");
return module.SftpHttpChallengeUploader;
} else if (type === "ftp") {
const module = await import("./impls/ftp.js");
return module.FtpHttpChallengeUploader;
} else if (type === "tencentcos") {
const module = await import("./impls/tencentcos.js");
return module.TencentCosHttpChallengeUploader;
} else if (type === "qiniuoss") {
const module = await import("./impls/qiniuoss.js");
return module.QiniuOssHttpChallengeUploader;
} else {
throw new Error(`暂不支持此文件上传方式: ${type}`);
}
}
async createUploaderByType(type: string, opts: { rootDir: string; access: any; ctx: HttpChallengeUploadContext }) {
const cls = await this.getClassByType(type);
if (cls) {
// @ts-ignore
const instance = new cls(opts);
await instance.setCtx(opts.ctx);
return instance;
}
}
}
export const httpChallengeUploaderFactory = new HttpChallengeUploaderFactory();

View File

@@ -1,39 +0,0 @@
import { BaseHttpChallengeUploader } from "../api.js";
import { AliossAccess, AliyunAccess } from "@certd/plugin-lib";
import { AliossClient } from "@certd/plugin-lib";
export class AliossHttpChallengeUploader extends BaseHttpChallengeUploader<AliossAccess> {
async upload(filePath: string, fileContent: Buffer) {
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,
});
const key = this.rootDir + filePath;
this.logger.info(`开始上传文件: ${key}`);
await client.uploadFile(key, fileContent);
this.logger.info(`校验文件上传成功: ${filePath}`);
}
async remove(filePath: string) {
const key = this.rootDir + filePath;
// remove file from alioss
const client = await this.getAliossClient();
await client.removeFile(key);
this.logger.info(`文件删除成功: ${key}`);
}
private async getAliossClient() {
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();
return client;
}
}

View File

@@ -1,41 +0,0 @@
import { BaseHttpChallengeUploader } from "../api.js";
import { FtpAccess, FtpClient } from "@certd/plugin-lib";
import path from "path";
import os from "os";
import fs from "fs";
export class FtpHttpChallengeUploader extends BaseHttpChallengeUploader<FtpAccess> {
async upload(filePath: string, fileContent: Buffer) {
const client = new FtpClient({
access: this.access,
logger: this.logger,
});
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.rootDir + filePath;
await client.upload(path, tmpFilePath);
} finally {
// Remove temp file
fs.unlinkSync(tmpFilePath);
}
});
}
async remove(filePath: string) {
const client = new FtpClient({
access: this.access,
logger: this.logger,
});
await client.connect(async (client) => {
const path = this.rootDir + filePath;
await client.client.remove(path);
});
}
}

View File

@@ -1,31 +0,0 @@
import { BaseHttpChallengeUploader } from "../api.js";
import { QiniuOssAccess, QiniuClient, QiniuAccess } from "@certd/plugin-lib";
export class QiniuOssHttpChallengeUploader extends BaseHttpChallengeUploader<QiniuOssAccess> {
async upload(filePath: string, fileContent: Buffer) {
const qiniuAccess = await this.ctx.accessService.getById<QiniuAccess>(this.access.accessId);
const client = new QiniuClient({
access: qiniuAccess,
logger: this.logger,
http: this.ctx.utils.http,
});
if (this.rootDir.endsWith("/")) {
this.rootDir = this.rootDir.slice(0, -1);
}
await client.uploadFile(this.access.bucket, this.rootDir + filePath, fileContent);
}
async remove(filePath: string) {
const qiniuAccess = await this.ctx.accessService.getById<QiniuAccess>(this.access.accessId);
const client = new QiniuClient({
access: qiniuAccess,
logger: this.logger,
http: this.ctx.utils.http,
});
if (this.rootDir.endsWith("/")) {
this.rootDir = this.rootDir.slice(0, -1);
}
await client.removeFile(this.access.bucket, this.rootDir + filePath);
}
}

View File

@@ -1,51 +0,0 @@
import { BaseHttpChallengeUploader } from "../api.js";
import { SshAccess, SshClient } from "@certd/plugin-lib";
import path from "path";
import os from "os";
import fs from "fs";
import { SftpAccess } from "@certd/plugin-lib";
export class SftpHttpChallengeUploader extends BaseHttpChallengeUploader<SftpAccess> {
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],
});
}
}

View File

@@ -1,45 +0,0 @@
import { BaseHttpChallengeUploader } from "../api.js";
import { SshAccess, SshClient } from "@certd/plugin-lib";
import path from "path";
import os from "os";
import fs from "fs";
export class SshHttpChallengeUploader extends BaseHttpChallengeUploader<SshAccess> {
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],
});
}
}

View File

@@ -1,28 +0,0 @@
import { BaseHttpChallengeUploader } from "../api.js";
import { TencentAccess, TencentCosAccess, TencentCosClient } from "@certd/plugin-lib";
export class TencentCosHttpChallengeUploader extends BaseHttpChallengeUploader<TencentCosAccess> {
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);
}
}