Compare commits

...

17 Commits

Author SHA1 Message Date
xiaojunnuo
74c5259af8 build: release 2026-04-01 00:29:52 +08:00
xiaojunnuo
a3e7d4414d build: publish 2026-03-31 23:59:12 +08:00
xiaojunnuo
986d32eb81 build: trigger build image 2026-03-31 23:59:00 +08:00
xiaojunnuo
de0ae14544 v1.39.8 2026-03-31 23:57:25 +08:00
xiaojunnuo
6b52276fb6 build: prepare to build 2026-03-31 23:54:34 +08:00
xiaojunnuo
a19ea7489c perf: 支持部署证书到百度CCE 2026-03-31 23:52:12 +08:00
xiaojunnuo
14229c2f00 chore: delete agents 2026-03-31 22:27:42 +08:00
xiaojunnuo
6eb20a1f2e fix: 修复上传头像退出登录的bug 2026-03-31 15:42:02 +08:00
xiaojunnuo
8debac2edf chore: 优化阿里云cdn 2026-03-29 02:40:26 +08:00
xiaojunnuo
a68301e4dc perf: 阿里云CDN部署支持根据证书域名自动匹配部署 2026-03-29 02:25:45 +08:00
xiaojunnuo
c6a988bc92 perf: dcdn自动匹配部署,支持新增域名感知 2026-03-29 01:57:33 +08:00
xiaojunnuo
fe02ce7b64 fix: 修复某些情况下报没有匹配到任何校验方式的bug 2026-03-29 00:13:44 +08:00
xiaojunnuo
df012dec90 perf: 阿里云dcdn支持根据证书域名匹配模式 2026-03-28 11:17:50 +08:00
xiaojunnuo
5969425a6f docs: 1 2026-03-26 22:30:37 +08:00
xiaojunnuo
b17b1e6463 build: release 2026-03-26 01:28:56 +08:00
xiaojunnuo
c99e61c402 build: publish 2026-03-26 01:08:03 +08:00
xiaojunnuo
f4aaec8b3c build: trigger build image 2026-03-26 01:07:51 +08:00
65 changed files with 1176 additions and 349 deletions

View File

