diff --git a/packages/ui/certd-client/src/components/plugins/common/remote-select.vue b/packages/ui/certd-client/src/components/plugins/common/remote-select.vue
index 55c0bcc11..e951b4d54 100644
--- a/packages/ui/certd-client/src/components/plugins/common/remote-select.vue
+++ b/packages/ui/certd-client/src/components/plugins/common/remote-select.vue
@@ -145,12 +145,18 @@ const getOptions = async () => {
showErrorNotify: false,
}
);
- const list = res?.list || res || [];
+ let list = res?.list || res || [];
if (list.length > 0) {
message.value = "获取数据成功,请从下拉框中选择";
} else {
message.value = "获取数据成功,没有数据";
}
+ list = list.map((item: any) => {
+ return {
+ ...item,
+ title: `${item.domain || item.value}`,
+ };
+ });
optionsRef.value = list;
pagerRef.value.total = list.length;
if (props.pager) {
diff --git a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/history-timeline-item.vue b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/history-timeline-item.vue
index 12546c7df..b8555ccf1 100644
--- a/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/history-timeline-item.vue
+++ b/packages/ui/certd-client/src/views/certd/pipeline/pipeline/component/history-timeline-item.vue
@@ -5,12 +5,12 @@
-
-
+
+
{{ status.label }}
- 当前
- 查看
+ 当前
+ 查看
@@ -89,5 +89,9 @@ export default defineComponent({
.ant-tag.pointer {
cursor: pointer;
}
+
+ .ant-timeline .ant-timeline-item-content {
+ margin-inline-start: 22px !important;
+ }
}
diff --git a/packages/ui/certd-server/src/modules/auto/auto-a-init-site.ts b/packages/ui/certd-server/src/modules/auto/auto-a-init-site.ts
index d7a33473b..194e8f0fc 100644
--- a/packages/ui/certd-server/src/modules/auto/auto-a-init-site.ts
+++ b/packages/ui/certd-server/src/modules/auto/auto-a-init-site.ts
@@ -1,10 +1,10 @@
-import { Autoload, Config, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
-import { http, logger } from '@certd/basic';
-import { UserService } from '../sys/authority/service/user-service.js';
+import { logger } from '@certd/basic';
import { PlusService, SysInstallInfo, SysPrivateSettings, SysSettingsService } from '@certd/lib-server';
-import { nanoid } from 'nanoid';
+import { Autoload, Config, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
import crypto from 'crypto';
-import {SafeService} from "../sys/settings/safe-service.js";
+import { nanoid } from 'nanoid';
+import { UserService } from '../sys/authority/service/user-service.js';
+import { SafeService } from "../sys/settings/safe-service.js";
@Autoload()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
diff --git a/packages/ui/certd-server/src/plugins/plugin-ucloud/access.ts b/packages/ui/certd-server/src/plugins/plugin-ucloud/access.ts
index 09e761ab2..6ddff3054 100644
--- a/packages/ui/certd-server/src/plugins/plugin-ucloud/access.ts
+++ b/packages/ui/certd-server/src/plugins/plugin-ucloud/access.ts
@@ -99,7 +99,7 @@ export class UCloudAccess extends BaseAccess {
const resp = await client.uaccount().getProjectList({
"Action": "GetProjectList"
});
- this.ctx.logger.info(`获取到项目列表:${JSON.stringify(resp)}`);
+ // this.ctx.logger.info(`获取到项目列表:${JSON.stringify(resp)}`);
return resp;
}
@@ -120,7 +120,7 @@ export class UCloudAccess extends BaseAccess {
"PageNo": req.PageNo,
"PageSize": req.PageSize,
});
- this.ctx.logger.info(`获取到CDN域名列表:${JSON.stringify(resp)}`);
+ // this.ctx.logger.info(`获取到CDN域名列表:${JSON.stringify(resp)}`);
return resp;
}
diff --git a/packages/ui/certd-server/src/plugins/plugin-ucloud/plugins/index.ts b/packages/ui/certd-server/src/plugins/plugin-ucloud/plugins/index.ts
index 6d7717aea..0dfa01f67 100644
--- a/packages/ui/certd-server/src/plugins/plugin-ucloud/plugins/index.ts
+++ b/packages/ui/certd-server/src/plugins/plugin-ucloud/plugins/index.ts
@@ -1,5 +1,5 @@
export * from './plugin-deploy-to-cdn.js';
export * from './plugin-upload-to-ussl.js';
export * from './plugin-deploy-to-waf.js';
-export * from './plugin-deploy-to-alb.js';
+export * from './plugin-deploy-to-ulb.js';
export * from './plugin-deploy-to-us3.js';
diff --git a/packages/ui/certd-server/src/plugins/plugin-ucloud/plugins/plugin-deploy-to-alb.ts b/packages/ui/certd-server/src/plugins/plugin-ucloud/plugins/plugin-deploy-to-alb.ts
deleted file mode 100644
index 056fb4b95..000000000
--- a/packages/ui/certd-server/src/plugins/plugin-ucloud/plugins/plugin-deploy-to-alb.ts
+++ /dev/null
@@ -1,289 +0,0 @@
-import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
-import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
-import { CertReader, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
-import { UCloudAccess } from "../access.js";
-import { UCloudRegions } from "./constants.js";
-
-@IsTaskPlugin({
- name: "UCloudDeployToALB",
- title: "UCloud-部署到ALB",
- desc: "将证书部署到UCloud ALB(应用负载均衡)",
- icon: "svg:icon-ucloud",
- group: pluginGroups.ucloud.key,
- needPlus: false,
- default: {
- strategy: {
- runStrategy: RunStrategy.SkipWhenSucceed
- }
- }
-})
-export class UCloudDeployToALB extends AbstractTaskPlugin {
- @TaskInput({
- title: "域名证书",
- helper: "请选择前置任务输出的域名证书",
- component: {
- name: "output-selector",
- from: [...CertApplyPluginNames]
- }
- })
- cert!: CertInfo;
-
- @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
- certDomains!: string[];
-
- @TaskInput({
- title: "UCloud授权",
- component: {
- name: "access-selector",
- type: "ucloud"
- },
- required: true
- })
- accessId!: string;
-
- @TaskInput(
- createRemoteSelectInputDefine({
- title: "地域",
- helper: "选择UCloud地域",
- action: UCloudDeployToALB.prototype.onGetRegionList.name,
- multi:false
- })
- )
- region!: string;
-
- @TaskInput(
- createRemoteSelectInputDefine({
- title: "负载均衡实例",
- helper: "选择ALB负载均衡实例",
- action: UCloudDeployToALB.prototype.onGetALBList.name,
- multi:false
- })
- )
- loadBalancerId!: string;
-
- @TaskInput(
- createRemoteSelectInputDefine({
- title: "监听器列表",
- helper: "要更新的ALB监听器列表",
- action: UCloudDeployToALB.prototype.onGetListenerList.name
- })
- )
- listenerList!: string[];
-
- @TaskInput({
- title: "上传证书模式",
- helper: "选择是更新默认证书还是添加扩展证书",
- component: {
- name: "a-select",
- options: [
- { label: "更新默认证书", value: "update_default" },
- { label: "添加扩展证书", value: "add_extension" }
- ]
- },
- required: true,
- default: "update_default"
- })
- deployMode!: string;
-
- async onInstance() {
- }
-
- async onGetRegionList(req: PageSearch = {}) {
- const access = await this.getAccess(this.accessId);
-
- const res = await access.GetRegion();
- let list = res.Regions || [];
-
- if (!list || list.length === 0) {
- throw new Error("没有获取到UCloud地域列表");
- }
-
- const haveSet = {}
- list = list.filter((item: any) => {
- const region = item.Region;
- if (haveSet[region]) {
- return false;
- }
- haveSet[region] = true;
- return true;
- })
- let options = list.map((item: any) => {
- const region = item.Region;
- const name = UCloudRegions.find((r) => r.value === region)?.label || item.RegionName;
- return {
- label: `${name}(${item.Region})`,
- value: item.Region
- };
- });
-
- return {
- list: options,
- total: options.length,
- pageNo: 1,
- pageSize: options.length
- };
- }
-
- async uploadCertToULB(){
- const access = await this.getAccess(this.accessId);
- const certInfo = this.cert as CertInfo;
- const sslName = CertReader.buildCertName(certInfo);
- const sslContent = certInfo.crt + '\n' + certInfo.key;
-
- const createRes = await access.invoke({
- "Action": "CreateSSL",
- "Region": this.region,
- "ProjectId": access.projectId,
- "SSLName": sslName,
- "SSLContent": sslContent,
- });
-
- if (createRes.RetCode !== 0) {
- throw new Error(`创建SSL证书失败: ${createRes.Message || '未知错误'}`);
- }
-
- return createRes.SSLId;
- }
-
- async execute(): Promise {
- const access = await this.getAccess(this.accessId);
- let certId = await this.uploadCertToULB();
-
-
-
- for (const item of this.listenerList) {
- this.logger.info(`----------- 开始处理监听器:${item}`);
- if (this.deployMode === "update_default") {
- await this.updateDefaultCert({
- access: access,
- loadBalancerId: this.loadBalancerId,
- listenerId: item,
- certId: certId
- });
- this.logger.info(`----------- 更新监听器默认证书${item}成功`);
- } else if (this.deployMode === "add_extension") {
- await this.addExtensionCert({
- access: access,
- loadBalancerId: this.loadBalancerId,
- listenerId: item,
- certId: certId
- });
- this.logger.info(`----------- 添加监听器扩展证书${item}成功`);
- }
- }
-
- this.logger.info("部署完成");
- }
-
- async updateDefaultCert(req: { access: any, loadBalancerId: string, listenerId: string, certId: string }) {
- const { access, loadBalancerId, listenerId, certId } = req
-
- this.logger.info(`----------- 更新ALB监听器默认证书${listenerId}`);
- const resp = await access.invoke({
- "Action": "UpdateListenerAttribute",
- "Region": this.region,
- "ProjectId": access.projectId,
- "LoadBalancerId": loadBalancerId,
- "ListenerId": listenerId,
- "Certificates": [certId]
- });
- this.logger.info(`----------- 更新监听器默认证书${listenerId}成功,${JSON.stringify(resp)}`);
- }
-
- async addExtensionCert(req: { access: any, loadBalancerId: string, listenerId: string, certId: string }) {
- const { access, loadBalancerId, listenerId, certId } = req
-
- this.logger.info(`----------- 添加ALB监听器扩展证书${listenerId}`);
- const resp = await access.invoke({
- "Action": "AddSSLBinding",
- "Region": this.region,
- "ProjectId": access.projectId,
- "LoadBalancerId": loadBalancerId,
- "ListenerId": listenerId,
- "SSLIds": [certId]
- });
- this.logger.info(`----------- 添加监听器扩展证书${listenerId}成功,${JSON.stringify(resp)}`);
- }
-
- async onGetALBList(req: PageSearch = {}) {
- const access = await this.getAccess(this.accessId);
-
- const pageNo = req.pageNo ?? 1;
- const pageSize = req.pageSize ?? 100;
-
- const res = await access.invoke({
- "Action": "DescribeLoadBalancers",
- "Region": this.region,
- "ProjectId": access.projectId,
- "Type": "Application",
- "Offset": (pageNo - 1) * pageSize,
- "Limit": pageSize
- });
-
- const total = res.LoadBalancers?.length || 0;
- const list = res.LoadBalancers || [];
-
- if (!list || list.length === 0) {
- throw new Error("没有找到ALB实例,请先在控制台创建ALB实例");
- }
-
- const options = list.map((item: any) => {
- return {
- label: `${item.Name || item.LoadBalancerId}<${item.LoadBalancerId}>`,
- value: `${item.LoadBalancerId}`
- };
- });
-
- return {
- list: options,
- total: total,
- pageNo: pageNo,
- pageSize: pageSize
- };
- }
-
-
- async onGetListenerList(req: PageSearch = {}) {
- const access = await this.getAccess(this.accessId);
-
- if (!this.loadBalancerId) {
- throw new Error("请先选择ALB负载均衡实例");
- }
-
- const pageNo = req.pageNo ?? 1;
- const pageSize = req.pageSize ?? 100;
-
- const res = await access.invoke({
- "Action": "DescribeListeners",
- "Region": this.region,
- "ProjectId": access.projectId,
- "LoadBalancerId": this.loadBalancerId,
- "Offset": (pageNo - 1) * pageSize,
- "Limit": pageSize
- });
-
- const total = res.TotalCount || 0;
- const list = res.Listeners || [];
-
- if (!list || list.length === 0) {
- throw new Error("没有找到ALB监听器,请先在控制台创建ALB实例和监听器");
- }
-
- const options = list.map((item: any) => {
- return {
- label: `${item.Name || item.ListenerId}<${item.ListenerId}>`,
- value: `${item.ListenerId}`,
- domain: item.Name || item.ListenerId
- };
- });
-
- return {
- list: this.ctx.utils.options.buildGroupOptions(options, this.certDomains),
- total: total,
- pageNo: pageNo,
- pageSize: pageSize
- };
- }
-}
-
-new UCloudDeployToALB();
diff --git a/packages/ui/certd-server/src/plugins/plugin-ucloud/plugins/plugin-deploy-to-ulb.ts b/packages/ui/certd-server/src/plugins/plugin-ucloud/plugins/plugin-deploy-to-ulb.ts
new file mode 100644
index 000000000..faa500832
--- /dev/null
+++ b/packages/ui/certd-server/src/plugins/plugin-ucloud/plugins/plugin-deploy-to-ulb.ts
@@ -0,0 +1,333 @@
+import { AbstractTaskPlugin, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
+import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
+import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
+import { UCloudAccess } from "../access.js";
+import { UCloudRegions } from "./constants.js";
+
+@IsTaskPlugin({
+ name: "UCloudDeployToULB",
+ title: "UCloud-部署到负载均衡",
+ desc: "将证书部署到UCloud负载均衡(ULB/ALB/CLB)",
+ icon: "svg:icon-ucloud",
+ group: pluginGroups.ucloud.key,
+ needPlus: false,
+ default: {
+ strategy: {
+ runStrategy: RunStrategy.SkipWhenSucceed
+ }
+ }
+})
+export class UCloudDeployToULB extends AbstractTaskPlugin {
+ @TaskInput({
+ title: "域名证书",
+ helper: "请选择前置任务输出的域名证书",
+ component: {
+ name: "output-selector",
+ from: [...CertApplyPluginNames]
+ }
+ })
+ cert!: CertInfo;
+
+ @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
+ certDomains!: string[];
+
+ @TaskInput({
+ title: "UCloud授权",
+ component: {
+ name: "access-selector",
+ type: "ucloud"
+ },
+ required: true
+ })
+ accessId!: string;
+
+ @TaskInput(
+ createRemoteSelectInputDefine({
+ title: "地域",
+ helper: "选择UCloud地域",
+ action: UCloudDeployToULB.prototype.onGetRegionList.name,
+ multi: false
+ })
+ )
+ region!: string;
+
+ @TaskInput(
+ createRemoteSelectInputDefine({
+ title: "证书列表",
+ helper: "选择要替换的证书名称\n如果证书不存在,可以手动输入证书名称(运行一次后将会自动创建,您可以在ULB控制台进行使用)\n请确保证书名称不要重复,如果重复,只会更新创建时间最近的那一条",
+ action: UCloudDeployToULB.prototype.onGetCertList.name,
+ })
+ )
+ certNameList!: string[];
+
+
+ async onInstance() {
+ }
+
+ async onGetRegionList(req: PageSearch = {}) {
+ const access = await this.getAccess(this.accessId);
+
+ const res = await access.GetRegion();
+ let list = res.Regions || [];
+
+ if (!list || list.length === 0) {
+ throw new Error("没有获取到UCloud地域列表");
+ }
+
+ const haveSet = {}
+ list = list.filter((item: any) => {
+ const region = item.Region;
+ if (haveSet[region]) {
+ return false;
+ }
+ haveSet[region] = true;
+ return true;
+ })
+ let options = list.map((item: any) => {
+ const region = item.Region;
+ const name = UCloudRegions.find((r) => r.value === region)?.label || item.RegionName;
+ return {
+ label: `${name}(${item.Region})`,
+ value: item.Region
+ };
+ });
+
+ return {
+ list: options,
+ total: options.length,
+ pageNo: 1,
+ pageSize: options.length
+ };
+ }
+
+
+ async execute(): Promise {
+ const access = await this.getAccess(this.accessId);
+ const allCertList = await this.getAllCert(access);
+ const certMap = {}
+ allCertList.forEach((item) => {
+ if (!item.name) {
+ return;
+ }
+ certMap[item.name] = item;
+ })
+
+ for (const certName of this.certNameList) {
+ this.logger.info(`----------- 开始更新证书:${certName}`);
+ const oldCert = certMap[certName];
+ if (!oldCert) {
+ //如果没有找到旧证书,则跳过
+ this.logger.info(`没有找到ULB证书${certName},仅上传证书,不更新`);
+ await this.uploadCertToULB({
+ access,
+ certName: `${certName}`,
+ });
+ this.logger.info(`----------- 上传证书${certName}完成`);
+ continue;
+ }
+
+ const bakCertName = `${certName}_${oldCert.id}_${Date.now()}`
+ let certId = await this.uploadCertToULB({
+ access,
+ certName:bakCertName,
+ });
+ this.logger.info(`----------- 上传证书${bakCertName}完成`);
+
+ await this.updateSSLBinding({
+ access,
+ oldSSL: oldCert,
+ newSSLId: certId,
+ certName,
+ });
+ this.logger.info(`----------- 证书${certName}部署完成`);
+
+ }
+
+ await this.ctx.utils.sleep(2000);
+ this.logger.info(`----------- 开始清理过期证书`);
+ await this.clearExpiredCert(access, allCertList);
+ this.logger.info(`----------- 清理过期证书完成`);
+ this.logger.info("部署完成");
+ }
+
+ async clearExpiredCert(access: UCloudAccess, allCertList: any[]) {
+ const now = Date.now()/1000;
+ const expiredCertList = allCertList.filter((item) => {
+ if (!item.notAfter) {
+ return false;
+ }
+ return item.notAfter < now;
+ })
+ if (!expiredCertList || expiredCertList.length === 0) {
+ this.logger.info(`----------- 没有过期证书需要清理`);
+ return;
+ }
+ for (const cert of expiredCertList) {
+ this.logger.info(`----------- 清理过期证书:${cert.name} ${cert.id}`);
+ try {
+ await access.invoke({
+ "Action": "DeleteSSL",
+ "Region": this.region,
+ "ProjectId": access.projectId,
+ "SSLId": cert.id,
+ });
+ this.logger.info(`----------- 清理过期证书成功:${cert.name} ${cert.id}`);
+ } catch (error) {
+ this.logger.error(`----------- 清理过期证书失败:${cert.name} ${cert.id}`, error);
+ }
+ }
+ }
+ async updateSSLBinding(req: {
+ access: UCloudAccess,
+ oldSSL: any,
+ newSSLId: string,
+ certName: string,
+ }) {
+ const access = req.access;
+ const oldSSLId = req.oldSSL.id;
+ const newSSLId = req.newSSLId;
+
+ if (!req.oldSSL.relations || req.oldSSL.relations.length === 0) {
+ this.logger.info(`----------- 证书${req.oldSSL.name} ${req.oldSSL.id} 没有绑定ULB,无需更新绑定`);
+ } else {
+ this.logger.info(`----------- 更新ULB证书绑定:${oldSSLId} -> ${newSSLId}`);
+ await access.invoke({
+ "Action": "UpdateSSLBinding",
+ "Region": this.region,
+ "ProjectId": access.projectId,
+ "OldSSLId": oldSSLId,
+ "NewSSLId": newSSLId,
+ });
+ this.logger.info(`----------- 更新ULB证书绑定成功: ${newSSLId}`);
+ }
+
+ // 更新证书名称
+ this.logger.info(`----------- 更新ULB证书名称:${newSSLId} -> ${req.certName}`);
+ await access.invoke({
+ "Action": "UpdateSSLAttribute",
+ "Region": this.region,
+ "ProjectId": access.projectId,
+ "SSLId": newSSLId,
+ "SSLName": req.certName,
+ });
+ this.logger.info(`----------- 更新ULB证书名称成功:${req.certName}`);
+ await this.ctx.utils.sleep(5000);
+ try {
+ this.logger.info(`----------- 删除ULB旧证书:${oldSSLId}`);
+ await access.invoke({
+ "Action": "DeleteSSL",
+ "Region": this.region,
+ "ProjectId": access.projectId,
+ "SSLId": oldSSLId,
+ });
+ this.logger.info(`----------- 删除ULB旧证书成功:${oldSSLId}`);
+ } catch (error) {
+ this.logger.error(`----------- 删除ULB旧证书失败:${oldSSLId}`, error);
+ }
+
+ }
+
+ async uploadCertToULB(req: {
+ access: UCloudAccess,
+ certName: string,
+ }) {
+ const access = req.access;
+ const certName = req.certName;
+ const certContent = `${this.cert.crt}\n${this.cert.key}`
+ const res = await access.invoke({
+ "Action": "CreateSSL",
+ "Region": this.region,
+ "ProjectId": access.projectId,
+ "SSLName": certName,
+ "SSLContent": certContent,
+ });
+
+ if (res.RetCode !== 0) {
+ throw new Error(`创建ULB证书失败: ${res.Message || '未知错误'}`);
+ }
+
+ return res.SSLId;
+ }
+
+
+ async getAllCert(access: UCloudAccess) {
+ const certList = [] as any[];
+ const pager = {
+ pageNo: 1,
+ pageSize: 100,
+ };
+ while (true) {
+ const { list, total } = await this.getCertPage(access, pager);
+ certList.push(...list);
+ if (certList.length >= total) {
+ break;
+ }
+ pager.pageNo++;
+ }
+ // this.logger.info(`----------- certList----------:\n`, certList);
+ return certList;
+ }
+
+
+ async getCertPage(access: UCloudAccess, req: PageSearch = {}) {
+ const pageNo = req.pageNo ?? 1;
+ const pageSize = req.pageSize ?? 100;
+
+ const res = await access.invoke({
+ "Action": "DescribeSSLV2",
+ "Region": this.region,
+ "ProjectId": access.projectId,
+ "Offset": (pageNo - 1) * pageSize,
+ "Limit": pageSize
+ });
+
+ let list = res.DataSet || [];
+ const total = res.TotalCount || list.length;
+
+ list = list.map((item: any) => {
+ const domains = [
+ ...item.Domains.split(','),
+ ...item.DNSNames.split(',')
+ ]
+ return {
+ id: item.SSLId,
+ name: item.SSLName,
+ domain: domains,
+ notAfter: item.NotAfter,
+ notBefore: item.NotBefore,
+ createTime: item.CreateTime,
+ relations: item.Relations,
+ };
+ })
+ return {
+ list: list,
+ total: total,
+ };
+ }
+
+ async onGetCertList(req: PageSearch = {}) {
+ const access = await this.getAccess(this.accessId);
+
+ const { list, total } = await this.getCertPage(access, req);
+
+ if (!list || list.length === 0) {
+ throw new Error("没有找到ULB证书,请先在控制台创建ULB证书");
+ }
+
+ const options = list.map((item: any) => {
+ return {
+ label: `${item.name}<${item.id}>`,
+ value: `${item.name}`,
+ domain: item.domain,
+ };
+ });
+
+ return {
+ list: options,
+ total: total,
+ };
+ }
+
+}
+
+new UCloudDeployToULB();