Files
certd/packages/ui/certd-server/src/plugins/plugin-aws/libs/aws-client.ts
T

189 lines
6.6 KiB
TypeScript
Raw Normal View History

2025-12-25 18:56:27 +08:00
// 导入所需的 SDK 模块
2026-05-31 01:41:33 +08:00
import { AwsAccess } from "../access.js";
import { CertInfo, DomainRecord } from "@certd/plugin-cert";
import { ILogger, utils } from "@certd/basic";
import { PageRes, PageSearch } from "@certd/pipeline";
type AwsClientOptions = { access: AwsAccess; region: string; logger: ILogger };
2025-12-25 18:56:27 +08:00
/**
* https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/route-53-domains/
*/
2025-12-25 18:56:27 +08:00
export class AwsClient {
options: AwsClientOptions;
access: AwsAccess;
region: string;
logger: ILogger;
constructor(options: AwsClientOptions) {
this.options = options;
this.access = options.access;
this.region = options.region;
this.logger = options.logger;
}
async importCertificate(certInfo: CertInfo) {
// 创建 ACM 客户端
2026-05-31 01:41:33 +08:00
const { ACMClient, ImportCertificateCommand } = await import("@aws-sdk/client-acm");
2025-12-25 18:56:27 +08:00
const acmClient = new ACMClient({
region: this.region, // 替换为您的 AWS 区域
credentials: {
accessKeyId: this.access.accessKeyId, // 从环境变量中读取
secretAccessKey: this.access.secretAccessKey,
},
});
// Split the full PEM chain: first block is the leaf cert, the rest is the intermediate chain
const pemBlocks = certInfo.crt.split(/(?<=-----END CERTIFICATE-----)/);
const cert = pemBlocks[0].trim();
2026-06-14 20:59:38 +08:00
const chain = pemBlocks.slice(1).join("").trim();
2025-12-25 18:56:27 +08:00
// 构建上传参数
const data = await acmClient.send(
new ImportCertificateCommand({
Certificate: Buffer.from(cert),
PrivateKey: Buffer.from(certInfo.key),
CertificateChain: chain ? Buffer.from(chain) : undefined,
2025-12-25 18:56:27 +08:00
})
);
this.logger.info(`Upload successful: ${data.CertificateArn}`);
2025-12-25 18:56:27 +08:00
// 返回证书 ARNAmazon Resource Name
return data.CertificateArn;
}
2026-02-15 18:44:35 +08:00
async getCallerIdentity() {
2026-05-31 01:41:33 +08:00
const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts");
2026-02-15 18:44:35 +08:00
const client = new STSClient({
2026-05-31 01:41:33 +08:00
region: this.access.region || "us-east-1",
2026-02-15 18:44:35 +08:00
credentials: {
accessKeyId: this.access.accessKeyId, // 从环境变量中读取
secretAccessKey: this.access.secretAccessKey,
},
});
const command = new GetCallerIdentityCommand({});
const response = await client.send(command);
this.logger.info(` 账户ID: ${response.Account}`);
this.logger.info(` ARN: ${response.Arn}`);
this.logger.info(` 用户ID: ${response.UserId}`);
return response;
}
2025-12-25 18:56:27 +08:00
async route53ClientGet() {
2026-05-31 01:41:33 +08:00
const { Route53Client } = await import("@aws-sdk/client-route-53");
2025-12-25 18:56:27 +08:00
return new Route53Client({
region: this.region,
credentials: {
accessKeyId: this.access.accessKeyId, // 从环境变量中读取
secretAccessKey: this.access.secretAccessKey,
},
});
}
2026-05-31 01:41:33 +08:00
async route53GetHostedZoneId(name: string): Promise<{ ZoneId: string; ZoneName: string }> {
2025-12-25 18:56:27 +08:00
const hostedZones = await this.route53ListHostedZones(name);
2026-05-31 01:41:33 +08:00
const zoneId = hostedZones[0].Id.replace("/hostedzone/", "");
2025-12-25 18:56:27 +08:00
this.logger.info(`获取到hostedZoneId:${zoneId},name:${hostedZones[0].Name}`);
return {
ZoneId: zoneId,
ZoneName: hostedZones[0].Name,
};
}
2026-05-31 01:41:33 +08:00
async route53ListHostedZones(name: string): Promise<{ Id: string; Name: string }[]> {
2025-12-26 23:20:14 +08:00
const { ListHostedZonesByNameCommand } = await import("@aws-sdk/client-route-53"); // ES Modules import
2025-12-25 18:56:27 +08:00
const client = await this.route53ClientGet();
2026-05-31 01:41:33 +08:00
const input = {
// ListHostedZonesByNameRequest
2025-12-25 18:56:27 +08:00
DNSName: name,
};
const command = new ListHostedZonesByNameCommand(input);
2025-12-26 23:20:14 +08:00
const response = await this.doRequest(() => client.send(command));
2025-12-25 18:56:27 +08:00
if (response.HostedZones.length === 0) {
throw new Error(`找不到 HostedZone ${name}`);
}
this.logger.info(`获取到hostedZoneId:${JSON.stringify(response.HostedZones)}`);
return response.HostedZones;
}
async route53ListHostedZonesPage(req: PageSearch): Promise<PageRes<DomainRecord>> {
const { ListHostedZonesByNameCommand } = await import("@aws-sdk/client-route-53"); // ES Modules import
const client = await this.route53ClientGet();
2026-05-31 01:41:33 +08:00
const input: any = {
// ListHostedZonesByNameRequest
MaxItems: req.pageSize,
};
if (req.searchKey) {
input.DNSName = req.searchKey;
}
const command = new ListHostedZonesByNameCommand(input);
const response = await this.doRequest(() => client.send(command));
2026-02-15 18:44:35 +08:00
let list: any[] = response.HostedZones || [];
list = list.map((item: any) => ({
2026-05-31 01:41:33 +08:00
id: item.Id.replace("/hostedzone/", ""),
domain: item.Name,
}));
return {
total: list.length,
list,
};
}
2026-05-31 01:41:33 +08:00
async route53ChangeRecord(req: { hostedZoneId: string; fullRecord: string; type: string; value: string; action: "UPSERT" | "DELETE" }) {
2025-12-26 23:20:14 +08:00
const { ChangeResourceRecordSetsCommand } = await import("@aws-sdk/client-route-53"); // ES Modules import
2025-12-25 18:56:27 +08:00
// const { Route53Client, ChangeResourceRecordSetsCommand } = require("@aws-sdk/client-route-53"); // CommonJS import
// import type { Route53ClientConfig } from "@aws-sdk/client-route-53";
const client = await this.route53ClientGet();
2026-05-31 01:41:33 +08:00
const input = {
// ChangeResourceRecordSetsRequest
2025-12-25 18:56:27 +08:00
HostedZoneId: req.hostedZoneId, // required
2026-05-31 01:41:33 +08:00
ChangeBatch: {
// ChangeBatch
Changes: [
// Changes // required
{
// Change
2025-12-26 23:20:14 +08:00
Action: req.action as any, // required
2026-05-31 01:41:33 +08:00
ResourceRecordSet: {
// ResourceRecordSet
2025-12-26 23:20:14 +08:00
Name: req.fullRecord, // required
2025-12-25 18:56:27 +08:00
Type: req.type.toUpperCase() as any,
2026-05-31 01:41:33 +08:00
ResourceRecords: [
// ResourceRecords
{
// ResourceRecord
2025-12-25 18:56:27 +08:00
Value: `"${req.value}"`, // required
},
],
TTL: 60,
2025-12-25 18:56:27 +08:00
},
},
],
},
};
2025-12-26 23:20:14 +08:00
this.logger.info(`设置域名解析参数:${JSON.stringify(input)}`);
2025-12-25 18:56:27 +08:00
const command = new ChangeResourceRecordSetsCommand(input);
2025-12-26 23:20:14 +08:00
const response = await this.doRequest(() => client.send(command));
2026-05-31 01:41:33 +08:00
console.log("Add record successful:", JSON.stringify(response));
2025-12-25 18:56:27 +08:00
await utils.sleep(3000);
return response;
/*
// { // ChangeResourceRecordSetsResponse
// ChangeInfo: { // ChangeInfo
// Id: "STRING_VALUE", // required
// Status: "PENDING" || "INSYNC", // required
// SubmittedAt: new Date("TIMESTAMP"), // required
// Comment: "STRING_VALUE",
// },
// };*/
}
2025-12-26 23:20:14 +08:00
async doRequest<T>(call: () => Promise<T>): Promise<T> {
try {
2025-12-25 18:56:27 +08:00
return await call();
2025-12-26 23:20:14 +08:00
} catch (err) {
2025-12-25 18:56:27 +08:00
this.logger.error(`调用接口失败:${err.Error?.Message || err.message},requestId:${err.requestId}`);
throw err;
}
}
}