@@ -89,16 +89,55 @@ certDomains!: string[];
accessId!: string;
```
### 4. 实现插件方法
### 4. 动态显隐配置mergeScript
#### 4.1 插件实例化时执行的方法
使用 `mergeScript` 可以实现根据其他输入值动态控制当前输入项的显隐状态。
```typescript
@TaskInput({
title: '匹配模式',
component: {
name: 'select',
options: [
{ label: '手动选择', value: 'manual' },
{ label: '根据证书匹配', value: 'auto' },
],
},
default: 'manual',
})
domainMatchMode!: 'manual' | 'auto';
@TaskInput(
createRemoteSelectInputDefine({
title: 'DCDN加速域名',
helper: '你在阿里云上配置的DCDN加速域名',
action: DeployCertToAliyunDCDN.prototype.onGetDomainList.name,
watches: ['certDomains', 'accessId'],
required: true,
mergeScript: `
return {
show: ctx.compute(({form})=>{
return domainMatchMode === "manual"
})
}
`,
})
)
domainName!: string | string[];
```
`mergeScript` 中的 `ctx.compute` 函数接收一个回调函数,通过 `form` 参数可以访问表单中的其他字段值。
### 5. 实现插件方法
#### 5.1 插件实例化时执行的方法
```typescript
// 插件实例化时执行的方法
async onInstance() {}
```
#### 4.2 插件执行方法
#### 5.2 插件执行方法
```typescript
// 插件执行方法
@@ -130,7 +169,9 @@ async execute(): Promise<void> {
}
```
#### 4.3 后端获取选项方法
#### 5.3 后端获取选项方法
使用 `createRemoteSelectInputDefine` 创建远程选择输入项,`action` 指向的方法接收 `PageSearch` 参数并返回 `{ list, total }` 格式。
```typescript
@TaskInput(
@@ -145,8 +186,8 @@ async execute(): Promise<void> {
)
siteName!: string | string[];
// 从后端获取选项的方法
async onGetSiteList(req: PageSearch) {
// 从后端获取选项的方法接收PageSearch参数
async onGetSiteList(data: PageSearch) {
if (!this.accessId) {
throw new Error('请选择Access授权');
}
@@ -154,7 +195,7 @@ async onGetSiteList(req: PageSearch) {
// @ts-ignore
const access = await this.getAccess(this.accessId);
// const siteRes = await access.GetDomainList(req);
// const siteRes = await access.GetDomainList(data);
// 以下是模拟数据
const siteRes = [
{ id: 1, siteName: 'site1.com' },
@@ -169,8 +210,12 @@ async onGetSiteList(req: PageSearch) {
domain: item.siteName,
};
});
// 将站点域名名称根据证书域名进行匹配分组,分成匹配的和不匹配的两组选项,返回给前端,供用户选择
return optionsUtils.buildGroupOptions(options, this.certDomains);
// 返回{list, total}格式
return {
list: optionsUtils.buildGroupOptions(options, this.certDomains),
total: siteRes.length,
};
}
```
@@ -383,7 +428,10 @@ export class DemoTest extends AbstractTaskPlugin {
};
});
//将站点域名名称根据证书域名进行匹配分组,分成匹配的和不匹配的两组选项,返回给前端,供用户选择
return optionsUtils.buildGroupOptions(options, this.certDomains);
return {
list: optionsUtils.buildGroupOptions(options, this.certDomains),
total: siteRes.length,
};
}
}
```

View File

@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
### Bug Fixes
* 修复某些情况下报没有匹配到任何校验方式的bug ([fe02ce7](https://github.com/certd/certd/commit/fe02ce7b64cf23c4dc4c30daccd5330059a35e9a))
* 修复上传头像退出登录的bug ([6eb20a1](https://github.com/certd/certd/commit/6eb20a1f2e31d984d9135edbf39c97cdd15621f9))
### Performance Improvements
* 阿里云CDN部署支持根据证书域名自动匹配部署 ([a68301e](https://github.com/certd/certd/commit/a68301e4dcea8b7391ad751aa57555d566297ad9))
* 阿里云dcdn支持根据证书域名匹配模式 ([df012de](https://github.com/certd/certd/commit/df012dec90590ecba85a69ed6355cfa8382c1da3))
* 支持部署证书到百度CCE ([a19ea74](https://github.com/certd/certd/commit/a19ea7489c01cdbf795fb51f804bd6d00389f604))
* dcdn自动匹配部署支持新增域名感知 ([c6a988b](https://github.com/certd/certd/commit/c6a988bc925886bd7163c1270f2b7a10a57b1c5b))
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
### Bug Fixes

View File

@@ -3,6 +3,31 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
### Bug Fixes
* 修复某些情况下报没有匹配到任何校验方式的bug ([fe02ce7](https://github.com/certd/certd/commit/fe02ce7b64cf23c4dc4c30daccd5330059a35e9a))
* 修复上传头像退出登录的bug ([6eb20a1](https://github.com/certd/certd/commit/6eb20a1f2e31d984d9135edbf39c97cdd15621f9))
### Performance Improvements
* 阿里云CDN部署支持根据证书域名自动匹配部署 ([a68301e](https://github.com/certd/certd/commit/a68301e4dcea8b7391ad751aa57555d566297ad9))
* 阿里云dcdn支持根据证书域名匹配模式 ([df012de](https://github.com/certd/certd/commit/df012dec90590ecba85a69ed6355cfa8382c1da3))
* 支持部署证书到百度CCE ([a19ea74](https://github.com/certd/certd/commit/a19ea7489c01cdbf795fb51f804bd6d00389f604))
* dcdn自动匹配部署支持新增域名感知 ([c6a988b](https://github.com/certd/certd/commit/c6a988bc925886bd7163c1270f2b7a10a57b1c5b))
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
### Bug Fixes
* 修复cname校验报该授权无权限的bug ([b1eb706](https://github.com/certd/certd/commit/b1eb7069258d6ff2b128091911fa448eaffc5f33))
### Performance Improvements
* 支持部署到火山云tos自定义域名证书 ([af6deb9](https://github.com/certd/certd/commit/af6deb99cd24a69a189b1fdd1df51c8f7816dcda))
* 支持部署证书到火山引擎vod ([f91d591](https://github.com/certd/certd/commit/f91d591b03c50166d9fa352ba11c62d963869aa5))
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
### Bug Fixes

View File

@@ -1,5 +1,5 @@
# 任务插件
`128` 款任务插件
`129` 款任务插件
## 1. 证书申请
| 序号 | 名称 | 说明 |
@@ -155,8 +155,9 @@
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **百度云-部署证书到负载均衡** | 部署到百度云负载均衡包括BLB、APPBLB |
| 2.| **百度云-部署证书到CDN** | 部署到百度云CDN |
| 3.| **百度云-上传到证书托管** | 上传证书到百度云证书托管中心 |
| 2.| **百度云-部署到CCE** | 部署到百度云CCE集群Ingress等通过Secret管理证书的应用 |
| 3.| **百度云-部署证书到CDN** | 部署到百度云CDN |
| 4.| **百度云-上传到证书托管** | 上传证书到百度云证书托管中心 |
## 12. 七牛云
| 序号 | 名称 | 说明 |

View File

@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.39.7"
"version": "1.39.8"
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/publishlab/node-acme-client/compare/v1.39.7...v1.39.8) (2026-03-31)
**Note:** Version bump only for package @certd/acme-client
## [1.39.7](https://github.com/publishlab/node-acme-client/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/acme-client

View File

@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.39.7",
"version": "1.39.8",
"type": "module",
"module": "scr/index.js",
"main": "src/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.39.7",
"@certd/basic": "^1.39.8",
"@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5",
"axios": "^1.9.0",
@@ -70,5 +70,5 @@
"bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues"
},
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
"gitHead": "de0ae14544f1c3da4923dddc6a1a3bea4db295e7"
}

View File

@@ -588,7 +588,8 @@ class AcmeClient {
if (invalidStates.includes(resp.data.status)) {
abort();
throw new Error(util.formatResponseError(resp));
this.log(`[${d}] : 检查状态 = ${resp.data.status} ${JSON.stringify(resp.data)}`);
throw new Error("校验失败:" + util.formatResponseError(resp));
}
else if (pendingStates.includes(resp.data.status)) {
throw new Error(`[${d}] Operation is pending or processing当前仍然在等待状态`);

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
**Note:** Version bump only for package @certd/basic
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/basic

View File

@@ -1 +1 @@
01:02
23:54

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.39.7",
"version": "1.39.8",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -47,5 +47,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
"gitHead": "de0ae14544f1c3da4923dddc6a1a3bea4db295e7"
}

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
### Performance Improvements
* 阿里云CDN部署支持根据证书域名自动匹配部署 ([a68301e](https://github.com/certd/certd/commit/a68301e4dcea8b7391ad751aa57555d566297ad9))
* 阿里云dcdn支持根据证书域名匹配模式 ([df012de](https://github.com/certd/certd/commit/df012dec90590ecba85a69ed6355cfa8382c1da3))
* dcdn自动匹配部署支持新增域名感知 ([c6a988b](https://github.com/certd/certd/commit/c6a988bc925886bd7163c1270f2b7a10a57b1c5b))
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/pipeline

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.39.7",
"version": "1.39.8",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -18,8 +18,8 @@
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/basic": "^1.39.7",
"@certd/plus-core": "^1.39.7",
"@certd/basic": "^1.39.8",
"@certd/plus-core": "^1.39.8",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"reflect-metadata": "^0.1.13"
@@ -45,5 +45,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
"gitHead": "de0ae14544f1c3da4923dddc6a1a3bea4db295e7"
}

View File

@@ -334,7 +334,7 @@ export class Executor {
//参数没有变化
inputChanged = false;
}
if (step.strategy?.runStrategy === RunStrategy.SkipWhenSucceed) {
if (step.strategy?.runStrategy === RunStrategy.SkipWhenSucceed && define.runStrategy !== RunStrategy.AlwaysRun) {
if (lastResult != null && lastResult === ResultType.success && !inputChanged) {
step.status!.output = lastNode?.status?.output;
step.status!.files = lastNode?.status?.files;

View File

@@ -1,16 +1,16 @@
import { Registrable } from "../registry/index.js";
import { FileItem, FormItemProps, Pipeline, Runnable, Step } from "../dt/index.js";
import { FileStore } from "../core/file-store.js";
import { accessRegistry, IAccessService } from "../access/index.js";
import { ICnameProxyService, IEmailService, IServiceGetter, IUrlService } from "../service/index.js";
import { CancelError, IContext, RunHistory, RunnableCollection } from "../core/index.js";
import { HttpRequestConfig, ILogger, logger, utils } from "@certd/basic";
import { HttpClient } from "@certd/basic";
import { domainUtils, HttpClient, HttpRequestConfig, ILogger, logger, utils } from "@certd/basic";
import dayjs from "dayjs";
import { IPluginConfigService } from "../service/config.js";
import { upperFirst } from "lodash-es";
import { cloneDeep, upperFirst } from "lodash-es";
import { accessRegistry, IAccessService } from "../access/index.js";
import { PageSearch } from "../context/index.js";
import { FileStore } from "../core/file-store.js";
import { CancelError, IContext, RunHistory, RunnableCollection } from "../core/index.js";
import { FileItem, FormItemProps, Pipeline, Runnable, Step } from "../dt/index.js";
import { INotificationService } from "../notification/index.js";
import { Registrable } from "../registry/index.js";
import { IPluginConfigService } from "../service/config.js";
import { TaskEmitter } from "../service/emit.js";
import { ICnameProxyService, IEmailService, IServiceGetter, IUrlService } from "../service/index.js";
export type PluginRequestHandleReq<T = any> = {
typeName: string;
@@ -64,6 +64,7 @@ export type PluginDefine = Registrable & {
onlyAdmin?: boolean;
needPlus?: boolean;
showRunStrategy?: boolean;
runStrategy?: any;
pluginType?: string; //类型
type?: string; //来源
};
@@ -81,6 +82,12 @@ export type TaskResult = {
pipelineVars: Record<string, any>;
pipelinePrivateVars?: Record<string, any>;
};
export type CertTargetItem = {
value: string;
label: string;
domain: string | string[];
};
export type TaskInstanceContext = {
//流水线定义
pipeline: Pipeline;
@@ -315,6 +322,103 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
getLastOutput(key: string) {
return this.getLastStatus().status?.output?.[key];
}
isDomainMatched(domainList: string | string[], certDomains: string[]): boolean {
const matched = domainUtils.match(domainList, certDomains);
return matched;
}
isNotChanged() {
const lastResult = this.ctx?.lastStatus?.status?.status;
return !this.ctx.inputChanged && lastResult === "success";
}
async getAutoMatchedTargets(req: {
targetName: string;
certDomains: string[];
pageSize: number;
getDeployTargetList: (req: PageSearch) => Promise<{ list: CertTargetItem[]; total: number }>;
}): Promise<CertTargetItem[]> {
const matchedDomains: CertTargetItem[] = [];
let pageNo = 1;
const { certDomains } = req;
const pageSize = req.pageSize || 100;
while (true) {
const result = await req.getDeployTargetList({
pageNo,
pageSize,
});
const pageData = result.list;
this.logger.info(`获取到 ${pageData.length}${req.targetName}`);
if (!pageData || pageData.length === 0) {
break;
}
for (const item of pageData) {
const domainName = item.domain;
if (this.isDomainMatched(domainName, certDomains)) {
matchedDomains.push(item);
}
}
const totalCount = result.total || 0;
if (pageNo * pageSize >= totalCount || matchedDomains.length == 0) {
break;
}
pageNo++;
}
return matchedDomains;
}
async autoMatchedDeploy(req: {
targetName: string;
getCertDomains: () => Promise<string[]>;
uploadCert: () => Promise<any>;
deployOne: (req: { target: CertTargetItem; cert: any }) => Promise<void>;
getDeployTargetList: (req: PageSearch) => Promise<{ list: CertTargetItem[]; total: number }>;
}): Promise<{ result: string; deployedList: string[] }> {
this.logger.info("证书匹配模式部署");
const certDomains = await req.getCertDomains();
const certTargetList = await this.getAutoMatchedTargets({
targetName: req.targetName,
pageSize: 200,
certDomains,
getDeployTargetList: req.getDeployTargetList,
});
if (certTargetList.length === 0) {
this.logger.warn(`未找到匹配的${req.targetName}`);
return { result: "skip", deployedList: [] };
}
this.logger.info(`找到 ${certTargetList.length} 个匹配的${req.targetName}`);
//开始部署,检查是否已经部署过
const deployedList = cloneDeep(this.getLastStatus()?.status?.output?.deployedList || []);
const unDeployedTargets = certTargetList.filter(item => !deployedList.includes(item.value));
const count = unDeployedTargets.length;
const deployedCount = certTargetList.length - count;
if (deployedCount > 0) {
this.logger.info(`跳过 ${deployedCount} 个已部署过的${req.targetName}`);
}
this.logger.info(`需要部署 ${count}${req.targetName}`);
if (count === 0) {
return { result: "skip", deployedList };
}
this.logger.info(`开始部署`);
const aliCrtId = await req.uploadCert();
for (const target of unDeployedTargets) {
await req.deployOne({
cert: aliCrtId,
target,
});
deployedList.push(target.value);
}
this.logger.info(`本次成功部署 ${count}${req.targetName}`);
return { result: "success", deployedList };
}
}
export type OutputVO = {

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
**Note:** Version bump only for package @certd/lib-huawei
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/lib-huawei

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.39.7",
"version": "1.39.8",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -24,5 +24,5 @@
"prettier": "^2.8.8",
"tslib": "^2.8.1"
},
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
"gitHead": "de0ae14544f1c3da4923dddc6a1a3bea4db295e7"
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
**Note:** Version bump only for package @certd/lib-iframe
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/lib-iframe

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.39.7",
"version": "1.39.8",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -31,5 +31,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
"gitHead": "de0ae14544f1c3da4923dddc6a1a3bea4db295e7"
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
**Note:** Version bump only for package @certd/jdcloud
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/jdcloud

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/jdcloud",
"version": "1.39.7",
"version": "1.39.8",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
@@ -56,5 +56,5 @@
"fetch"
]
},
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
"gitHead": "de0ae14544f1c3da4923dddc6a1a3bea4db295e7"
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
### Performance Improvements
* 支持部署证书到百度CCE ([a19ea74](https://github.com/certd/certd/commit/a19ea7489c01cdbf795fb51f804bd6d00389f604))
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/lib-k8s

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-k8s",
"private": false,
"version": "1.39.7",
"version": "1.39.8",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -14,10 +14,11 @@
"build3": "rollup -c",
"build2": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"pub": "npm publish"
"pub": "npm publish",
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/basic": "^1.39.7",
"@certd/basic": "^1.39.8",
"@kubernetes/client-node": "0.21.0"
},
"devDependencies": {
@@ -32,5 +33,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
"gitHead": "de0ae14544f1c3da4923dddc6a1a3bea4db295e7"
}

View File

@@ -59,9 +59,9 @@ export class K8sClient {
const yml = loadYaml<KubernetesObject>(manifest);
const client = this.getKubeClient();
try {
this.logger.info("apply yaml:", yml);
await client.create(yml);
} catch (e) {
this.logger.error("apply error", e.response?.body);
if (e.response?.body?.reason === "AlreadyExists") {
//patch
this.logger.info("patch existing resource: ", yml.metadata?.name);
@@ -70,13 +70,26 @@ export class K8sClient {
yml.metadata = {};
}
yml.metadata.resourceVersion = existing.body.metadata.resourceVersion;
await client.patch(yml);
return;
const res = await client.patch(yml);
return res?.body;
}
throw e;
}
}
async applyPatch(manifest: string) {
const yml = loadYaml<KubernetesObject>(manifest);
const client = this.getKubeClient();
this.logger.info("patch yaml:", yml);
const existing = await client.read(yml as any);
if (!yml.metadata) {
yml.metadata = {};
}
yml.metadata.resourceVersion = existing.body.metadata.resourceVersion;
const res = await client.patch(yml);
return res?.body;
}
/**
*
* @param localRecords { [domain]:{ip:'xxx.xx.xxx'} }
@@ -112,6 +125,7 @@ export class K8sClient {
*/
async createSecret(opts: { namespace: string; body: V1Secret }) {
const namespace = opts.namespace || "default";
this.logger.info("create secret:", opts.body.metadata);
const created = await this.client.createNamespacedSecret(namespace, opts.body);
this.logger.info("new secrets:", opts.body.metadata);
return created.body;
@@ -152,6 +166,8 @@ export class K8sClient {
this.logger.info(`secret ${secretName} 已创建`);
return res;
}
throw new Error(`secret ${secretName} 不存在`);
}
throw e;
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
**Note:** Version bump only for package @certd/lib-server
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.39.7",
"version": "1.39.8",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -28,11 +28,11 @@
],
"license": "AGPL",
"dependencies": {
"@certd/acme-client": "^1.39.7",
"@certd/basic": "^1.39.7",
"@certd/pipeline": "^1.39.7",
"@certd/plugin-lib": "^1.39.7",
"@certd/plus-core": "^1.39.7",
"@certd/acme-client": "^1.39.8",
"@certd/basic": "^1.39.8",
"@certd/pipeline": "^1.39.8",
"@certd/plugin-lib": "^1.39.8",
"@certd/plus-core": "^1.39.8",
"@midwayjs/cache": "3.14.0",
"@midwayjs/core": "3.20.11",
"@midwayjs/i18n": "3.20.13",
@@ -64,5 +64,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
"gitHead": "de0ae14544f1c3da4923dddc6a1a3bea4db295e7"
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/midway-flyway-js

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
"version": "1.39.7",
"version": "1.39.8",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -46,5 +46,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
"gitHead": "de0ae14544f1c3da4923dddc6a1a3bea4db295e7"
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
**Note:** Version bump only for package @certd/plugin-cert
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/plugin-cert

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.39.7",
"version": "1.39.8",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -17,10 +17,10 @@
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/acme-client": "^1.39.7",
"@certd/basic": "^1.39.7",
"@certd/pipeline": "^1.39.7",
"@certd/plugin-lib": "^1.39.7",
"@certd/acme-client": "^1.39.8",
"@certd/basic": "^1.39.8",
"@certd/pipeline": "^1.39.8",
"@certd/plugin-lib": "^1.39.8",
"psl": "^1.9.0",
"punycode.js": "^2.3.1"
},
@@ -38,5 +38,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
"gitHead": "de0ae14544f1c3da4923dddc6a1a3bea4db295e7"
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
### Performance Improvements
* dcdn自动匹配部署支持新增域名感知 ([c6a988b](https://github.com/certd/certd/commit/c6a988bc925886bd7163c1270f2b7a10a57b1c5b))
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/plugin-lib

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
"version": "1.39.7",
"version": "1.39.8",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -22,10 +22,10 @@
"@alicloud/pop-core": "^1.7.10",
"@alicloud/tea-util": "^1.4.11",
"@aws-sdk/client-s3": "^3.964.0",
"@certd/acme-client": "^1.39.7",
"@certd/basic": "^1.39.7",
"@certd/pipeline": "^1.39.7",
"@certd/plus-core": "^1.39.7",
"@certd/acme-client": "^1.39.8",
"@certd/basic": "^1.39.8",
"@certd/pipeline": "^1.39.8",
"@certd/plus-core": "^1.39.8",
"@kubernetes/client-node": "0.21.0",
"ali-oss": "^6.22.0",
"basic-ftp": "^5.0.5",
@@ -57,5 +57,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
"gitHead": "de0ae14544f1c3da4923dddc6a1a3bea4db295e7"
}

View File

@@ -43,6 +43,12 @@ const formats = {
jks: ["jks"],
p7b: ["p7b", "key"],
};
export type SimpleCertDetail = {
notBefore: Date;
notAfter: Date;
domains: string[];
};
export class CertReader {
cert: CertInfo;
@@ -116,6 +122,15 @@ export class CertReader {
return CertReader.readCertDetail(crt);
}
getSimpleDetail() {
const { detail } = this.getCrtDetail();
return {
notBefore: detail.notBefore,
notAfter: detail.notAfter,
domains: this.getAllDomains(),
};
}
static readCertDetail(crt: string) {
const detail = crypto.readCertificateInfo(crt.toString());
const effective = detail.notBefore;

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
### Bug Fixes
* 修复上传头像退出登录的bug ([6eb20a1](https://github.com/certd/certd/commit/6eb20a1f2e31d984d9135edbf39c97cdd15621f9))
### Performance Improvements
* 阿里云dcdn支持根据证书域名匹配模式 ([df012de](https://github.com/certd/certd/commit/df012dec90590ecba85a69ed6355cfa8382c1da3))
* dcdn自动匹配部署支持新增域名感知 ([c6a988b](https://github.com/certd/certd/commit/c6a988bc925886bd7163c1270f2b7a10a57b1c5b))
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/ui-client

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/ui-client",
"version": "1.39.7",
"version": "1.39.8",
"private": true,
"scripts": {
"dev": "vite --open",
@@ -106,8 +106,8 @@
"zod-defaults": "^0.1.3"
},
"devDependencies": {
"@certd/lib-iframe": "^1.39.7",
"@certd/pipeline": "^1.39.7",
"@certd/lib-iframe": "^1.39.8",
"@certd/pipeline": "^1.39.8",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12",

View File

@@ -235,7 +235,6 @@ watch(
const { form } = value;
const oldForm: any = oldValue?.form;
let changed = oldForm == null || optionsRef.value.length == 0;
debugger;
if (props.watches && props.watches.length > 0) {
for (const key of props.watches) {
if (oldForm && JSON.stringify(form[key]) != JSON.stringify(oldForm[key])) {

View File

@@ -589,11 +589,11 @@ export default {
userValidityPeriodHelper: "有效期内用户可正常使用,失效后用户的流水线将被停用",
enableUsernameRegistration: "开启用户名注册",
enableEmailRegistration: "开启邮箱注册",
proFeature: "专业版功能",
proFeature: "Certd专业版功能",
emailServerSetup: "设置邮箱服务器",
enableSmsLoginRegister: "开启手机号登录、注册",
defaultLoginType: "默认登录方式",
commFeature: "商业版功能",
commFeature: "Certd商业版功能",
smsProvider: "短信提供商",
aliyunSms: "阿里云短信",
tencentSms: "腾讯云短信",
@@ -813,7 +813,7 @@ export default {
},
domain: {
domainManager: "域名管理",
domainDescription: "管理域名的校验方式,用于申请证书时自动选择验证方式",
domainDescription: "流水线校验方式选择“自动选择”时此处配置才有用,支持自动导入;注意:这里只需要管理主域名即可,子域名不要填写(子域名托管和免费二级子域名除外)",
domain: "域名",
challengeType: "校验类型",
dnsProviderType: "DNS提供商类型",

View File

@@ -88,13 +88,13 @@ export default {
activation_code_one_use: "激活码使用过一次之后,不可再次使用,如果要更换站点,请",
bind_account: "绑定账号",
transfer_vip: '然后"转移VIP"即可',
needVipTip: "此为专业版功能,请先开通专业版",
needVipTip: "此为Certd专业版功能,请先开通Certd专业版",
manual_activation: "激活码手动激活",
close: "关闭",
have_activation_code: "已经有激活码了?",
buy: "立即购买",
already_plus: "已经是专业版了,是否升级为商业版?注意:专业版时长将被覆盖",
already_comm: "已经是商业版了,不能降级为专业版",
already_plus: "已经是Certd专业版了,是否升级为商业版?注意:Certd专业版时长将被覆盖",
already_comm: "已经是Certd商业版了,不能降级为专业版",
already_perpetual_plus: "您已经是永久专业版了,无法继续升级",
confirm: "确认",
not_effective: "VIP没有生效/时长未同步?",

View File

@@ -66,7 +66,7 @@ export function useUserProfile() {
width: "auto",
},
buildUrl(key: string) {
return `api/basic/file/download?&key=` + key;
return `api/basic/file/download?token=${userStore.getToken}&key=` + key;
},
},
},
@@ -82,7 +82,7 @@ export function useUserProfile() {
onReady: null,
uploader: {
type: "form",
action: "/basic/file/upload",
action: "/basic/file/upload?token=" + userStore.getToken,
name: "file",
headers: {
Authorization: "Bearer " + userStore.getToken,
@@ -92,7 +92,7 @@ export function useUserProfile() {
},
},
buildUrl(key: string) {
return `api/basic/file/download?&key=` + key;
return `api/basic/file/download?token=${userStore.getToken}&key=` + key;
},
},
},

View File

@@ -147,6 +147,7 @@ import { isEmpty } from "lodash-es";
import { dict } from "@fast-crud/fast-crud";
import dayjs from "dayjs";
import { useRouter } from "vue-router";
import { useUserStore } from "/@/store/user";
const { t } = useI18n();
@@ -351,7 +352,7 @@ const checkPasskeySupport = () => {
passkeySupported.value = true;
}
};
const userStore = useUserStore();
const userAvatar = computed(() => {
if (isEmpty(userInfo.value.avatar)) {
return "";
@@ -360,7 +361,7 @@ const userAvatar = computed(() => {
return userInfo.value.avatar;
}
return "api/basic/file/download?&key=" + userInfo.value.avatar;
return `api/basic/file/download?token=${userStore.getToken}&key=${userInfo.value.avatar}`;
});
onMounted(async () => {

View File

@@ -3,7 +3,7 @@
<div class="header-profile flex-wrap bg-white dark:bg-black">
<div class="flex flex-1">
<div class="avatar">
<a-avatar v-if="userInfo.avatar" size="large" :src="'api/basic/file/download?&key=' + userInfo.avatar" style="background-color: #eee"> </a-avatar>
<a-avatar v-if="userInfo.avatar" size="large" :src="avatar" style="background-color: #eee"> </a-avatar>
<a-avatar v-else size="large" style="background-color: #00b4f5">
{{ userInfo.username }}
</a-avatar>
@@ -228,6 +228,16 @@ const userStore = useUserStore();
const userInfo: ComputedRef<UserInfoRes> = computed(() => {
return userStore.getUserInfo;
});
const avatar = computed(() => {
const avt = userStore.getUserInfo?.avatar;
if (!avt) {
return "";
}
if (avt.startsWith("http")) {
return avt;
}
return `/api/basic/file/download?key=${avt}`;
});
const now = computed(() => {
const serverTime = Date.now() - settingStore.app.deltaTime;
return dayjs(serverTime).format("YYYY-MM-DD HH:mm:ss");

View File

@@ -80,7 +80,7 @@ onMounted(() => {
await settingStore.doBindUrl();
notification.success({
message: "更新成功",
description: "专业版/商业版已激活",
description: "Certd专业版/商业版已激活",
});
});
});

View File

@@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.8](https://github.com/certd/certd/compare/v1.39.7...v1.39.8) (2026-03-31)
### Bug Fixes
* 修复某些情况下报没有匹配到任何校验方式的bug ([fe02ce7](https://github.com/certd/certd/commit/fe02ce7b64cf23c4dc4c30daccd5330059a35e9a))
### Performance Improvements
* 阿里云CDN部署支持根据证书域名自动匹配部署 ([a68301e](https://github.com/certd/certd/commit/a68301e4dcea8b7391ad751aa57555d566297ad9))
* 阿里云dcdn支持根据证书域名匹配模式 ([df012de](https://github.com/certd/certd/commit/df012dec90590ecba85a69ed6355cfa8382c1da3))
* 支持部署证书到百度CCE ([a19ea74](https://github.com/certd/certd/commit/a19ea7489c01cdbf795fb51f804bd6d00389f604))
* dcdn自动匹配部署支持新增域名感知 ([c6a988b](https://github.com/certd/certd/commit/c6a988bc925886bd7163c1270f2b7a10a57b1c5b))
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
### Bug Fixes

View File

@@ -7,6 +7,7 @@ title: 阿里云-部署证书至CDN
icon: svg:icon-aliyun
group: aliyun
desc: 自动部署域名证书至阿里云CDN
runStrategy: 0
input:
endpoint:
title: 证书服务接入点
@@ -59,36 +60,6 @@ input:
type: aliyun
required: true
order: 0
domainName:
title: CDN加速域名
component:
name: remote-select
vModel: value
mode: tags
type: plugin
typeName: DeployCertToAliyunCDN
action: onGetDomainList
search: false
pager: false
multi: true
watches:
- certDomains
- accessId
- certDomains
- accessId
required: true
mergeScript: |2-
return {
component:{
form: ctx.compute(({form})=>{
return form
})
},
}
helper: 你在阿里云上配置的CDN加速域名比如:certd.docmirror.cn
order: 0
certRegion:
title: 证书所在地域
helper: cn-hangzhou和ap-southeast-1默认cn-hangzhou。国际站用户建议使用ap-southeast-1。
@@ -106,7 +77,49 @@ input:
title: 证书名称
helper: 上传后将以此名称作为前缀备注
order: 0
output: {}
domainMatchMode:
title: 域名匹配模式
helper: 根据证书匹配根据证书域名自动匹配DCDN加速域名自动部署新增加速域名自动感知自动新增部署
component:
name: a-select
options:
- label: 手动选择
value: manual
- label: 根据证书匹配
value: auto
value: manual
order: 0
domainName:
title: CDN加速域名
component:
name: remote-select
vModel: value
mode: tags
type: plugin
typeName: DeployCertToAliyunCDN
action: onGetDomainList
search: false
pager: true
multi: true
watches:
- certDomains
- accessId
- certDomains
- accessId
required: true
mergeScript: |2-
return {
show: ctx.compute(({form})=>{
return form.domainMatchMode === "manual"
})
}
helper: 你在阿里云上配置的CDN加速域名比如:certd.docmirror.cn
order: 0
output:
deployedList:
title: 已部署过的DCDN加速域名
pluginType: deploy
type: builtIn
scriptFilePath: /plugins/plugin-aliyun/plugin/deploy-to-cdn/index.js

View File

@@ -7,6 +7,7 @@ title: 阿里云-部署证书至DCDN
icon: svg:icon-aliyun
group: aliyun
desc: 依赖证书申请前置任务自动部署域名证书至阿里云DCDN
runStrategy: 0
input:
cert:
title: 域名证书
@@ -47,6 +48,18 @@ input:
title: 证书名称
helper: 上传后将以此名称作为前缀备注
order: 0
domainMatchMode:
title: 域名匹配模式
helper: 根据证书匹配根据证书域名自动匹配DCDN加速域名自动部署新增加速域名自动感知自动新增部署
component:
name: a-select
options:
- label: 手动选择
value: manual
- label: 根据证书匹配
value: auto
value: manual
order: 0
domainName:
title: DCDN加速域名
component:
@@ -56,7 +69,7 @@ input:
type: plugin
action: onGetDomainList
search: false
pager: false
pager: true
multi: true
watches:
- certDomains
@@ -66,17 +79,17 @@ input:
required: true
mergeScript: |2-
return {
component:{
form: ctx.compute(({form})=>{
return form
})
},
}
return {
show: ctx.compute(({form})=>{
return form.domainMatchMode === "manual"
})
}
helper: 你在阿里云上配置的DCDN加速域名比如:certd.docmirror.cn
order: 0
output: {}
output:
deployedList:
title: 已部署过的DCDN加速域名
pluginType: deploy
type: builtIn
scriptFilePath: /plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.js

View File

@@ -0,0 +1,110 @@
showRunStrategy: false
default:
strategy:
runStrategy: 1
name: DeployCertToBaiduCce
title: 百度云-部署到CCE
icon: ant-design:cloud-outlined
desc: 部署到百度云CCE集群Ingress等通过Secret管理证书的应用
group: baidu
needPlus: true
input:
cert:
title: 域名证书
helper: 请选择前置任务输出的域名证书
component:
name: output-selector
from:
- ':cert:'
required: true
order: 0
accessId:
title: Access授权
helper: 百度云授权AccessKey、SecretKey
component:
name: access-selector
type: baidu
required: true
order: 0
regionId:
title: 大区
component:
name: a-auto-complete
vModel: value
options:
- value: bj
label: 北京
- value: gz
label: 广州
- value: su
label: 苏州
- value: bd
label: 保定
- value: fwh
label: 武汉
- value: hkg
label: 香港
- value: yq
label: 阳泉
- value: cd
label: 成都
- value: nj
label: 南京
placeholder: 集群所属大区
required: true
order: 0
clusterId:
title: 集群id
component:
placeholder: 集群id
required: true
order: 0
secretName:
title: 保密字典Id
component:
placeholder: 保密字典Id
helper: 原本存储证书的secret的name
required: true
order: 0
namespace:
title: 命名空间
value: default
component:
placeholder: 命名空间
required: true
order: 0
kubeconfigType:
title: Kubeconfig类型
value: public
component:
name: a-auto-complete
vModel: value
options:
- value: vpc
label: VPC私网IP (BLB VPCIP)
- value: public
label: 公网IP (BLB EIP)
placeholder: 选择集群连接端点类型
helper: VPC类型使用私网IP连接需要certd运行在同一网络环境public类型使用公网IP连接
required: true
order: 0
skipTLSVerify:
title: 忽略证书校验
required: false
helper: 是否忽略证书校验
component:
name: a-switch
vModel: checked
order: 0
createOnNotFound:
title: Secret自动创建
helper: 如果Secret不存在则创建百度云的自动创建secret有问题
value: false
component:
name: a-switch
vModel: checked
order: 0
output: {}
pluginType: deploy
type: builtIn
scriptFilePath: /plugins/plugin-plus/baidu/plugins/plugin-deploy-to-cce.js

View File

@@ -35,6 +35,19 @@ input:
type: k8s
required: true
order: 0
strategy:
title: 应用策略
helper: 选择使用apply创建或更新还是patch补丁更新
component:
name: a-select
options:
- label: apply(创建)
value: apply
- label: patch(更新)
value: patch
value: apply
required: true
order: 0
yamlContent:
title: yaml
required: true

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/ui-server",
"version": "1.39.7",
"version": "1.39.8",
"description": "fast-server base midway",
"private": true,
"type": "module",
@@ -50,20 +50,20 @@
"@aws-sdk/client-route-53": "^3.964.0",
"@aws-sdk/client-s3": "^3.964.0",
"@aws-sdk/client-sts": "^3.990.0",
"@certd/acme-client": "^1.39.7",
"@certd/basic": "^1.39.7",
"@certd/commercial-core": "^1.39.7",
"@certd/acme-client": "^1.39.8",
"@certd/basic": "^1.39.8",
"@certd/commercial-core": "^1.39.8",
"@certd/cv4pve-api-javascript": "^8.4.2",
"@certd/jdcloud": "^1.39.7",
"@certd/lib-huawei": "^1.39.7",
"@certd/lib-k8s": "^1.39.7",
"@certd/lib-server": "^1.39.7",
"@certd/midway-flyway-js": "^1.39.7",
"@certd/pipeline": "^1.39.7",
"@certd/plugin-cert": "^1.39.7",
"@certd/plugin-lib": "^1.39.7",
"@certd/plugin-plus": "^1.39.7",
"@certd/plus-core": "^1.39.7",
"@certd/jdcloud": "^1.39.8",
"@certd/lib-huawei": "^1.39.8",
"@certd/lib-k8s": "^1.39.8",
"@certd/lib-server": "^1.39.8",
"@certd/midway-flyway-js": "^1.39.8",
"@certd/pipeline": "^1.39.8",
"@certd/plugin-cert": "^1.39.8",
"@certd/plugin-lib": "^1.39.8",
"@certd/plugin-plus": "^1.39.8",
"@certd/plus-core": "^1.39.8",
"@google-cloud/publicca": "^1.3.0",
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.185",
"@huaweicloud/huaweicloud-sdk-core": "^3.1.185",

View File

@@ -31,7 +31,7 @@ export class UserTwoFactorSettingController extends BaseController {
@Post("/save", { description: Constants.per.authOnly, summary: "保存双因子认证设置" })
async save(@Body(ALL) bean: any) {
if (!isPlus()) {
throw new Error('本功能需要开通专业版')
throw new Error('本功能需要开通Certd专业版')
}
const userId = this.getUserId();
const setting = new UserTwoFactorSetting();
@@ -57,7 +57,7 @@ export class UserTwoFactorSettingController extends BaseController {
@Post("/authenticator/save", { description: Constants.per.authOnly, summary: "保存验证器设置" })
async authenticatorSave(@Body(ALL) bean: any) {
if (!isPlus()) {
throw new Error('本功能需要开通专业版')
throw new Error('本功能需要开通Certd专业版')
}
const userId = this.getUserId();
await this.twoFactorService.saveAuthenticator({

View File

@@ -81,7 +81,7 @@ export class UserSettingsController extends CrudController<UserSettingsService>
@Post("/grant/save", { description: Constants.per.authOnly, summary: "保存授权设置" })
async grantSettingsSave(@Body(ALL) bean: UserGrantSetting) {
if (!isPlus()) {
throw new Error('本功能需要开通专业版')
throw new Error('本功能需要开通Certd专业版')
}
const userId = this.getUserId();
const setting = new UserGrantSetting();

View File

@@ -180,7 +180,7 @@ export class LoginService {
async loginByTwoFactor(req: { loginId: string; verifyCode: string }) {
//检查是否开启多重认证
if (!isPlus()) {
throw new Error('本功能需要开通专业版')
throw new Error('本功能需要开通Certd专业版')
}
const userId = cache.get(`login_2fa_code:${req.loginId}`)
if (!userId) {

View File

@@ -83,7 +83,7 @@ export class NotificationService extends BaseService<NotificationEntity> {
const define = this.getDefineByType(type)
//@ts-ignore
if (define.needPlus && !isPlus()) {
throw new NeedVIPException("此通知类型为专业版功能,请升级到专业版或以上级别");
throw new NeedVIPException("此通知类型为Certd专业版功能,请升级到专业版或以上级别");
}
}

View File

@@ -941,7 +941,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
async batchDelete(ids: number[], userId?: number, projectId?: number) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
throw new NeedVIPException("此功能需要升级Certd专业版");
}
for (const id of ids) {
if (userId && userId > 0) {
@@ -956,7 +956,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
async batchUpdateGroup(ids: number[], groupId: number, userId: any, projectId?: number) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
throw new NeedVIPException("此功能需要升级Certd专业版");
}
const query: any = {}
if (userId && userId > 0) {
@@ -982,7 +982,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
*/
async batchTransfer(ids: number[], projectId: number) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
throw new NeedVIPException("此功能需要升级Certd专业版");
}
if (!isEnterprise()) {
throw new Error("当前为非企业模式,不允许转移到其他项目");
@@ -1075,7 +1075,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
async batchUpdateTrigger(ids: number[], trigger: any, userId: any, projectId?: number) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
throw new NeedVIPException("此功能需要升级Certd专业版");
}
//允许管理员修改userId=null
const query: any = {}
@@ -1128,7 +1128,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
async batchUpdateNotifications(ids: number[], notification: Notification, userId: any, projectId?: number) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
throw new NeedVIPException("此功能需要升级Certd专业版");
}
//允许管理员修改userId=null
const query: any = {}
@@ -1167,7 +1167,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
async batchRerun(ids: number[], force: boolean, userId: any, projectId?: number) {
if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版");
throw new NeedVIPException("此功能需要升级Certd专业版");
}
//允许userId为空为空则为管理员触发
if (ids.length === 0) {

View File

@@ -1,8 +1,8 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { optionsUtils } from '@certd/basic';
import { AbstractTaskPlugin, CertTargetItem, IsTaskPlugin, Pager, PageSearch, pluginGroups, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
import { CertApplyPluginNames, CertReader } from "@certd/plugin-cert";
import { CertInfo, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { AliyunAccess } from "../../../plugin-lib/aliyun/access/index.js";
import { optionsUtils } from '@certd/basic';
import { CertApplyPluginNames, CertReader } from "@certd/plugin-cert";
import { AliyunClient, AliyunSslClient, CasCertId } from "../../../plugin-lib/aliyun/lib/index.js";
@IsTaskPlugin({
name: 'DeployCertToAliyunCDN',
@@ -10,11 +10,12 @@ import { AliyunClient, AliyunSslClient, CasCertId } from "../../../plugin-lib/al
icon: 'svg:icon-aliyun',
group: pluginGroups.aliyun.key,
desc: '自动部署域名证书至阿里云CDN',
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
runStrategy: RunStrategy.AlwaysRun,
// default: {
// strategy: {
// runStrategy: RunStrategy.SkipWhenSucceed,
// },
// },
})
export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
@TaskInput({
@@ -40,10 +41,10 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
name: 'output-selector',
from: [...CertApplyPluginNames, 'uploadCertToAliyun'],
},
template:false,
template: false,
required: true,
})
cert!: CertInfo | CasCertId |number;
cert!: CertInfo | CasCertId | number;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@@ -59,32 +60,20 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
})
accessId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: 'CDN加速域名',
helper: '你在阿里云上配置的CDN加速域名比如:certd.docmirror.cn',
typeName: 'DeployCertToAliyunCDN',
action: DeployCertToAliyunCDN.prototype.onGetDomainList.name,
watches: ['certDomains', 'accessId'],
required: true,
})
)
domainName!: string | string[];
@TaskInput({
title: '证书所在地域',
helper: 'cn-hangzhou和ap-southeast-1默认cn-hangzhou。国际站用户建议使用ap-southeast-1。',
value:"cn-hangzhou",
value: "cn-hangzhou",
component: {
name: 'a-select',
options:[
{value:'cn-hangzhou',label:'中国大陆'},
{value:'ap-southeast-1',label:'新加坡'}
options: [
{ value: 'cn-hangzhou', label: '中国大陆' },
{ value: 'ap-southeast-1', label: '新加坡' }
]
},
required: true,
})
certRegion:string
certRegion: string
@TaskInput({
title: '证书名称',
@@ -93,57 +82,131 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
certName!: string;
@TaskInput({
title: '域名匹配模式',
helper: '根据证书匹配根据证书域名自动匹配DCDN加速域名自动部署新增加速域名自动感知自动新增部署',
component: {
name: 'a-select',
options: [
{ label: '手动选择', value: 'manual' },
{ label: '根据证书匹配', value: 'auto' },
],
},
value: 'manual',
})
domainMatchMode!: 'manual' | 'auto';
async onInstance() {}
async execute(): Promise<void> {
@TaskInput(
createRemoteSelectInputDefine({
title: 'CDN加速域名',
helper: '你在阿里云上配置的CDN加速域名比如:certd.docmirror.cn',
typeName: 'DeployCertToAliyunCDN',
action: DeployCertToAliyunCDN.prototype.onGetDomainList.name,
watches: ['certDomains', 'accessId'],
required: true,
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.domainMatchMode === "manual"
})
}
`,
pager:true,
})
)
domainName!: string | string[];
@TaskOutput({
title: '已部署过的DCDN加速域名',
})
deployedList!: string[];
async onInstance() { }
async execute(): Promise<any> {
this.logger.info('开始部署证书到阿里云cdn');
const access = await this.getAccess<AliyunAccess>(this.accessId);
if (this.cert == null) {
throw new Error('域名证书参数为空,请检查前置任务')
}
const client = await this.getClient(access);
const sslClient = new AliyunSslClient({
access,
logger: this.logger,
endpoint: this.endpoint || 'cas.aliyuncs.com',
});
if(this.cert == null){
throw new Error('域名证书参数为空,请检查前置任务')
if (this.domainMatchMode === 'auto') {
const { result, deployedList } = await this.autoMatchedDeploy({
targetName: 'DCDN加速域名',
uploadCert: async () => {
return await sslClient.uploadCertOrGet(this.cert);
},
deployOne: async (req: { target: CertTargetItem, cert: any }) => {
return await this.deployOne(client, req.target.value, req.cert);
},
getCertDomains:async ()=>{
return sslClient.getCertDomains(this.cert);
},
getDeployTargetList: this.onGetDomainList.bind(this)
});
this.deployedList = deployedList;
return result;
} else {
if (this.isNotChanged()) {
this.logger.info('输入参数未变更,跳过');
return "skip";
}
const certId = await this.getOrUploadCasCert(sslClient);
if (typeof this.domainName === 'string') {
this.domainName = [this.domainName];
}
for (const domain of this.domainName) {
await this.deployOne(client, domain, certId );
}
}
this.logger.info('部署完成');
}
async getOrUploadCasCert(sslClient: AliyunSslClient) {
let certId: any = this.cert;
let certName = this.appendTimeSuffix(this.certName);
let certName = this.appendTimeSuffix(this.certName);
if (typeof this.cert === 'object') {
const certInfo = this.cert as CertInfo;
const casCert = this.cert as CasCertId;
if (casCert.certId) {
certId = casCert.certId;
} else if (certInfo.crt) {
certName = this.buildCertName(CertReader.getMainDomain(certInfo.crt))
certName = CertReader.buildCertName(certInfo);
const certIdRes = await sslClient.uploadCertificate({
name:certName,
name: certName,
cert: certInfo,
});
certId = certIdRes.certId as any;
}else{
throw new Error('证书格式错误'+JSON.stringify(this.cert));
} else {
throw new Error('证书格式错误' + JSON.stringify(this.cert));
}
}
const client = await this.getClient(access);
if (typeof this.domainName === 'string') {
this.domainName = [this.domainName];
}
for (const domain of this.domainName) {
await this.SetCdnDomainSSLCertificate(client, {
CertId: certId,
DomainName: domain,
CertName: certName,
CertRegion:this.certRegion || 'cn-hangzhou',
});
return {
certId,
certName,
}
}
this.logger.info('部署完成');
async deployOne(client: any, domain: string, cert: any ) {
const { certId, certName } = cert;
await this.SetCdnDomainSSLCertificate(client, {
CertId: certId,
DomainName: domain,
CertName: certName,
CertRegion: this.certRegion || 'cn-hangzhou',
});
}
async getClient(access: AliyunAccess) {
@@ -157,8 +220,8 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
return client;
}
async SetCdnDomainSSLCertificate(client: any, params: { CertId: number; DomainName: string,CertName:string,CertRegion:string }) {
this.logger.info('设置CDN: ',JSON.stringify(params));
async SetCdnDomainSSLCertificate(client: any, params: { CertId: number; DomainName: string, CertName: string, CertRegion: string }) {
this.logger.info('设置CDN: ', JSON.stringify(params));
const requestOption = {
method: 'POST',
formatParams: false,
@@ -168,7 +231,7 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
'SetCdnDomainSSLCertificate',
{
SSLProtocol: 'on',
CertType:"cas",
CertType: "cas",
...params,
},
requestOption
@@ -183,7 +246,7 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
}
}
async onGetDomainList(data: any) {
async onGetDomainList(data: PageSearch) {
if (!this.accessId) {
throw new Error('请选择Access授权');
}
@@ -191,9 +254,11 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
const client = await this.getClient(access);
const pager = new Pager(data)
const params = {
// 'DomainName': 'aaa',
PageSize: 500,
PageSize: pager.pageSize || 100,
PageNumber: pager.pageNo || 1,
};
const requestOption = {
@@ -205,8 +270,12 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
this.checkRet(res);
const pageData = res?.Domains?.PageData;
if (!pageData || pageData.length === 0) {
throw new Error('找不到CDN域名您可以手动输入');
return {
list: [],
total: 0,
};
}
const total = res?.Domains?.TotalCount || 0;
const options = pageData.map((item: any) => {
return {
value: item.DomainName,
@@ -214,7 +283,10 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin {
domain: item.DomainName,
};
});
return optionsUtils.buildGroupOptions(options, this.certDomains);
return {
list: optionsUtils.buildGroupOptions(options, this.certDomains),
total,
};
}
}
new DeployCertToAliyunCDN();

View File

@@ -1,26 +1,26 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import dayjs from 'dayjs';
import { AbstractTaskPlugin, CertTargetItem, IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput, TaskOutput } from '@certd/pipeline';
import {
createCertDomainGetterInputDefine,
createRemoteSelectInputDefine
} from "@certd/plugin-lib";
import dayjs from 'dayjs';
import { AliyunAccess } from "../../../plugin-lib/aliyun/access/index.js";
import { CertInfo } from '@certd/plugin-cert';
import { CertApplyPluginNames } from '@certd/plugin-cert';
import { optionsUtils } from "@certd/basic";
import { AliyunClient, CasCertId } from "../../../plugin-lib/aliyun/lib/index.js";
import { CertApplyPluginNames, CertInfo } from '@certd/plugin-cert';
import { AliyunClient, AliyunSslClient, CasCertId } from "../../../plugin-lib/aliyun/lib/index.js";
@IsTaskPlugin({
name: 'DeployCertToAliyunDCDN',
title: '阿里云-部署证书至DCDN',
icon: 'svg:icon-aliyun',
group: pluginGroups.aliyun.key,
desc: '依赖证书申请前置任务自动部署域名证书至阿里云DCDN',
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
runStrategy: RunStrategy.AlwaysRun,
// default: {
// strategy: {
// runStrategy: RunStrategy.SkipWhenSucceed,
// },
// },
})
export class DeployCertToAliyunDCDN extends AbstractTaskPlugin {
@TaskInput({
@@ -55,6 +55,19 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin {
})
certName!: string;
@TaskInput({
title: '域名匹配模式',
helper: '根据证书匹配根据证书域名自动匹配DCDN加速域名自动部署新增加速域名自动感知自动新增部署',
component: {
name: 'a-select',
options: [
{ label: '手动选择', value: 'manual' },
{ label: '根据证书匹配', value: 'auto' },
],
},
value: 'manual',
})
domainMatchMode!: 'manual' | 'auto';
@TaskInput(
createRemoteSelectInputDefine({
@@ -63,33 +76,78 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin {
action: DeployCertToAliyunDCDN.prototype.onGetDomainList.name,
watches: ['certDomains', 'accessId'],
required: true,
pager:true,
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.domainMatchMode === "manual"
})
}
`,
})
)
domainName!: string | string[];
@TaskOutput({
title: '已部署过的DCDN加速域名',
})
deployedList!: string[];
async onInstance() { }
async execute(): Promise<void> {
async execute(): Promise<any> {
this.logger.info('开始部署证书到阿里云DCDN');
if (!this.domainName) {
throw new Error('您还未选择DCDN域名');
}
const access = (await this.getAccess(this.accessId)) as AliyunAccess;
const client = await this.getClient(access);
if (typeof this.domainName === 'string') {
this.domainName = [this.domainName];
}
for (const domainName of this.domainName) {
this.logger.info(`[${domainName}]开始部署`)
const params = await this.buildParams(domainName);
await this.doRequest(client, params);
await this.ctx.utils.sleep(1000);
this.logger.info(`[${domainName}]部署成功`)
const sslClient = new AliyunSslClient({ access, logger: this.logger });
if (this.domainMatchMode === 'auto') {
const { result, deployedList } = await this.autoMatchedDeploy({
targetName: 'CDN加速域名',
uploadCert: async () => {
return await sslClient.uploadCertOrGet(this.cert);
},
deployOne: async (req: { target: CertTargetItem, cert: any }) => {
return await this.deployOne(client, req.target.value, req.cert);
},
getCertDomains: async ()=>{
return sslClient.getCertDomains(this.cert);
},
getDeployTargetList: this.onGetDomainList.bind(this)
});
this.deployedList = deployedList;
return result;
} else {
if (this.isNotChanged()) {
this.logger.info('输入参数未变更,跳过');
return "skip";
}
if (!this.domainName) {
throw new Error('您还未选择DCDN域名');
}
let domains: string[] = [];
domains = typeof this.domainName === 'string' ? [this.domainName] : this.domainName;
const aliCrtId = await sslClient.uploadCertOrGet(this.cert);
for (const domainName of domains) {
await this.deployOne(client, domainName, aliCrtId);
}
}
this.logger.info('部署完成');
}
async deployOne(client: any, domainName: string, aliCrtId: CasCertId) {
this.logger.info(`[${domainName}]开始部署`)
const params = await this.buildParams(domainName, aliCrtId);
await this.doRequest(client, params);
await this.ctx.utils.sleep(1000);
this.logger.info(`[${domainName}]部署成功`)
}
async getClient(access: AliyunAccess) {
const client = new AliyunClient({ logger: this.logger });
await client.init({
@@ -101,30 +159,9 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin {
return client;
}
async buildParams(domainName: string) {
async buildParams(domainName: string, aliCrtId: CasCertId) {
const CertName = (this.certName ?? 'certd') + '-' + dayjs().format('YYYYMMDDHHmmss');
let certId: any = this.cert
if (typeof this.cert === 'object') {
const certInfo = this.cert as CertInfo;
const casCertId = this.cert as CasCertId;
if (certInfo.crt) {
this.logger.info('上传证书:', CertName);
const cert: any = this.cert;
return {
DomainName: domainName,
SSLProtocol: 'on',
CertName: CertName,
CertType: 'upload',
SSLPub: cert.crt,
SSLPri: cert.key,
};
}else if (casCertId.certId){
certId = casCertId.certId;
}else{
throw new Error('证书格式错误'+JSON.stringify(this.cert));
}
}
const certId = aliCrtId.certId;
this.logger.info('使用已上传的证书:', certId);
return {
DomainName: domainName,
@@ -152,7 +189,7 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin {
}
async onGetDomainList(data: any) {
async onGetDomainList(data: PageSearch): Promise<{ list: CertTargetItem[], total: number }> {
if (!this.accessId) {
throw new Error('请选择Access授权');
}
@@ -161,7 +198,7 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin {
const client = await this.getClient(access);
const params = {
// 'DomainName': 'aaa',
PageNumber: data.pageNo || 1,
PageSize: 500,
};
@@ -172,10 +209,9 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin {
const res = await client.request('DescribeDcdnUserDomains', params, requestOption);
this.checkRet(res);
const pageData = res?.Domains?.PageData;
if (!pageData || pageData.length === 0) {
throw new Error('找不到CDN域名您可以手动输入');
}
const pageData = res?.Domains?.PageData || [];
const total = res?.Domains?.TotalCount || 0;
const options = pageData.map((item: any) => {
return {
value: item.DomainName,
@@ -183,7 +219,11 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin {
domain: item.DomainName,
};
});
return optionsUtils.buildGroupOptions(options, this.certDomains);
return {
list: optionsUtils.buildGroupOptions(options, this.certDomains),
total: total,
};
}
}
new DeployCertToAliyunDCDN();

View File

@@ -99,11 +99,13 @@ export class UploadCertToAliyun extends AbstractTaskPlugin {
endpoint,
});
let certName = ""
const certReader = new CertReader(this.cert);
if (this.name){
certName = this.appendTimeSuffix(this.name)
}else {
certName = this.buildCertName(CertReader.getMainDomain(this.cert.crt))
certName = this.buildCertName(certReader.getMainDomain())
}
const certIdRes = await client.uploadCertificate({
name: certName,
cert: this.cert,

View File

@@ -256,7 +256,7 @@ export class AcmeService {
}
if (providers.domainsVerifyPlan) {
//按照计划执行
const domainVerifyPlan = providers.domainsVerifyPlan[origFullDomain];
const domainVerifyPlan = providers.domainsVerifyPlan[origFullDomain] || providers.domainsVerifyPlan[fullDomain];
if (domainVerifyPlan) {
if (domainVerifyPlan.type === "dns") {
checkIpChallenge("dns");

View File

@@ -1,7 +1,7 @@
import { ILogger, utils } from "@certd/basic";
import { AliyunAccess } from "../access/index.js";
import { AliyunClient } from "./index.js";
import { CertInfo, CertReader } from "@certd/plugin-lib";
import { CertInfo, CertReader, SimpleCertDetail } from "@certd/plugin-lib";
export type AliyunCertInfo = {
crt: string; //fullchain证书
@@ -37,6 +37,7 @@ export type CasCertId = {
certId: number;
certIdentifier: string;
certName: string;
detail?: SimpleCertDetail;
}
export class AliyunSslClient {
opts: AliyunSslClientOpts;
@@ -119,10 +120,12 @@ export class AliyunSslClient {
this.checkRet(ret);
this.opts.logger.info("证书上传成功aliyunCertId=", ret.CertId);
//output
const certReader = new CertReader(req.cert as any);
return {
certId: ret.CertId,
certName: req.name,
certIdentifier: this.getCertIdentifier(ret.CertId),
detail:certReader.getSimpleDetail(),
}
}
@@ -136,7 +139,8 @@ export class AliyunSslClient {
const certInfo = cert as CertInfo;
// 上传证书到阿里云
this.logger.info(`开始上传证书`);
const certName = CertReader.buildCertName(certInfo);
const certReader = new CertReader(certInfo);
const certName = certReader.buildCertName();
const res = await this.uploadCertificate({
name: certName,
cert: certInfo
@@ -151,7 +155,7 @@ export class AliyunSslClient {
return {
certId,
certIdentifier,
certName
certName,
}
}
@@ -232,4 +236,19 @@ export class AliyunSslClient {
}
return region;
}
getCertDomains(cert: CertInfo | number | CasCertId): string[]{
const casCert = cert as CasCertId;
const certInfo = cert as CertInfo;
if (casCert.certId) {
if (!casCert.detail){
throw new Error('未获取到证书域名列表,请尝试强制重新运行一下流水线');
}
return casCert.detail?.domains || [];
}else if (certInfo.crt){
return new CertReader(certInfo).getSimpleDetail().domains || [];
}else{
throw new Error('未获取到证书域名列表,请尝试强制重新运行一下流水线');
}
}
}

View File

@@ -1,3 +1,4 @@
export * from "./plugin-deploy-to-cdn.js";
export * from "./plugin-deploy-to-blb.js";
export * from "./plugin-upload-to-baidu.js";
export * from "./plugin-deploy-to-cce.js";

View File

@@ -0,0 +1,245 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { utils } from "@certd/basic";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { BaiduAccess } from "../access.js";
import { BaiduYunClient } from "../client.js";
@IsTaskPlugin({
name: "DeployCertToBaiduCce",
title: "百度云-部署到CCE",
icon: "ant-design:cloud-outlined",
desc: "部署到百度云CCE集群Ingress等通过Secret管理证书的应用",
group: pluginGroups.baidu.key,
needPlus: true,
input: {},
output: {},
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class DeployCertToBaiduCcePlugin extends AbstractTaskPlugin {
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames],
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: "Access授权",
helper: "百度云授权AccessKey、SecretKey",
component: {
name: "access-selector",
type: "baidu",
},
required: true,
})
accessId!: string;
@TaskInput({
title: "大区",
component: {
name: "a-auto-complete",
vModel: "value",
options: [
{ value: "bj", label: "北京" },
{ value: "gz", label: "广州" },
{ value: "su", label: "苏州" },
{ value: "bd", label: "保定" },
{ value: "fwh", label: "武汉" },
{ value: "hkg", label: "香港" },
{ value: "yq", label: "阳泉" },
{ value: "cd", label: "成都" },
{ value: "nj", label: "南京" },
],
placeholder: "集群所属大区",
},
required: true,
})
regionId!: string;
@TaskInput({
title: "集群id",
component: {
placeholder: "集群id",
},
required: true,
})
clusterId!: string;
@TaskInput({
title: "保密字典Id",
component: {
placeholder: "保密字典Id",
},
helper: "原本存储证书的secret的name",
required: true,
})
secretName!: string | string[];
@TaskInput({
title: "命名空间",
value: "default",
component: {
placeholder: "命名空间",
},
required: true,
})
namespace = "default";
@TaskInput({
title: "Kubeconfig类型",
value: "public",
component: {
name: "a-auto-complete",
vModel: "value",
options: [
{ value: "vpc", label: "VPC私网IP (BLB VPCIP)" },
{ value: "public", label: "公网IP (BLB EIP)" },
],
placeholder: "选择集群连接端点类型",
},
helper: "VPC类型使用私网IP连接需要certd运行在同一网络环境public类型使用公网IP连接",
required: true,
})
kubeconfigType!: string;
@TaskInput({
title: "忽略证书校验",
required: false,
helper: "是否忽略证书校验",
component: {
name: "a-switch",
vModel: "checked",
},
})
skipTLSVerify!: boolean;
@TaskInput({
title: "Secret自动创建",
helper: "如果Secret不存在则创建百度云的自动创建secret有问题",
value: false,
component: {
name: "a-switch",
vModel: "checked",
},
})
createOnNotFound: boolean;
K8sClient: any;
async onInstance() {
const sdk = await import("@certd/lib-k8s");
this.K8sClient = sdk.K8sClient;
}
async execute(): Promise<void> {
this.logger.info("开始部署证书到百度云CCE");
const { regionId, clusterId, kubeconfigType, cert } = this;
const access = (await this.getAccess(this.accessId)) as BaiduAccess;
const client = new BaiduYunClient({
access,
logger: this.logger,
http: this.ctx.http,
});
const kubeConfigStr = await this.getKubeConfig(client, clusterId, regionId, kubeconfigType);
this.logger.info("kubeconfig已成功获取");
const k8sClient = new this.K8sClient({
kubeConfigStr,
logger: this.logger,
skipTLSVerify: this.skipTLSVerify,
});
await this.patchCertSecret({ cert, k8sClient });
await utils.sleep(5000);
try {
await this.restartIngress({ k8sClient });
} catch (e) {
this.logger.warn(`重启ingress失败:${e.message}`);
}
}
async restartIngress(options: { k8sClient: any }) {
const { k8sClient } = options;
const { namespace } = this;
const body = {
metadata: {
labels: {
certd: this.appendTimeSuffix("certd"),
},
},
};
const ingressList = await k8sClient.getIngressList({ namespace });
this.logger.info("ingressList:", JSON.stringify(ingressList));
if (!ingressList || !ingressList.items) {
return;
}
const ingressNames = ingressList.items
.filter((item: any) => {
if (!item.spec.tls) {
return false;
}
for (const tls of item.spec.tls) {
if (tls.secretName === this.secretName) {
return true;
}
}
return false;
})
.map((item: any) => {
return item.metadata.name;
});
for (const ingress of ingressNames) {
await k8sClient.patchIngress({ namespace, ingressName: ingress, body, createOnNotFound: this.createOnNotFound });
this.logger.info(`ingress已重启:${ingress}`);
}
}
async patchCertSecret(options: { cert: CertInfo; k8sClient: any }) {
const { cert, k8sClient } = options;
const crt = cert.crt;
const key = cert.key;
const crtBase64 = Buffer.from(crt).toString("base64");
const keyBase64 = Buffer.from(key).toString("base64");
const { namespace, secretName } = this;
const body = {
data: {
"tls.crt": crtBase64,
"tls.key": keyBase64,
},
metadata: {
labels: {
certd: this.appendTimeSuffix("certd"),
},
},
};
let secretNames: any = secretName;
if (typeof secretName === "string") {
secretNames = [secretName];
}
for (const secret of secretNames) {
await k8sClient.patchSecret({ namespace, secretName: secret, body ,createOnNotFound: this.createOnNotFound});
this.logger.info(`cert secret已更新: ${secret}`);
}
}
async getKubeConfig(client: BaiduYunClient, clusterId: string, regionId: string, kubeconfigType: string) {
const res = await client.doRequest({
host: `cce.${regionId}.baidubce.com`,
uri: `/v2/kubeconfig/${clusterId}/${kubeconfigType}`,
method: "get",
});
return res.kubeConfig;
}
}
new DeployCertToBaiduCcePlugin();

View File

@@ -62,6 +62,21 @@ export class K8sApplyPlugin extends AbstractPlusTaskPlugin {
// })
// namespace!: string;
@TaskInput({
title: "应用策略",
helper: "选择使用apply创建或更新还是patch补丁更新",
component: {
name: "a-select",
options: [
{ label: "apply(创建)", value: "apply" },
{ label: "patch(更新)", value: "patch" },
],
},
value: "apply",
required: true,
})
strategy!: string;
@TaskInput({
title: "yaml",
required: true,
@@ -112,8 +127,13 @@ export class K8sApplyPlugin extends AbstractPlusTaskPlugin {
try {
// this.logger.info("apply yaml:", compiledYaml);
// this.logger.info("apply yamlDoc:", JSON.stringify(doc));
const res = await client.apply(compiledYaml);
this.logger.info("apply result:", res);
if (this.strategy === "apply") {
await client.apply(compiledYaml);
this.logger.info("apply success");
} else {
await client.applyPatch(compiledYaml);
this.logger.info("patch success");
}
} catch (e) {
if (e.response?.body) {
throw new Error(JSON.stringify(e.response.body));

88
pnpm-lock.yaml generated
View File

@@ -49,7 +49,7 @@ importers:
packages/core/acme-client:
dependencies:
'@certd/basic':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../basic
'@peculiar/x509':
specifier: ^1.11.0
@@ -213,10 +213,10 @@ importers:
packages/core/pipeline:
dependencies:
'@certd/basic':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../basic
'@certd/plus-core':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../pro/plus-core
dayjs:
specifier: ^1.11.7
@@ -412,7 +412,7 @@ importers:
packages/libs/lib-k8s:
dependencies:
'@certd/basic':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/basic
'@kubernetes/client-node':
specifier: 0.21.0
@@ -452,19 +452,19 @@ importers:
packages/libs/lib-server:
dependencies:
'@certd/acme-client':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/acme-client
'@certd/basic':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/basic
'@certd/pipeline':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/pipeline
'@certd/plugin-lib':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../plugins/plugin-lib
'@certd/plus-core':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../pro/plus-core
'@midwayjs/cache':
specifier: 3.14.0
@@ -610,16 +610,16 @@ importers:
packages/plugins/plugin-cert:
dependencies:
'@certd/acme-client':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/acme-client
'@certd/basic':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/basic
'@certd/pipeline':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/pipeline
'@certd/plugin-lib':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../plugin-lib
psl:
specifier: ^1.9.0
@@ -683,16 +683,16 @@ importers:
specifier: ^3.964.0
version: 3.964.0(aws-crt@1.26.2)
'@certd/acme-client':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/acme-client
'@certd/basic':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/basic
'@certd/pipeline':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/pipeline
'@certd/plus-core':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../pro/plus-core
'@kubernetes/client-node':
specifier: 0.21.0
@@ -783,16 +783,16 @@ importers:
packages/pro/commercial-core:
dependencies:
'@certd/basic':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/basic
'@certd/lib-server':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../libs/lib-server
'@certd/pipeline':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/pipeline
'@certd/plus-core':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../plus-core
'@midwayjs/core':
specifier: 3.20.11
@@ -868,16 +868,16 @@ importers:
packages/pro/plugin-plus:
dependencies:
'@certd/basic':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/basic
'@certd/pipeline':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/pipeline
'@certd/plugin-lib':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../plugins/plugin-lib
'@certd/plus-core':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../plus-core
crypto-js:
specifier: ^4.2.0
@@ -953,7 +953,7 @@ importers:
packages/pro/plus-core:
dependencies:
'@certd/basic':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/basic
dayjs:
specifier: ^1.11.7
@@ -1249,10 +1249,10 @@ importers:
version: 0.1.3(zod@3.24.4)
devDependencies:
'@certd/lib-iframe':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../libs/lib-iframe
'@certd/pipeline':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/pipeline
'@rollup/plugin-commonjs':
specifier: ^25.0.7
@@ -1447,46 +1447,46 @@ importers:
specifier: ^3.990.0
version: 3.990.0(aws-crt@1.26.2)
'@certd/acme-client':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/acme-client
'@certd/basic':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/basic
'@certd/commercial-core':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../pro/commercial-core
'@certd/cv4pve-api-javascript':
specifier: ^8.4.2
version: 8.4.2
'@certd/jdcloud':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../libs/lib-jdcloud
'@certd/lib-huawei':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../libs/lib-huawei
'@certd/lib-k8s':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../libs/lib-k8s
'@certd/lib-server':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../libs/lib-server
'@certd/midway-flyway-js':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../libs/midway-flyway-js
'@certd/pipeline':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../core/pipeline
'@certd/plugin-cert':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../plugins/plugin-cert
'@certd/plugin-lib':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../plugins/plugin-lib
'@certd/plugin-plus':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../pro/plugin-plus
'@certd/plus-core':
specifier: ^1.39.6
specifier: ^1.39.7
version: link:../../pro/plus-core
'@google-cloud/publicca':
specifier: ^1.3.0
@@ -20854,13 +20854,13 @@ snapshots:
resolve: 1.22.10
semver: 6.3.1
eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8):
eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8):
dependencies:
eslint: 7.32.0
prettier: 2.8.8
prettier-linter-helpers: 1.0.0
optionalDependencies:
eslint-config-prettier: 8.10.0(eslint@7.32.0)
eslint-config-prettier: 8.10.0(eslint@8.57.0)
eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8):
dependencies:
@@ -23286,7 +23286,7 @@ snapshots:
eslint: 7.32.0
eslint-config-prettier: 8.10.0(eslint@7.32.0)
eslint-plugin-node: 11.1.0(eslint@7.32.0)
eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8)
eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8)
execa: 5.1.1
inquirer: 7.3.3
json5: 2.2.3

View File

@@ -1 +1 @@
00:07
23:58

View File

@@ -1 +1 @@
00:23
00:29

View File

@@ -1,32 +0,0 @@
{
"compileOnSave": true,
"compilerOptions": {
// 这样就可以对 `this` 上的数据属性进行更严格的推断`
"noImplicitAny": true,
"allowJs": true,
"target": "esnext",
"module": "esnext",
"strict": true,
"importHelpers": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strictNullChecks" :false,
"inlineSourceMap": true,
"baseUrl": ".",
"outDir": "dist",
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules",
"vite.config.ts"
]
}