diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.test.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.test.ts
new file mode 100644
index 000000000..1f5086fc4
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.test.ts
@@ -0,0 +1,111 @@
+///
+
+import assert from "node:assert/strict";
+import { DeployCertToTencentCLB } from "./index.js";
+
+describe("DeployCertToTencentCLB", () => {
+ it("uses remote single-select inputs for CLB and HTTPS listener", () => {
+ const input = (DeployCertToTencentCLB as any).define.input;
+
+ assert.equal(input.loadBalancerId.component.name, "remote-select");
+ assert.equal(input.loadBalancerId.component.single, true);
+ assert.equal(input.loadBalancerId.component.action, "onGetCLBList");
+ assert.equal(input.loadBalancerId.required, true);
+
+ assert.equal(input.listenerId.component.name, "remote-select");
+ assert.equal(input.listenerId.component.single, true);
+ assert.equal(input.listenerId.component.action, "onGetListenerList");
+ assert.deepEqual(input.listenerId.component.watches, ["certDomains", "accessId", "region", "loadBalancerId"]);
+ assert.equal(input.listenerId.required, true);
+
+ assert.equal(input.domain.component.name, "remote-select");
+ assert.equal(input.domain.component.single, false);
+ assert.equal(input.domain.component.action, "onGetDomainList");
+ assert.deepEqual(input.domain.component.watches, ["certDomains", "accessId", "region", "loadBalancerId", "listenerId"]);
+ assert.equal(input.domain.required, false);
+ });
+
+ it("maps CLB API results to remote-select options", async () => {
+ const plugin = new DeployCertToTencentCLB();
+ plugin.accessId = "access-1";
+ plugin.logger = { info: () => undefined } as any;
+ (plugin as any).getClient = async () => ({});
+ (plugin as any).getCLBList = async () => [
+ {
+ LoadBalancerId: "lb-1",
+ LoadBalancerName: "业务负载均衡",
+ },
+ ];
+
+ const options = await plugin.onGetCLBList({});
+
+ assert.deepEqual(options, [
+ {
+ value: "lb-1",
+ label: "业务负载均衡",
+ },
+ ]);
+ });
+
+ it("maps HTTPS listener API results after selecting a CLB", async () => {
+ const plugin = new DeployCertToTencentCLB();
+ plugin.accessId = "access-1";
+ plugin.loadBalancerId = "lb-1";
+ plugin.logger = { info: () => undefined } as any;
+ (plugin as any).getClient = async () => ({});
+ (plugin as any).getListenerList = async (_client: any, loadBalancerId: string, listenerIds: any) => {
+ assert.equal(loadBalancerId, "lb-1");
+ assert.equal(listenerIds, null);
+ return [
+ {
+ ListenerId: "listener-1",
+ ListenerName: "HTTPS监听器",
+ Port: 443,
+ },
+ ];
+ };
+
+ const options = await plugin.onGetListenerList({});
+
+ assert.deepEqual(options, [
+ {
+ value: "listener-1",
+ label: "HTTPS监听器:443",
+ },
+ ]);
+ });
+
+ it("maps SNI domains from the selected listener rules", async () => {
+ const plugin = new DeployCertToTencentCLB();
+ plugin.accessId = "access-1";
+ plugin.loadBalancerId = "lb-1";
+ plugin.listenerId = "listener-1";
+ plugin.logger = { info: () => undefined } as any;
+ (plugin as any).getClient = async () => ({});
+ (plugin as any).getListenerList = async (_client: any, loadBalancerId: string, listenerIds: string[]) => {
+ assert.equal(loadBalancerId, "lb-1");
+ assert.deepEqual(listenerIds, ["listener-1"]);
+ return [
+ {
+ ListenerId: "listener-1",
+ Rules: [{ Domain: "www.example.com" }, { Domain: "api.example.com" }],
+ },
+ ];
+ };
+
+ const options = await plugin.onGetDomainList({});
+
+ assert.deepEqual(options, [
+ {
+ value: "www.example.com",
+ label: "www.example.com",
+ domain: "www.example.com",
+ },
+ {
+ value: "api.example.com",
+ label: "api.example.com",
+ domain: "api.example.com",
+ },
+ ]);
+ });
+});
diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts
index 5a49d739d..4737e69b9 100644
--- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts
+++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts
@@ -1,7 +1,8 @@
-import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
+import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import dayjs from "dayjs";
import { TencentAccess } from "../../../plugin-lib/tencent/index.js";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
+import { createRemoteSelectInputDefine } from "@certd/plugin-lib";
@IsTaskPlugin({
name: "DeployCertToTencentCLB",
title: "腾讯云-部署到CLB",
@@ -73,29 +74,44 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin {
})
region!: string;
- @TaskInput({
- title: "负载均衡ID",
- required: true,
- })
+ @TaskInput(
+ createRemoteSelectInputDefine({
+ title: "负载均衡ID",
+ helper: "请选择要部署证书的负载均衡",
+ action: DeployCertToTencentCLB.prototype.onGetCLBList.name,
+ watches: ["region"],
+ single: true,
+ pager: false,
+ search: false,
+ })
+ )
loadBalancerId!: string;
- @TaskInput({
- title: "监听器ID",
- required: true,
- })
+ @TaskInput(
+ createRemoteSelectInputDefine({
+ title: "监听器ID",
+ helper: "请选择要部署证书的HTTPS监听器",
+ action: DeployCertToTencentCLB.prototype.onGetListenerList.name,
+ watches: ["region", "loadBalancerId"],
+ single: true,
+ pager: false,
+ search: false,
+ })
+ )
listenerId!: string;
- @TaskInput({
- title: "域名",
- required: false,
- component: {
- name: "a-select",
- vModel: "value",
- open: false,
- mode: "tags",
- },
- helper: "如果开启了sni,则此项必须填写,未开启,则不要填写",
- })
+ @TaskInput(
+ createRemoteSelectInputDefine({
+ title: "域名",
+ helper: "如果开启了SNI,请选择要部署证书的域名;未开启SNI时可以留空",
+ action: DeployCertToTencentCLB.prototype.onGetDomainList.name,
+ watches: ["region", "loadBalancerId", "listenerId"],
+ required: false,
+ single: false,
+ pager: false,
+ search: false,
+ })
+ )
domain!: string | string[];
@TaskInput({
@@ -272,6 +288,27 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin {
return ret.LoadBalancerSet;
}
+ async onGetCLBList(data: PageSearch) {
+ if (!this.accessId) {
+ throw new Error("请选择Access提供者");
+ }
+
+ const client = await this.getClient();
+ const list = await this.getCLBList(client);
+ if (!list || list.length === 0) {
+ return [];
+ }
+
+ return list.map((item: any) => {
+ const loadBalancerId = item.LoadBalancerId;
+ const loadBalancerName = item.LoadBalancerName || loadBalancerId;
+ return {
+ value: loadBalancerId,
+ label: `${loadBalancerName}<${loadBalancerId}>`,
+ };
+ });
+ }
+
async getListenerList(client: any, balancerId: any, listenerIds: any) {
// HTTPS
const params = {
@@ -284,6 +321,57 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin {
return ret.Listeners;
}
+ async onGetListenerList(data: PageSearch) {
+ if (!this.accessId) {
+ throw new Error("请选择Access提供者");
+ }
+ if (!this.loadBalancerId) {
+ throw new Error("请先选择负载均衡");
+ }
+
+ const client = await this.getClient();
+ const list = await this.getListenerList(client, this.loadBalancerId, null);
+ if (!list || list.length === 0) {
+ return [];
+ }
+
+ return list.map((item: any) => {
+ const listenerId = item.ListenerId;
+ const listenerName = item.ListenerName || "HTTPS监听器";
+ const port = item.Port ? `:${item.Port}` : "";
+ return {
+ value: listenerId,
+ label: `${listenerName}${port}<${listenerId}>`,
+ };
+ });
+ }
+
+ async onGetDomainList(data: PageSearch) {
+ if (!this.accessId) {
+ throw new Error("请选择Access提供者");
+ }
+ if (!this.loadBalancerId) {
+ throw new Error("请先选择负载均衡");
+ }
+ if (!this.listenerId) {
+ throw new Error("请先选择监听器");
+ }
+
+ const client = await this.getClient();
+ const listeners = await this.getListenerList(client, this.loadBalancerId, [this.listenerId]);
+ const listener = listeners?.[0];
+ const domains = listener?.Rules?.map((rule: any) => rule.Domain).filter(Boolean) || [];
+ const uniqueDomains = [...new Set(domains)];
+
+ return uniqueDomains.map(domain => {
+ return {
+ value: domain,
+ label: domain,
+ domain,
+ };
+ });
+ }
+
checkRet(ret: any) {
if (!ret || ret.Error) {
throw new Error("执行失败:" + ret.Error.Code + "," + ret.Error.Message);