mirror of
https://github.com/certd/certd.git
synced 2026-04-24 04:17:25 +08:00
perf: dns支持火山引擎
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import {AccessInput, BaseAccess, IsAccess} from '@certd/pipeline';
|
||||
|
||||
/**
|
||||
* 这个注解将注册一个授权配置
|
||||
* 在certd的后台管理系统中,用户可以选择添加此类型的授权
|
||||
*/
|
||||
@IsAccess({
|
||||
name: 'volcengine',
|
||||
title: '火山引擎',
|
||||
desc: '',
|
||||
icon: 'svg:icon-volcengine',
|
||||
})
|
||||
export class VolcengineAccess extends BaseAccess {
|
||||
|
||||
@AccessInput({
|
||||
title: 'AccessKeyID',
|
||||
component: {
|
||||
placeholder: 'AccessKeyID',
|
||||
},
|
||||
helper:"[获取密钥](https://console.volcengine.com/iam/keymanage/)",
|
||||
required: true,
|
||||
})
|
||||
accessKeyId = '';
|
||||
@AccessInput({
|
||||
title: 'SecretAccessKey',
|
||||
component: {
|
||||
placeholder: 'SecretAccessKey',
|
||||
},
|
||||
required: true,
|
||||
encrypt: true,
|
||||
})
|
||||
secretAccessKey = '';
|
||||
|
||||
}
|
||||
|
||||
new VolcengineAccess();
|
||||
@@ -0,0 +1,182 @@
|
||||
import { VolcengineAccess } from "./access.js";
|
||||
import { http, HttpClient, ILogger } from "@certd/basic";
|
||||
import querystring from "querystring";
|
||||
|
||||
export type VolcengineOpts = {
|
||||
access: VolcengineAccess
|
||||
logger: ILogger
|
||||
http: HttpClient
|
||||
}
|
||||
|
||||
export type VolcengineReq = {
|
||||
method?: string;
|
||||
path?: string;
|
||||
headers?: any;
|
||||
body?: any;
|
||||
query?: any;
|
||||
service?: string, // 替换为实际服务名称
|
||||
region?: string, // 替换为实际区域名称
|
||||
}
|
||||
|
||||
export class VolcengineClient {
|
||||
opts: VolcengineOpts;
|
||||
|
||||
constructor(opts: VolcengineOpts) {
|
||||
this.opts = opts;
|
||||
}
|
||||
|
||||
// // 生成签名函数
|
||||
// async createSignedRequest(req: VolcengineReq) {
|
||||
// if (!req.body) {
|
||||
// req.body = {};
|
||||
// }
|
||||
// const bodyStr = JSON.stringify(req.body);
|
||||
// const { method, path, body, query } = req;
|
||||
// const crypto = await import("crypto");
|
||||
// const config = {
|
||||
// accessKeyId: this.opts.access.accessKeyId,
|
||||
// secretKey: this.opts.access.secretAccessKey,
|
||||
// service: req.service || "dns", // 默认服务名称为 dns
|
||||
// region: req.region || "cn-beijing", // 默认区域名称为 cn-beijing
|
||||
// endpoint: "https://open.volcengineapi.com"
|
||||
// };
|
||||
//
|
||||
// // 1. 生成时间戳
|
||||
// const now = new Date();
|
||||
// // 20201103T104027Z
|
||||
// const timestamp = now.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
||||
//
|
||||
// // 2. 处理查询参数
|
||||
// const sortedQuery = Object.keys(query || {})
|
||||
// .sort()
|
||||
// .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(query[k])}`)
|
||||
// .join("&");
|
||||
//
|
||||
// // 3. 构造规范请求
|
||||
// const canonicalRequest = [
|
||||
// method.toUpperCase(),
|
||||
// path || "/",
|
||||
// sortedQuery,
|
||||
// `content-type:application/json\nhost:${new URL(config.endpoint).host}`,
|
||||
// "content-type;host",
|
||||
// crypto.createHash("sha256").update(bodyStr).digest("hex")
|
||||
// ].join("\n");
|
||||
//
|
||||
// // 4. 生成签名字符串
|
||||
// const date = now.toISOString().substring(0, 10).replace(/-/g, "");
|
||||
// const credentialScope = `${date}/${config.region}/${config.service}/request`;
|
||||
//
|
||||
// const stringToSign = [
|
||||
// "HMAC-SHA256",
|
||||
// timestamp,
|
||||
// credentialScope,
|
||||
// crypto.createHash("sha256").update(canonicalRequest).digest("hex")
|
||||
// ].join("\n");
|
||||
//
|
||||
// // 5. 计算签名
|
||||
// const sign = (key: Buffer, msg: string) => crypto.createHmac("sha256", key).update(msg).digest();
|
||||
//
|
||||
// const kDate = sign(Buffer.from(`HMAC${config.secretKey}`, "utf8"), date);
|
||||
// const kRegion = sign(kDate, config.region);
|
||||
// const kService = sign(kRegion, config.service);
|
||||
// const kSigning = sign(kService, "request");
|
||||
// const signature = crypto.createHmac("sha256", kSigning)
|
||||
// .update(stringToSign)
|
||||
// .digest("hex");
|
||||
//
|
||||
// // 6. 构造请求头
|
||||
// const headers = {
|
||||
// "Content-Type": "application/json",
|
||||
// Host: new URL(config.endpoint).host,
|
||||
// "X-Date": timestamp,
|
||||
// Authorization: `HMAC-SHA256 Credential=${config.accessKeyId}/${credentialScope}, SignedHeaders=content-type;host, Signature=${signature}`
|
||||
// };
|
||||
//
|
||||
// return {
|
||||
// method,
|
||||
// url: `${config.endpoint}${path || ""}${sortedQuery ? `?${sortedQuery}` : ""}`,
|
||||
// headers,
|
||||
// data: body
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// async doRequest(req: VolcengineReq) {
|
||||
// const requestConfig = await this.createSignedRequest(req);
|
||||
// try {
|
||||
// const res = await this.opts.http.request(requestConfig);
|
||||
// if (res?.ResponseMetadata?.Error) {
|
||||
// throw new Error(JSON.stringify(res.ResponseMetadata.Error));
|
||||
// }
|
||||
// return res;
|
||||
// } catch (e) {
|
||||
// if (e?.response?.ResponseMetadata.Error) {
|
||||
// throw new Error(JSON.stringify(e.response.ResponseMetadata.Error));
|
||||
// }
|
||||
// throw e;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
async doRequest(req: VolcengineReq) {
|
||||
const {Signer} =await import('@volcengine/openapi') ;
|
||||
|
||||
// http request data
|
||||
const openApiRequestData: any = {
|
||||
region: req.region,
|
||||
method: req.method,
|
||||
// [optional] http request url query
|
||||
params: {
|
||||
...req.query,
|
||||
},
|
||||
// http request headers
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
// [optional] http request body
|
||||
body: req.body,
|
||||
}
|
||||
|
||||
const signer = new Signer(openApiRequestData, req.service);
|
||||
|
||||
// sign
|
||||
signer.addAuthorization({accessKeyId:this.opts.access.accessKeyId, secretKey:this.opts.access.secretAccessKey});
|
||||
|
||||
// Print signed headers
|
||||
console.log(openApiRequestData.headers);
|
||||
|
||||
|
||||
const url = `https://open.volcengineapi.com/?${querystring.stringify(req.query)}`
|
||||
const res = await http.request({
|
||||
url: url,
|
||||
method: req.method,
|
||||
headers: openApiRequestData.headers,
|
||||
data:req.body
|
||||
});
|
||||
|
||||
if (res?.ResponseMetadata?.Error) {
|
||||
throw new Error(JSON.stringify(res.ResponseMetadata.Error));
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
|
||||
// 列出域名解析记录
|
||||
async findDomain(domain: string) {
|
||||
const req: VolcengineReq = {
|
||||
method: "POST",
|
||||
region: "cn-beijing",
|
||||
service: "dns",
|
||||
query: {
|
||||
Action: "ListZones",
|
||||
Version: "2018-08-01",
|
||||
},
|
||||
body:{
|
||||
Key: domain,
|
||||
SearchMode: "exact"
|
||||
}
|
||||
};
|
||||
|
||||
return this.doRequest(req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './plugins/index.js';
|
||||
export * from './access.js';
|
||||
export * from './volcengine-dns-provider.js';
|
||||
@@ -0,0 +1,93 @@
|
||||
import { AbstractDnsProvider, CreateRecordOptions, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert";
|
||||
import { Autowire } from "@certd/pipeline";
|
||||
|
||||
import { VolcengineClient } from "./client.js";
|
||||
import { VolcengineAccess } from "./access.js";
|
||||
|
||||
@IsDnsProvider({
|
||||
name: "volcengine",
|
||||
title: "火山引擎",
|
||||
desc: "火山引擎DNS解析提供商",
|
||||
accessType: "volcengine",
|
||||
icon: "svg:icon-volcengine"
|
||||
})
|
||||
export class VolcengineDnsProvider extends AbstractDnsProvider {
|
||||
client: VolcengineClient;
|
||||
@Autowire()
|
||||
access!: VolcengineAccess;
|
||||
|
||||
|
||||
async onInstance() {
|
||||
this.client = new VolcengineClient({
|
||||
access: this.access,
|
||||
logger: this.logger,
|
||||
http: this.http
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param domain
|
||||
*/
|
||||
async getDomain(domain: string) {
|
||||
const res = await this.client.findDomain(domain)
|
||||
|
||||
if (res.Result.Zones && res.Result.Zones.length > 0) {
|
||||
return res.Result.Zones[0];
|
||||
}
|
||||
throw new Error(`域名${domain}不存在`);
|
||||
}
|
||||
|
||||
|
||||
async createRecord(options: CreateRecordOptions): Promise<any> {
|
||||
const { fullRecord, hostRecord, value, type, domain } = options;
|
||||
this.logger.info("添加域名解析:", fullRecord, value, domain);
|
||||
|
||||
const domainInfo = await this.getDomain(domain);
|
||||
const ZID = domainInfo.ZID;
|
||||
|
||||
const body = {
|
||||
ZID,
|
||||
"Host": hostRecord,
|
||||
"Type": type,
|
||||
"Value": value
|
||||
};
|
||||
|
||||
const res = await this.client.doRequest({
|
||||
method: "POST",
|
||||
service: "dns",
|
||||
region: "cn-beijing",
|
||||
query: {
|
||||
Action: "CreateRecord",
|
||||
Version: "2018-08-01"
|
||||
},
|
||||
body
|
||||
});
|
||||
|
||||
return {
|
||||
RecordID: res.Result.RecordID,
|
||||
ZID: ZID
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
async removeRecord(options: RemoveRecordOptions<any>): Promise<any> {
|
||||
const record = options.recordRes;
|
||||
|
||||
const body = {
|
||||
RecordID: record.RecordID
|
||||
};
|
||||
|
||||
await this.client.doRequest({
|
||||
method: "POST",
|
||||
service: "dns",
|
||||
region: "cn-beijing",
|
||||
query: {
|
||||
Action: "DeleteRecord",
|
||||
Version: "2018-08-01"
|
||||
},
|
||||
body
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
new VolcengineDnsProvider();
|
||||
Reference in New Issue
Block a user