Merge branch 'v2-dev' into v2

pull/409/head
xiaojunnuo 2025-04-21 00:18:53 +08:00
commit b9dab77c8b
72 changed files with 1608 additions and 614 deletions

View File

@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
### Bug Fixes
* 上传商用证书直接粘贴文本报错的问题修复无法上传ec加密证书的bug ([5750bb7](https://github.com/certd/certd/commit/5750bb706779da274d8e7a87e71416cb64d2df79))
* 修复下载证书时提示token已过期的问题 ([0e07ae6](https://github.com/certd/certd/commit/0e07ae6ce84dcb9279d3c44060d621566afa593c))
### Performance Improvements
* 更新license时同时绑定url ([78367af](https://github.com/certd/certd/commit/78367af8307f801e778c76d49f0918c21ffe032f))
* 切换到不同的分组后再打开创建对话框,会自动选择分组 ([893dcd4](https://github.com/certd/certd/commit/893dcd4f2487891199ed3e5a3d47a79a75efc942))
* 新增部署到火山引擎ALB/CLB、上传到证书中心 ([c9a3e3d](https://github.com/certd/certd/commit/c9a3e3d9d26f964c7af7b56667936f1414fbf42a))
* 优化/api缓存为0 ([dc05cd4](https://github.com/certd/certd/commit/dc05cd481f186b13375192be965000e6b4b429a5))
* 优化华为cdn插件引用ccm证书 ([b565b4b](https://github.com/certd/certd/commit/b565b4b3b919b71b98ea2517670bc1ef00e00dc9))
* 优化证书流水线创建,支持选择分组 ([d613aa8](https://github.com/certd/certd/commit/d613aa8f3e85d8dc475ef1b62d49394ce7fd7d24))
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
### Performance Improvements ### Performance Improvements

View File

@ -1 +1 @@
23:37 00:08

View File

@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
### Bug Fixes
* 上传商用证书直接粘贴文本报错的问题修复无法上传ec加密证书的bug ([5750bb7](https://github.com/certd/certd/commit/5750bb706779da274d8e7a87e71416cb64d2df79))
* 修复下载证书时提示token已过期的问题 ([0e07ae6](https://github.com/certd/certd/commit/0e07ae6ce84dcb9279d3c44060d621566afa593c))
### Performance Improvements
* 更新license时同时绑定url ([78367af](https://github.com/certd/certd/commit/78367af8307f801e778c76d49f0918c21ffe032f))
* 切换到不同的分组后再打开创建对话框,会自动选择分组 ([893dcd4](https://github.com/certd/certd/commit/893dcd4f2487891199ed3e5a3d47a79a75efc942))
* 新增部署到火山引擎ALB/CLB、上传到证书中心 ([c9a3e3d](https://github.com/certd/certd/commit/c9a3e3d9d26f964c7af7b56667936f1414fbf42a))
* 优化/api缓存为0 ([dc05cd4](https://github.com/certd/certd/commit/dc05cd481f186b13375192be965000e6b4b429a5))
* 优化华为cdn插件引用ccm证书 ([b565b4b](https://github.com/certd/certd/commit/b565b4b3b919b71b98ea2517670bc1ef00e00dc9))
* 优化证书流水线创建,支持选择分组 ([d613aa8](https://github.com/certd/certd/commit/d613aa8f3e85d8dc475ef1b62d49394ce7fd7d24))
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
### Performance Improvements ### Performance Improvements

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

View File

@ -13,7 +13,7 @@
#### 2.1 应用商店一键部署【推荐】 #### 2.1 应用商店一键部署【推荐】
* 在应用商店中找到`certd`(要先点右上角更新应用) * 在宝塔Docker应用商店中找到`certd`(要先点右上角更新应用)
* 点击安装,配置域名等基本信息即可完成安装 * 点击安装,配置域名等基本信息即可完成安装
> 需要宝塔9.2.0及以上版本才支持 > 需要宝塔9.2.0及以上版本才支持
@ -70,3 +70,12 @@ admin/123456
## 五、备份恢复 ## 五、备份恢复
将备份的`db.sqlite`及同目录下的其他文件一起覆盖到原来的位置重启certd即可 将备份的`db.sqlite`及同目录下的其他文件一起覆盖到原来的位置重启certd即可
## 六、宝塔部署相关问题排查
### 1. 无法访问Certd
1. 确认服务器的安全规则,是否放开了对应端口
2. 确认宝塔防火墙是否放开对应端口
3. 尝试将Certd容器加入宝塔的`bridge`网络
![](./images/network.png)

View File

@ -1,5 +1,6 @@
# 数据库自动备份 # 数据库备份
* 两种备份方法: 1、手动备份 2、自动备份
* 本文仅限sqlite数据库。
## 一、手动备份 ## 一、手动备份
数据库文件根据不同的部署方式保存的位置不一样,您可以手动复制出来进行备份 数据库文件根据不同的部署方式保存的位置不一样,您可以手动复制出来进行备份

View File

@ -9,5 +9,5 @@
} }
}, },
"npmClient": "pnpm", "npmClient": "pnpm",
"version": "1.33.5" "version": "1.33.6"
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/publishlab/node-acme-client/compare/v1.33.5...v1.33.6) (2025-04-20)
**Note:** Version bump only for package @certd/acme-client
## [1.33.5](https://github.com/publishlab/node-acme-client/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/publishlab/node-acme-client/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/acme-client **Note:** Version bump only for package @certd/acme-client

View File

@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client", "description": "Simple and unopinionated ACME client",
"private": false, "private": false,
"author": "nmorsman", "author": "nmorsman",
"version": "1.33.5", "version": "1.33.6",
"type": "module", "type": "module",
"module": "scr/index.js", "module": "scr/index.js",
"main": "src/index.js", "main": "src/index.js",
@ -18,7 +18,7 @@
"types" "types"
], ],
"dependencies": { "dependencies": {
"@certd/basic": "^1.33.5", "@certd/basic": "^1.33.6",
"@peculiar/x509": "^1.11.0", "@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5", "asn1js": "^3.0.5",
"axios": "^1.7.2", "axios": "^1.7.2",
@ -67,5 +67,5 @@
"bugs": { "bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues" "url": "https://github.com/publishlab/node-acme-client/issues"
}, },
"gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1" "gitHead": "8abe62886ab50634b4b05abedd2981ec8ee0422e"
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
**Note:** Version bump only for package @certd/basic
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
### Performance Improvements ### Performance Improvements

View File

@ -1 +1 @@
23:32 00:04

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/basic", "name": "@certd/basic",
"private": false, "private": false,
"version": "1.33.5", "version": "1.33.6",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@ -44,5 +44,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1" "gitHead": "8abe62886ab50634b4b05abedd2981ec8ee0422e"
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
**Note:** Version bump only for package @certd/pipeline
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/pipeline **Note:** Version bump only for package @certd/pipeline

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/pipeline", "name": "@certd/pipeline",
"private": false, "private": false,
"version": "1.33.5", "version": "1.33.6",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@ -16,8 +16,8 @@
"test": "mocha --loader=ts-node/esm" "test": "mocha --loader=ts-node/esm"
}, },
"dependencies": { "dependencies": {
"@certd/basic": "^1.33.5", "@certd/basic": "^1.33.6",
"@certd/plus-core": "^1.33.5", "@certd/plus-core": "^1.33.6",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"reflect-metadata": "^0.1.13" "reflect-metadata": "^0.1.13"
@ -43,5 +43,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1" "gitHead": "8abe62886ab50634b4b05abedd2981ec8ee0422e"
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
**Note:** Version bump only for package @certd/lib-huawei
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/lib-huawei **Note:** Version bump only for package @certd/lib-huawei

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-huawei", "name": "@certd/lib-huawei",
"private": false, "private": false,
"version": "1.33.5", "version": "1.33.6",
"main": "./dist/bundle.js", "main": "./dist/bundle.js",
"module": "./dist/bundle.js", "module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts", "types": "./dist/d/index.d.ts",
@ -23,5 +23,5 @@
"prettier": "^2.8.8", "prettier": "^2.8.8",
"tslib": "^2.8.1" "tslib": "^2.8.1"
}, },
"gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1" "gitHead": "8abe62886ab50634b4b05abedd2981ec8ee0422e"
} }

View File

@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
### Performance Improvements
* 更新license时同时绑定url ([78367af](https://github.com/certd/certd/commit/78367af8307f801e778c76d49f0918c21ffe032f))
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/lib-iframe **Note:** Version bump only for package @certd/lib-iframe

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-iframe", "name": "@certd/lib-iframe",
"private": false, "private": false,
"version": "1.33.5", "version": "1.33.6",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@ -30,5 +30,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1" "gitHead": "8abe62886ab50634b4b05abedd2981ec8ee0422e"
} }

View File

@ -65,7 +65,7 @@ export class IframeClient {
return window.self !== window.top; return window.self !== window.top;
} }
register<T = any>(action: string, handler: (data: IframeMessageData<T>) => Promise<void>) { register<T = any>(action: string, handler: (data: IframeMessageData<T>) => Promise<any>) {
this.handlers[action] = handler; this.handlers[action] = handler;
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
**Note:** Version bump only for package @certd/jdcloud
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/jdcloud **Note:** Version bump only for package @certd/jdcloud

View File

@ -1,6 +1,6 @@
{ {
"name": "@certd/jdcloud", "name": "@certd/jdcloud",
"version": "1.33.5", "version": "1.33.6",
"description": "jdcloud openApi sdk", "description": "jdcloud openApi sdk",
"main": "./dist/bundle.js", "main": "./dist/bundle.js",
"module": "./dist/bundle.js", "module": "./dist/bundle.js",
@ -60,5 +60,5 @@
"fetch" "fetch"
] ]
}, },
"gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1" "gitHead": "8abe62886ab50634b4b05abedd2981ec8ee0422e"
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
**Note:** Version bump only for package @certd/lib-k8s
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/lib-k8s **Note:** Version bump only for package @certd/lib-k8s

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-k8s", "name": "@certd/lib-k8s",
"private": false, "private": false,
"version": "1.33.5", "version": "1.33.6",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@ -16,7 +16,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@certd/basic": "^1.33.5", "@certd/basic": "^1.33.6",
"@kubernetes/client-node": "0.21.0" "@kubernetes/client-node": "0.21.0"
}, },
"devDependencies": { "devDependencies": {
@ -31,5 +31,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1" "gitHead": "8abe62886ab50634b4b05abedd2981ec8ee0422e"
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
**Note:** Version bump only for package @certd/lib-server
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
### Performance Improvements ### Performance Improvements

View File

@ -1,6 +1,6 @@
{ {
"name": "@certd/lib-server", "name": "@certd/lib-server",
"version": "1.33.5", "version": "1.33.6",
"description": "midway with flyway, sql upgrade way ", "description": "midway with flyway, sql upgrade way ",
"private": false, "private": false,
"type": "module", "type": "module",
@ -27,10 +27,10 @@
], ],
"license": "AGPL", "license": "AGPL",
"dependencies": { "dependencies": {
"@certd/acme-client": "^1.33.5", "@certd/acme-client": "^1.33.6",
"@certd/basic": "^1.33.5", "@certd/basic": "^1.33.6",
"@certd/pipeline": "^1.33.5", "@certd/pipeline": "^1.33.6",
"@certd/plus-core": "^1.33.5", "@certd/plus-core": "^1.33.6",
"@midwayjs/cache": "~3.14.0", "@midwayjs/cache": "~3.14.0",
"@midwayjs/core": "~3.20.3", "@midwayjs/core": "~3.20.3",
"@midwayjs/i18n": "~3.20.3", "@midwayjs/i18n": "~3.20.3",
@ -61,5 +61,5 @@
"typeorm": "^0.3.11", "typeorm": "^0.3.11",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1" "gitHead": "8abe62886ab50634b4b05abedd2981ec8ee0422e"
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/midway-flyway-js **Note:** Version bump only for package @certd/midway-flyway-js

View File

@ -1,6 +1,6 @@
{ {
"name": "@certd/midway-flyway-js", "name": "@certd/midway-flyway-js",
"version": "1.33.5", "version": "1.33.6",
"description": "midway with flyway, sql upgrade way ", "description": "midway with flyway, sql upgrade way ",
"private": false, "private": false,
"type": "module", "type": "module",
@ -46,5 +46,5 @@
"typeorm": "^0.3.11", "typeorm": "^0.3.11",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1" "gitHead": "8abe62886ab50634b4b05abedd2981ec8ee0422e"
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
**Note:** Version bump only for package @certd/plugin-cert
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/plugin-cert **Note:** Version bump only for package @certd/plugin-cert

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/plugin-cert", "name": "@certd/plugin-cert",
"private": false, "private": false,
"version": "1.33.5", "version": "1.33.6",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@ -15,10 +15,10 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@certd/acme-client": "^1.33.5", "@certd/acme-client": "^1.33.6",
"@certd/basic": "^1.33.5", "@certd/basic": "^1.33.6",
"@certd/pipeline": "^1.33.5", "@certd/pipeline": "^1.33.6",
"@certd/plugin-lib": "^1.33.5", "@certd/plugin-lib": "^1.33.6",
"@google-cloud/publicca": "^1.3.0", "@google-cloud/publicca": "^1.3.0",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"jszip": "^3.10.1", "jszip": "^3.10.1",
@ -41,5 +41,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1" "gitHead": "8abe62886ab50634b4b05abedd2981ec8ee0422e"
} }

View File

@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
**Note:** Version bump only for package @certd/plugin-lib
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/plugin-lib **Note:** Version bump only for package @certd/plugin-lib

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/plugin-lib", "name": "@certd/plugin-lib",
"private": false, "private": false,
"version": "1.33.5", "version": "1.33.6",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@ -16,8 +16,8 @@
}, },
"dependencies": { "dependencies": {
"@alicloud/pop-core": "^1.7.10", "@alicloud/pop-core": "^1.7.10",
"@certd/basic": "^1.33.5", "@certd/basic": "^1.33.6",
"@certd/pipeline": "^1.33.5", "@certd/pipeline": "^1.33.6",
"@kubernetes/client-node": "0.21.0", "@kubernetes/client-node": "0.21.0",
"ali-oss": "^6.21.0", "ali-oss": "^6.21.0",
"basic-ftp": "^5.0.5", "basic-ftp": "^5.0.5",
@ -48,5 +48,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1" "gitHead": "8abe62886ab50634b4b05abedd2981ec8ee0422e"
} }

View File

@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
### Bug Fixes
* 上传商用证书直接粘贴文本报错的问题修复无法上传ec加密证书的bug ([5750bb7](https://github.com/certd/certd/commit/5750bb706779da274d8e7a87e71416cb64d2df79))
* 修复下载证书时提示token已过期的问题 ([0e07ae6](https://github.com/certd/certd/commit/0e07ae6ce84dcb9279d3c44060d621566afa593c))
### Performance Improvements
* 更新license时同时绑定url ([78367af](https://github.com/certd/certd/commit/78367af8307f801e778c76d49f0918c21ffe032f))
* 切换到不同的分组后再打开创建对话框,会自动选择分组 ([893dcd4](https://github.com/certd/certd/commit/893dcd4f2487891199ed3e5a3d47a79a75efc942))
* 优化/api缓存为0 ([dc05cd4](https://github.com/certd/certd/commit/dc05cd481f186b13375192be965000e6b4b429a5))
* 优化证书流水线创建,支持选择分组 ([d613aa8](https://github.com/certd/certd/commit/d613aa8f3e85d8dc475ef1b62d49394ce7fd7d24))
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
### Performance Improvements ### Performance Improvements

View File

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

View File

@ -49,33 +49,32 @@ function createService() {
} }
// 这个状态码是和后端约定的 // 这个状态码是和后端约定的
const { code } = dataAxios; if (dataAxios?.code === undefined) {
// 根据 code 进行判断
if (code === undefined) {
// 如果没有 code 代表这不是项目后端开发的接口 // 如果没有 code 代表这不是项目后端开发的接口
errorCreate(`非标准返回:${dataAxios} ${response.config.url}`); errorCreate(`非标准返回:${dataAxios} ${response.config.url}`);
return dataAxios; return dataAxios;
} else { }
// 有 code 代表这是一个后端接口 可以进行进一步的判断 const { code } = dataAxios;
switch (code) { // 有 code 代表这是一个后端接口 可以进行进一步的判断
case 0: switch (code) {
// [ 示例 ] code === 0 代表没有错误 case 0:
// [ 示例 ] code === 0 代表没有错误
// @ts-ignore
return dataAxios?.data;
default:
// 不是正确的 code
const errorMessage = dataAxios.msg || dataAxios.message || "未知错误";
// @ts-ignore
if (response?.config?.onError) {
const err = new CodeError(errorMessage, dataAxios.code, dataAxios.data);
// @ts-ignore // @ts-ignore
return dataAxios.data; response.config.onError(err);
default: return;
// 不是正确的 code }
const errorMessage = dataAxios.msg || dataAxios.message || "未知错误"; //@ts-ignore
// @ts-ignore const showErrorNotify = response?.config?.showErrorNotify;
if (response?.config?.onError) { errorCreate(`${errorMessage}: ${response.config.url}`, showErrorNotify, dataAxios);
const err = new CodeError(errorMessage, dataAxios.code, dataAxios.data); return dataAxios;
response.config.onError(err);
return;
}
//@ts-ignore
const showErrorNotify = response?.config?.showErrorNotify;
errorCreate(`${errorMessage}: ${response.config.url}`, showErrorNotify, dataAxios);
return dataAxios;
}
} }
}, },
error => { error => {

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="pem-input"> <div class="pem-input">
<FileInput v-bind="fileInput" class="mb-5" type="primary" text="选择文件" @change="onChange" /> <FileInput v-bind="fileInput" class="mb-5" type="primary" text="选择文件" @change="onChange" />
<a-textarea v-bind="textarea" v-model:value="textRef"></a-textarea> <a-textarea v-bind="textarea" :value="modelValue" @update:value="emitValue"></a-textarea>
</div> </div>
</template> </template>
@ -17,7 +17,6 @@ const props = defineProps<{
}>(); }>();
const emit = defineEmits(["update:modelValue"]); const emit = defineEmits(["update:modelValue"]);
const textRef = ref();
function emitValue(value: string) { function emitValue(value: string) {
emit("update:modelValue", value); emit("update:modelValue", value);
@ -39,16 +38,6 @@ function onChange(e: any) {
}; };
fileReader.readAsText(file); // fileReader.readAsText(file); //
} }
watch(
() => props.modelValue,
value => {
textRef.value = value;
},
{
immediate: true,
}
);
</script> </script>
<style lang="less"> <style lang="less">

View File

@ -3,12 +3,12 @@ export default {
crud: { i18n: { name: "姓名", city: "城市", status: "状态" } }, crud: { i18n: { name: "姓名", city: "城市", status: "状态" } },
login: { login: {
logoutTip: "确认", logoutTip: "确认",
logoutMessage: "确定要注销登录吗?" logoutMessage: "确定要注销登录吗?",
} },
}, },
fs: { fs: {
rowHandle: { rowHandle: {
title: "操作列" title: "操作列",
} },
} },
}; };

View File

@ -20,7 +20,7 @@ import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
defineOptions({ defineOptions({
name: "FsUserInfo" name: "FsUserInfo",
}); });
const userStore = useUserStore(); const userStore = useUserStore();
const { t } = useI18n(); const { t } = useI18n();
@ -38,7 +38,7 @@ function doLogout() {
content: t("app.login.logoutMessage"), content: t("app.login.logoutMessage"),
onOk: async () => { onOk: async () => {
await userStore.logout(true); await userStore.logout(true);
} },
}); });
} }
</script> </script>

View File

@ -30,7 +30,7 @@ const avatar = computed(() => {
}); });
async function handleLogout() { async function handleLogout() {
userStore.logout(true); await userStore.logout(true);
} }
const settingStore = useSettingStore(); const settingStore = useSettingStore();

View File

@ -10,6 +10,7 @@ import { updatePreferences } from "/@/vben/preferences";
import { useTitle } from "@vueuse/core"; import { useTitle } from "@vueuse/core";
import { utils } from "/@/utils"; import { utils } from "/@/utils";
import { cloneDeep } from "lodash-es"; import { cloneDeep } from "lodash-es";
export interface SettingState { export interface SettingState {
sysPublic?: SysPublicSetting; sysPublic?: SysPublicSetting;
installInfo?: { installInfo?: {
@ -184,6 +185,17 @@ export const useSettingStore = defineStore({
useTitle(this.siteInfo.title); useTitle(this.siteInfo.title);
} }
}, },
getBaseUrl() {
let url = window.location.href;
//只要hash前面的部分
url = url.split("#")[0];
return url;
},
async doBindUrl() {
const url = this.getBaseUrl();
await basicApi.bindUrl({ url });
await this.loadSysSettings();
},
async checkUrlBound() { async checkUrlBound() {
const userStore = useUserStore(); const userStore = useUserStore();
const settingStore = useSettingStore(); const settingStore = useSettingStore();
@ -193,22 +205,9 @@ export const useSettingStore = defineStore({
const bindUrl = this.installInfo.bindUrl; const bindUrl = this.installInfo.bindUrl;
function getBaseUrl() {
let url = window.location.href;
//只要hash前面的部分
url = url.split("#")[0];
return url;
}
const doBindUrl = async (url: string) => {
await basicApi.bindUrl({ url });
await this.loadSysSettings();
};
const baseUrl = getBaseUrl();
if (!bindUrl) { if (!bindUrl) {
//绑定url //绑定url
await doBindUrl(baseUrl); await this.doBindUrl();
} else { } else {
//检查当前url 是否与绑定的url一致 //检查当前url 是否与绑定的url一致
const url = window.location.href; const url = window.location.href;
@ -217,7 +216,7 @@ export const useSettingStore = defineStore({
title: "URL地址有变化", title: "URL地址有变化",
content: "以后都用这个新地址访问本系统吗?", content: "以后都用这个新地址访问本系统吗?",
onOk: async () => { onOk: async () => {
await doBindUrl(baseUrl); await this.doBindUrl();
}, },
okText: "是的,继续", okText: "是的,继续",
cancelText: "不是,回到原来的地址", cancelText: "不是,回到原来的地址",

View File

@ -41,6 +41,12 @@ export async function register(user: RegisterReq): Promise<UserInfoRes> {
data: user, data: user,
}); });
} }
export async function logout() {
return await request({
url: "/logout",
method: "post",
});
}
export async function login(data: LoginReq): Promise<LoginRes> { export async function login(data: LoginReq): Promise<LoginRes> {
//如果开启了登录与权限模块,则真实登录 //如果开启了登录与权限模块,则真实登录

View File

@ -108,9 +108,10 @@ export const useUserStore = defineStore({
/** /**
* @description: logout * @description: logout
*/ */
logout(goLogin = true) { async logout(goLogin = true) {
this.resetState(); this.resetState();
resetAllStores(); resetAllStores();
await UserApi.logout(); //主要是清空cookie
goLogin && router.push("/login"); goLogin && router.push("/login");
mitter.emit("app.logout"); mitter.emit("app.logout");
}, },

View File

@ -7,6 +7,7 @@ import { routerUtils } from "./util.router";
import { treeUtils } from "./util.tree"; import { treeUtils } from "./util.tree";
import { hashUtils } from "./util.hash"; import { hashUtils } from "./util.hash";
import { amountUtils } from "./util.amount"; import { amountUtils } from "./util.amount";
import { cache } from "./util.cache";
export const util = { export const util = {
...envs, ...envs,
...sites, ...sites,
@ -17,5 +18,6 @@ export const util = {
tree: treeUtils, tree: treeUtils,
hash: hashUtils, hash: hashUtils,
amount: amountUtils, amount: amountUtils,
cache,
}; };
export const utils = util; export const utils = util;

View File

@ -0,0 +1,17 @@
export class Cache {
bucket: Record<string, any> = {};
async get(key: string) {
return this.bucket[key];
}
async set(key: string, value: any, ttl?: number) {
this.bucket[key] = value;
}
async del(key: string) {
delete this.bucket[key];
}
}
export const cache = new Cache();

View File

@ -7,7 +7,7 @@ export function createNotificationApi() {
return await request({ return await request({
url: apiPrefix + "/page", url: apiPrefix + "/page",
method: "post", method: "post",
data: query data: query,
}); });
}, },
@ -15,7 +15,7 @@ export function createNotificationApi() {
return await request({ return await request({
url: apiPrefix + "/add", url: apiPrefix + "/add",
method: "post", method: "post",
data: obj data: obj,
}); });
}, },
@ -23,7 +23,7 @@ export function createNotificationApi() {
return await request({ return await request({
url: apiPrefix + "/update", url: apiPrefix + "/update",
method: "post", method: "post",
data: obj data: obj,
}); });
}, },
@ -31,7 +31,7 @@ export function createNotificationApi() {
return await request({ return await request({
url: apiPrefix + "/delete", url: apiPrefix + "/delete",
method: "post", method: "post",
params: { id } params: { id },
}); });
}, },
@ -39,14 +39,14 @@ export function createNotificationApi() {
return await request({ return await request({
url: apiPrefix + "/info", url: apiPrefix + "/info",
method: "post", method: "post",
params: { id } params: { id },
}); });
}, },
async GetOptions(id: number) { async GetOptions(id: number) {
return await request({ return await request({
url: apiPrefix + "/options", url: apiPrefix + "/options",
method: "post" method: "post",
}); });
}, },
@ -54,14 +54,14 @@ export function createNotificationApi() {
return await request({ return await request({
url: apiPrefix + "/setDefault", url: apiPrefix + "/setDefault",
method: "post", method: "post",
params: { id } params: { id },
}); });
}, },
async GetDefaultId() { async GetDefaultId() {
return await request({ return await request({
url: apiPrefix + "/getDefaultId", url: apiPrefix + "/getDefaultId",
method: "post" method: "post",
}); });
}, },
@ -69,14 +69,14 @@ export function createNotificationApi() {
return await request({ return await request({
url: apiPrefix + "/simpleInfo", url: apiPrefix + "/simpleInfo",
method: "post", method: "post",
params: { id } params: { id },
}); });
}, },
async GetDefineTypes() { async GetDefineTypes() {
return await request({ return await request({
url: apiPrefix + "/getTypeDict", url: apiPrefix + "/getTypeDict",
method: "post" method: "post",
}); });
}, },
@ -84,7 +84,7 @@ export function createNotificationApi() {
return await request({ return await request({
url: apiPrefix + "/define", url: apiPrefix + "/define",
method: "post", method: "post",
params: { type } params: { type },
}); });
}, },
@ -92,15 +92,15 @@ export function createNotificationApi() {
return await request({ return await request({
url: apiPrefix + "/defineByType", url: apiPrefix + "/defineByType",
method: "post", method: "post",
params: { type } params: { type },
}); });
}, },
async GetOrCreateDefault(param: { email: any }) { async GetOrCreateDefault(param: { email: any }) {
return await request({ return await request({
url: apiPrefix + "/getOrCreateDefault", url: apiPrefix + "/getOrCreateDefault",
method: "post", method: "post",
data: param data: param,
}); });
} },
}; };
} }

View File

@ -122,3 +122,11 @@ export async function GetCert(pipelineId: number): Promise<CertInfo> {
params: { id: pipelineId }, params: { id: pipelineId },
}); });
} }
export async function ReadCertDetail(crt: string): Promise<any> {
return await request({
url: certApiPrefix + "/readCertDetail",
method: "post",
data: { crt },
});
}

View File

@ -8,7 +8,7 @@
</template> </template>
<script lang="tsx" setup> <script lang="tsx" setup>
import { computed, inject } from "vue"; import { computed, inject, watch, ref } from "vue";
import { useCertUpload } from "./use"; import { useCertUpload } from "./use";
import { getAllDomainsFromCrt } from "/@/views/certd/pipeline/utils"; import { getAllDomainsFromCrt } from "/@/views/certd/pipeline/utils";
@ -27,19 +27,36 @@ const emit = defineEmits(["updated", "update:modelValue"]);
const { openUpdateCertDialog } = useCertUpload(); const { openUpdateCertDialog } = useCertUpload();
const domain = computed(() => { const domainsRef = ref([]);
if (!props.modelValue?.crt) {
return "";
}
const domains = getAllDomainsFromCrt(props.modelValue?.crt);
return domains[0]; watch(
() => {
return props.modelValue?.crt;
},
async crt => {
if (crt) {
domainsRef.value = await getAllDomainsFromCrt(crt);
} else {
domainsRef.value = [];
}
emit("updated", { domains: domainsRef.value });
},
{
immediate: true,
}
);
const domain = computed(() => {
if (domainsRef.value && domainsRef.value.length > 0) {
return domainsRef.value[0];
}
return "";
}); });
function onUpdated(res: { uploadCert: any }) { async function onUpdated(res: { uploadCert: any }) {
emit("update:modelValue", res.uploadCert); emit("update:modelValue", res.uploadCert);
const domains = getAllDomainsFromCrt(res.uploadCert.crt);
emit("updated", { domains });
} }
const pipeline: any = inject("pipeline"); const pipeline: any = inject("pipeline");

View File

@ -1,4 +1,4 @@
import { compute, useFormWrapper } from "@fast-crud/fast-crud"; import { compute, dict, useFormWrapper } from "@fast-crud/fast-crud";
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue"; import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
import { cloneDeep, omit } from "lodash-es"; import { cloneDeep, omit } from "lodash-es";
import { useReference } from "/@/use/use-refrence"; import { useReference } from "/@/use/use-refrence";
@ -8,6 +8,7 @@ import { checkPipelineLimit, getAllDomainsFromCrt } from "/@/views/certd/pipelin
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { usePluginStore } from "/@/store/plugin"; import { usePluginStore } from "/@/store/plugin";
import GroupSelector from "/@/views/certd/pipeline/group/group-selector.vue";
export function useCertUpload() { export function useCertUpload() {
const { openCrudFormDialog } = useFormWrapper(); const { openCrudFormDialog } = useFormWrapper();
@ -90,7 +91,7 @@ export function useCertUpload() {
return inputs; return inputs;
} }
async function openUploadCreateDialog() { async function openUploadCreateDialog(req: { defaultGroupId?: number }) {
//检查是否流水线数量超出限制 //检查是否流水线数量超出限制
await checkPipelineLimit(); await checkPipelineLimit();
@ -102,7 +103,11 @@ export function useCertUpload() {
return wrapperRef.value.getFormData(); return wrapperRef.value.getFormData();
} }
const inputs = await buildUploadCertPluginInputs(getFormData); const inputs = await buildUploadCertPluginInputs(getFormData);
const groupDictRef = dict({
url: "/pi/pipeline/group/all",
value: "id",
label: "name",
});
function createCrudOptions() { function createCrudOptions() {
return { return {
crudOptions: { crudOptions: {
@ -127,6 +132,19 @@ export function useCertUpload() {
helper: "任务执行失败实时提醒", helper: "任务执行失败实时提醒",
}, },
}, },
groupId: {
title: "流水线分组",
type: "dict-select",
dict: groupDictRef,
form: {
component: {
name: GroupSelector,
vModel: "modelValue",
},
value: req.defaultGroupId || undefined,
order: 9999,
},
},
}, },
form: { form: {
wrapper: { wrapper: {
@ -135,7 +153,7 @@ export function useCertUpload() {
}, },
async doSubmit({ form }: any) { async doSubmit({ form }: any) {
const cert = form.uploadCert; const cert = form.uploadCert;
const domains = getAllDomainsFromCrt(cert.crt); const domains = await getAllDomainsFromCrt(cert.crt);
const notifications = []; const notifications = [];
if (form.notification != null) { if (form.notification != null) {
@ -191,6 +209,7 @@ export function useCertUpload() {
content: JSON.stringify(pipeline), content: JSON.stringify(pipeline),
keepHistoryCount: 30, keepHistoryCount: 30,
type: "cert_upload", type: "cert_upload",
groupId: form.groupId,
}); });
router.push({ router.push({
path: "/certd/pipeline/detail", path: "/certd/pipeline/detail",

View File

@ -1,143 +0,0 @@
import { compute, CreateCrudOptionsRet, dict } from "@fast-crud/fast-crud";
import { useReference } from "/@/use/use-refrence";
import { merge, cloneDeep } from "lodash-es";
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
import { usePluginStore } from "/@/store/plugin";
export default function (certPlugins: any[], formWrapperRef: any): CreateCrudOptionsRet {
const inputs: any = {};
const moreParams = [];
for (const plugin of certPlugins) {
for (const inputKey in plugin.input) {
if (inputs[inputKey]) {
//如果两个插件有的字段,直接显示
inputs[inputKey].form.show = true;
continue;
}
const inputDefine = cloneDeep(plugin.input[inputKey]);
if (!inputDefine.required && !inputDefine.maybeNeed) {
moreParams.push(inputKey);
// continue;
}
useReference(inputDefine);
inputs[inputKey] = {
title: inputDefine.title,
form: {
...inputDefine,
show: compute(ctx => {
const form = formWrapperRef.value.getFormData();
if (!form) {
return false;
}
let inputDefineShow = true;
if (inputDefine.show != null) {
const computeShow = inputDefine.show as any;
if (computeShow === false) {
inputDefineShow = false;
} else if (computeShow && computeShow.computeFn) {
inputDefineShow = computeShow.computeFn({ form });
}
}
return form?.certApplyPlugin === plugin.name && inputDefineShow;
}),
},
};
}
}
const pluginStore = usePluginStore();
const randomHour = Math.floor(Math.random() * 6);
const randomMin = Math.floor(Math.random() * 60);
return {
crudOptions: {
form: {
wrapper: {
width: 1350,
saveRemind: false,
title: "创建证书流水线",
},
group: {
groups: {
more: {
header: "更多参数",
columns: moreParams,
collapsed: true,
},
},
},
},
columns: {
certApplyPlugin: {
title: "证书申请插件",
type: "dict-select",
dict: dict({
data: [
{ value: "CertApply", label: "JS-ACME" },
{ value: "CertApplyLego", label: "Lego-ACME" },
],
}),
form: {
order: 0,
value: "CertApply",
helper: {
render: () => {
return (
<ul>
<li>JS-ACME使便</li>
<li>Lego-ACMELegoDNSLEGO使</li>
</ul>
);
},
},
valueChange: {
handle: async ({ form, value }) => {
const config = await pluginStore.getPluginConfig({
name: value,
type: "builtIn",
});
if (config.sysSetting?.input) {
merge(form, config.sysSetting.input);
}
},
immediate: true,
},
},
},
...inputs,
triggerCron: {
title: "定时触发",
type: "text",
form: {
value: `0 ${randomMin} ${randomHour} * * *`,
component: {
name: "cron-editor",
vModel: "modelValue",
placeholder: "0 0 4 * * *",
},
helper: "点击上面的按钮,选择每天几点定时执行。\n建议设置为每天触发一次证书未到期之前任务会跳过不会重复执行",
order: 100,
},
},
notification: {
title: "失败通知",
type: "text",
form: {
value: 0,
component: {
name: NotificationSelector,
vModel: "modelValue",
on: {
selectedChange({ $event, form }) {
form.notificationTarget = $event;
},
},
},
order: 101,
helper: "任务执行失败实时提醒",
},
},
},
},
};
}

View File

@ -1,67 +0,0 @@
<template>
<fs-form-wrapper v-if="formWrapperOptions" ref="formWrapperRef" />
</template>
<script lang="ts" setup>
import { useColumns } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud.jsx";
import { ref } from "vue";
import { merge } from "lodash-es";
import { PluginGroup, usePluginStore } from "/@/store/plugin";
import { createNotificationApi } from "/@/views/certd/notification/api";
defineOptions({
name: "PiCertdForm",
});
const formWrapperRef = ref();
const formWrapperOptions = ref();
const doSubmitRef = ref();
const pluginStore = usePluginStore();
async function buildFormOptions() {
const pluginGroup = await pluginStore.getGroups();
const pluginGroups: { [key: string]: PluginGroup } = pluginGroup.groups;
const certPluginGroup = pluginGroups.cert;
const certPlugins = [];
for (const plugin of certPluginGroup.plugins) {
const detail: any = await pluginStore.getPluginDefine(plugin.name);
certPlugins.push(detail);
}
//
const { buildFormOptions } = useColumns();
//使crudOptions
let { crudOptions } = createCrudOptions(certPlugins, formWrapperRef);
const formOptions = buildFormOptions(
merge(crudOptions, {
form: {
async doSubmit({ form }: any) {
// certd pipeline
await doSubmitRef.value({ form });
if (form.email) {
//
const notificationApi = createNotificationApi();
await notificationApi.GetOrCreateDefault({ email: form.email });
}
},
},
}) as any
);
formWrapperOptions.value = formOptions;
}
buildFormOptions();
function open(doSubmit: any) {
doSubmitRef.value = doSubmit;
formWrapperRef.value.open(formWrapperOptions.value);
}
defineExpose({
open,
});
</script>
<style scoped></style>

View File

@ -1,126 +0,0 @@
import { checkPipelineLimit, readCertDetail } from "/@/views/certd/pipeline/utils";
import { omit } from "lodash-es";
import * as api from "/@/views/certd/pipeline/api";
import { message } from "ant-design-vue";
import { nanoid } from "nanoid";
import { useRouter } from "vue-router";
export function setRunnableIds(pipeline: any) {
const idMap: any = {};
function createId(oldId: any) {
if (oldId == null) {
return nanoid();
}
const newId = nanoid();
idMap[oldId] = newId;
return newId;
}
if (pipeline.stages) {
for (const stage of pipeline.stages) {
stage.id = createId(stage.id);
if (stage.tasks) {
for (const task of stage.tasks) {
task.id = createId(task.id);
if (task.steps) {
for (const step of task.steps) {
step.id = createId(step.id);
}
}
}
}
}
}
for (const trigger of pipeline.triggers) {
trigger.id = nanoid();
}
for (const notification of pipeline.notifications) {
notification.id = nanoid();
}
let content = JSON.stringify(pipeline);
for (const key in idMap) {
content = content.replaceAll(key, idMap[key]);
}
return JSON.parse(content);
}
export function useCertd(certdFormRef: any) {
const router = useRouter();
async function openAddCertdPipelineDialog() {
//检查是否流水线数量超出限制
await checkPipelineLimit();
certdFormRef.value.open(async ({ form }: any) => {
// const certDetail = readCertDetail(form.cert.crt);
// 添加certd pipeline
const triggers = [];
if (form.triggerCron) {
triggers.push({ title: "定时触发", type: "timer", props: { cron: form.triggerCron } });
}
const notifications = [];
if (form.notification != null) {
notifications.push({
type: "custom",
when: ["error", "turnToSuccess", "success"],
notificationId: form.notification,
title: form.notificationTarget?.name || "自定义通知",
});
}
const pluginInput = omit(form, ["triggerCron", "notification", "notificationTarget", "certApplyPlugin"]);
let pipeline = {
title: form.domains[0] + "证书自动化",
runnableType: "pipeline",
stages: [
{
title: "证书申请阶段",
maxTaskCount: 1,
runnableType: "stage",
tasks: [
{
title: "证书申请任务",
runnableType: "task",
steps: [
{
title: "申请证书",
runnableType: "step",
input: {
renewDays: 35,
...pluginInput,
},
strategy: {
runStrategy: 0, // 正常执行
},
type: form.certApplyPlugin,
},
],
},
],
},
],
triggers,
notifications,
};
pipeline = setRunnableIds(pipeline);
/**
* // cert: 证书; backup: 备份; custom:自定义;
* type: string;
* // custom: 自定义; monitor: 监控;
* from: string;
*/
const id = await api.Save({
title: pipeline.title,
content: JSON.stringify(pipeline),
keepHistoryCount: 30,
type: "cert",
});
message.success("创建成功,请添加证书部署任务");
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
});
}
return {
openAddCertdPipelineDialog,
};
}

View File

@ -0,0 +1,333 @@
import { checkPipelineLimit } from "/@/views/certd/pipeline/utils";
import { cloneDeep, merge, omit } from "lodash-es";
import { message } from "ant-design-vue";
import { nanoid } from "nanoid";
import { useRouter } from "vue-router";
import { compute, CreateCrudOptionsRet, dict, useFormWrapper } from "@fast-crud/fast-crud";
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
import { useReference } from "/@/use/use-refrence";
import { ref } from "vue";
import * as api from "../api";
import { PluginGroup, usePluginStore } from "/@/store/plugin";
import { createNotificationApi } from "/@/views/certd/notification/api";
import GroupSelector from "../group/group-selector.vue";
export function setRunnableIds(pipeline: any) {
const idMap: any = {};
function createId(oldId: any) {
if (oldId == null) {
return nanoid();
}
const newId = nanoid();
idMap[oldId] = newId;
return newId;
}
if (pipeline.stages) {
for (const stage of pipeline.stages) {
stage.id = createId(stage.id);
if (stage.tasks) {
for (const task of stage.tasks) {
task.id = createId(task.id);
if (task.steps) {
for (const step of task.steps) {
step.id = createId(step.id);
}
}
}
}
}
}
for (const trigger of pipeline.triggers) {
trigger.id = nanoid();
}
for (const notification of pipeline.notifications) {
notification.id = nanoid();
}
let content = JSON.stringify(pipeline);
for (const key in idMap) {
content = content.replaceAll(key, idMap[key]);
}
return JSON.parse(content);
}
export function useCertPipelineCreator() {
const { openCrudFormDialog } = useFormWrapper();
const pluginStore = usePluginStore();
const router = useRouter();
function createCrudOptions(certPlugins: any[], getFormData: any, doSubmit: any): CreateCrudOptionsRet {
const inputs: any = {};
const moreParams = [];
for (const plugin of certPlugins) {
for (const inputKey in plugin.input) {
if (inputs[inputKey]) {
//如果两个插件有的字段,直接显示
inputs[inputKey].form.show = true;
continue;
}
const inputDefine = cloneDeep(plugin.input[inputKey]);
if (!inputDefine.required && !inputDefine.maybeNeed) {
moreParams.push(inputKey);
// continue;
}
useReference(inputDefine);
inputs[inputKey] = {
title: inputDefine.title,
form: {
...inputDefine,
show: compute(ctx => {
const form = getFormData();
if (!form) {
return false;
}
let inputDefineShow = true;
if (inputDefine.show != null) {
const computeShow = inputDefine.show as any;
if (computeShow === false) {
inputDefineShow = false;
} else if (computeShow && computeShow.computeFn) {
inputDefineShow = computeShow.computeFn({ form });
}
}
return form?.certApplyPlugin === plugin.name && inputDefineShow;
}),
},
};
}
}
const pluginStore = usePluginStore();
const randomHour = Math.floor(Math.random() * 6);
const randomMin = Math.floor(Math.random() * 60);
const groupDictRef = dict({
url: "/pi/pipeline/group/all",
value: "id",
label: "name",
});
return {
crudOptions: {
form: {
doSubmit,
wrapper: {
width: 1350,
saveRemind: false,
title: "创建证书流水线",
},
group: {
groups: {
more: {
header: "更多参数",
columns: moreParams,
collapsed: true,
},
},
},
},
columns: {
certApplyPlugin: {
title: "证书申请插件",
type: "dict-select",
dict: dict({
data: [
{ value: "CertApply", label: "JS-ACME" },
{ value: "CertApplyLego", label: "Lego-ACME" },
],
}),
form: {
order: 0,
value: "CertApply",
helper: {
render: () => {
return (
<ul>
<li>JS-ACME使便</li>
<li>Lego-ACMELegoDNSLEGO使</li>
</ul>
);
},
},
valueChange: {
handle: async ({ form, value }) => {
const config = await pluginStore.getPluginConfig({
name: value,
type: "builtIn",
});
if (config.sysSetting?.input) {
merge(form, config.sysSetting.input);
}
},
immediate: true,
},
},
},
...inputs,
triggerCron: {
title: "定时触发",
type: "text",
form: {
value: `0 ${randomMin} ${randomHour} * * *`,
component: {
name: "cron-editor",
vModel: "modelValue",
placeholder: "0 0 4 * * *",
},
helper: "点击上面的按钮,选择每天几点定时执行。\n建议设置为每天触发一次证书未到期之前任务会跳过不会重复执行",
order: 100,
},
},
notification: {
title: "失败通知",
type: "text",
form: {
value: 0,
component: {
name: NotificationSelector,
vModel: "modelValue",
on: {
selectedChange({ $event, form }) {
form.notificationTarget = $event;
},
},
},
order: 101,
helper: "任务执行失败实时提醒",
},
},
groupId: {
title: "流水线分组",
type: "dict-select",
dict: groupDictRef,
form: {
component: {
name: GroupSelector,
vModel: "modelValue",
},
order: 9999,
},
},
},
},
};
}
async function getCertPlugins() {
const pluginGroup = await pluginStore.getGroups();
const pluginGroups: { [key: string]: PluginGroup } = pluginGroup.groups;
const certPluginGroup = pluginGroups.cert;
const certPlugins = [];
for (const plugin of certPluginGroup.plugins) {
const detail: any = await pluginStore.getPluginDefine(plugin.name);
certPlugins.push(detail);
}
return certPlugins;
}
async function openAddCertdPipelineDialog(req: { defaultGroupId?: number }) {
//检查是否流水线数量超出限制
await checkPipelineLimit();
const wrapperRef = ref();
function getFormData() {
if (!wrapperRef.value) {
return null;
}
return wrapperRef.value.getFormData();
}
async function doSubmit({ form }: any) {
// const certDetail = readCertDetail(form.cert.crt);
// 添加certd pipeline
const triggers = [];
if (form.triggerCron) {
triggers.push({ title: "定时触发", type: "timer", props: { cron: form.triggerCron } });
}
const notifications = [];
if (form.notification != null) {
notifications.push({
type: "custom",
when: ["error", "turnToSuccess", "success"],
notificationId: form.notification,
title: form.notificationTarget?.name || "自定义通知",
});
}
const pluginInput = omit(form, ["triggerCron", "notification", "notificationTarget", "certApplyPlugin", "groupId"]);
let pipeline = {
title: form.domains[0] + "证书自动化",
runnableType: "pipeline",
stages: [
{
title: "证书申请阶段",
maxTaskCount: 1,
runnableType: "stage",
tasks: [
{
title: "证书申请任务",
runnableType: "task",
steps: [
{
title: "申请证书",
runnableType: "step",
input: {
renewDays: 35,
...pluginInput,
},
strategy: {
runStrategy: 0, // 正常执行
},
type: form.certApplyPlugin,
},
],
},
],
},
],
triggers,
notifications,
};
pipeline = setRunnableIds(pipeline);
/**
* // cert: 证书; backup: 备份; custom:自定义;
* type: string;
* // custom: 自定义; monitor: 监控;
* from: string;
*/
const groupId = form.groupId;
const id = await api.Save({
title: pipeline.title,
content: JSON.stringify(pipeline),
keepHistoryCount: 30,
type: "cert",
groupId,
});
if (form.email) {
try {
//创建一个默认的邮件通知
const notificationApi = createNotificationApi();
await notificationApi.GetOrCreateDefault({ email: form.email });
} catch (e) {
console.error(e);
}
}
message.success("创建成功,请添加证书部署任务");
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
}
const certPlugins = await getCertPlugins();
const { crudOptions } = createCrudOptions(certPlugins, getFormData, doSubmit);
//@ts-ignore
crudOptions.columns.groupId.form.value = req.defaultGroupId || undefined;
const wrapper = await openCrudFormDialog({ crudOptions });
wrapperRef.value = wrapper;
}
return {
openAddCertdPipelineDialog,
};
}

View File

@ -13,15 +13,16 @@ import { cloneDeep } from "lodash-es";
import { useModal } from "/@/use/use-modal"; import { useModal } from "/@/use/use-modal";
import CertView from "./cert-view.vue"; import CertView from "./cert-view.vue";
import { eachStages } from "./utils"; import { eachStages } from "./utils";
import { setRunnableIds, useCertd } from "/@/views/certd/pipeline/certd-form/use"; import { setRunnableIds, useCertPipelineCreator } from "/@/views/certd/pipeline/certd-form/use";
import { useCertUpload } from "/@/views/certd/pipeline/cert-upload/use"; import { useCertUpload } from "/@/views/certd/pipeline/cert-upload/use";
import GroupSelector from "/@/views/certd/pipeline/group/group-selector.vue";
export default function ({ crudExpose, context: { certdFormRef, groupDictRef, selectedRowKeys } }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const router = useRouter(); const router = useRouter();
const { t } = useI18n(); const { t } = useI18n();
const lastResRef = ref(); const lastResRef = ref();
const { openAddCertdPipelineDialog } = useCertd(certdFormRef); const { openAddCertdPipelineDialog } = useCertPipelineCreator();
const { openUploadCreateDialog } = useCertUpload(); const { openUploadCreateDialog } = useCertUpload();
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
@ -116,6 +117,13 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
const userStore = useUserStore(); const userStore = useUserStore();
const settingStore = useSettingStore(); const settingStore = useSettingStore();
function onDialogOpen(opt: any) {
const searchForm = crudExpose.getSearchValidatedFormData();
opt.initialForm = {
groupId: searchForm.groupId,
};
}
return { return {
crudOptions: { crudOptions: {
request: { request: {
@ -157,7 +165,9 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
type: "primary", type: "primary",
icon: "ion:ios-add-circle-outline", icon: "ion:ios-add-circle-outline",
click() { click() {
openAddCertdPipelineDialog(); const searchForm = crudExpose.getSearchValidatedFormData();
const defaultGroupId = searchForm.groupId;
openAddCertdPipelineDialog({ defaultGroupId });
}, },
}, },
uploadCert: { uploadCert: {
@ -179,7 +189,8 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
}, },
icon: "ion:cloud-upload-outline", icon: "ion:cloud-upload-outline",
click() { click() {
openUploadCreateDialog(); const searchForm = crudExpose.getSearchValidatedFormData();
openUploadCreateDialog({ defaultGroupId: searchForm.groupId });
}, },
}, },
}, },
@ -190,6 +201,9 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
router.push({ path: "/certd/pipeline/detail", query: { id: res.id, editMode: "true" } }); router.push({ path: "/certd/pipeline/detail", query: { id: res.id, editMode: "true" } });
} }
}, },
wrapper: {
onOpen: onDialogOpen,
},
}, },
table: { table: {
scroll: { x: 1500 }, scroll: { x: 1500 },
@ -418,7 +432,7 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
show: false, show: false,
}, },
column: { column: {
sorter: true, sorter: false,
width: 150, width: 150,
align: "center", align: "center",
}, },
@ -489,6 +503,12 @@ export default function ({ crudExpose, context: { certdFormRef, groupDictRef, se
show: true, show: true,
}, },
dict: groupDictRef, dict: groupDictRef,
form: {
component: {
name: GroupSelector,
vModel: "modelValue",
},
},
column: { column: {
width: 130, width: 130,
align: "center", align: "center",

View File

@ -5,7 +5,6 @@ import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, Edi
import { pipelineGroupApi } from "./api"; import { pipelineGroupApi } from "./api";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { t } = useI18n();
const api = pipelineGroupApi; const api = pipelineGroupApi;
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => { const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
return await api.GetList(query); return await api.GetList(query);
@ -34,32 +33,32 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
mobile: { mobile: {
props: { props: {
rowHandle: { rowHandle: {
width: 160 width: 160,
} },
} },
} },
} },
}, },
request: { request: {
pageRequest, pageRequest,
addRequest, addRequest,
editRequest, editRequest,
delRequest delRequest,
}, },
form: { form: {
labelCol: { labelCol: {
//固定label宽度 //固定label宽度
span: null, span: null,
style: { style: {
width: "100px" width: "100px",
} },
}, },
col: { col: {
span: 22 span: 22,
}, },
wrapper: { wrapper: {
width: 600 width: 600,
} },
}, },
rowHandle: { rowHandle: {
width: 200, width: 200,
@ -72,12 +71,12 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
click({ row, index }) { click({ row, index }) {
crudExpose.openEdit({ crudExpose.openEdit({
index, index,
row row,
}); });
} },
} },
} },
} },
}, },
table: { table: {
editable: { editable: {
@ -90,8 +89,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const { row, key, value } = opts; const { row, key, value } = opts;
//如果是添加,需要返回{[rowKey]:xxx},比如:{id:2} //如果是添加,需要返回{[rowKey]:xxx},比如:{id:2}
return await api.UpdateObj({ id: row.id, [key]: value }); return await api.UpdateObj({ id: row.id, [key]: value });
} },
} },
}, },
columns: { columns: {
id: { id: {
@ -99,37 +98,37 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
key: "id", key: "id",
type: "number", type: "number",
search: { search: {
show: true show: true,
}, },
column: { column: {
width: 100, width: 100,
editable: { editable: {
disabled: true disabled: true,
} },
}, },
form: { form: {
show: false show: false,
} },
}, },
name: { name: {
title: "分组名称", title: "分组名称",
search: { search: {
show: true show: true,
}, },
type: "text", type: "text",
form: { form: {
rules: [ rules: [
{ {
required: true, required: true,
message: "请输入分组名称" message: "请输入分组名称",
} },
] ],
}, },
column: { column: {
width: 400 width: 400,
} },
} },
} },
} },
}; };
} }

View File

@ -0,0 +1,60 @@
<template>
<div class="pi-group-selector flex full-w">
<div class="flex-1">
<fs-dict-select :value="modelValue" :dict="groupDictRef" @update:value="doUpdate"></fs-dict-select>
</div>
<fs-table-select
class="flex-0"
:create-crud-options="createCrudOptions"
:crud-options-override="{
search: { show: false },
table: {
scroll: {
x: 540,
},
},
}"
:model-value="modelValue"
:dict="groupDictRef"
:show-current="false"
:show-select="false"
:dialog="{ width: 960 }"
:destroy-on-close="false"
height="400px"
@update:model-value="doUpdate"
@dialog-closed="doRefresh"
>
<template #default="scope">
<fs-button class="ml-5" type="primary" icon="ant-design:edit-outlined" @click="scope.open"></fs-button>
</template>
</fs-table-select>
</div>
</template>
<script setup lang="ts">
import createCrudOptions from "./crud";
import { dict, FsDictSelect } from "@fast-crud/fast-crud";
const props = defineProps<{
modelValue?: number;
}>();
defineOptions({
name: "GroupSelector",
});
const groupDictRef = dict({
url: "/pi/pipeline/group/all",
value: "id",
label: "name",
});
const emit = defineEmits(["refresh", "update:modelValue"]);
function doRefresh() {
emit("refresh");
groupDictRef.reloadDict();
}
function doUpdate(value: any) {
emit("update:modelValue", value);
}
</script>

View File

@ -15,7 +15,6 @@
<template #form-bottom> <template #form-bottom>
<div>申请证书</div> <div>申请证书</div>
</template> </template>
<pi-certd-form ref="certdFormRef"></pi-certd-form>
</fs-crud> </fs-crud>
</fs-page> </fs-page>
</template> </template>
@ -33,7 +32,6 @@ defineOptions({
name: "PipelineManager", name: "PipelineManager",
}); });
const certdFormRef = ref();
const groupDictRef = dict({ const groupDictRef = dict({
url: "/pi/pipeline/group/all", url: "/pi/pipeline/group/all",
value: "id", value: "id",
@ -41,7 +39,6 @@ const groupDictRef = dict({
}); });
const selectedRowKeys = ref([]); const selectedRowKeys = ref([]);
const context: any = { const context: any = {
certdFormRef,
groupDictRef, groupDictRef,
selectedRowKeys, selectedRowKeys,
}; };

View File

@ -2,8 +2,8 @@ import { forEach } from "lodash-es";
import { mySuiteApi } from "/@/views/certd/suite/mine/api"; import { mySuiteApi } from "/@/views/certd/suite/mine/api";
import { notification } from "ant-design-vue"; import { notification } from "ant-design-vue";
import { useSettingStore } from "/@/store/settings"; import { useSettingStore } from "/@/store/settings";
//@ts-ignore import { ReadCertDetail } from "./api";
import forge from "node-forge"; import { util } from "/@/utils";
export function eachStages(list: any[], exec: (item: any, runnableType: string) => void, runnableType: string = "stage") { export function eachStages(list: any[], exec: (item: any, runnableType: string) => void, runnableType: string = "stage") {
if (!list || list.length <= 0) { if (!list || list.length <= 0) {
return; return;
@ -70,33 +70,22 @@ export async function checkPipelineLimit() {
} }
} }
export function readCertDetail(crt: string) { export async function readCertDetail(crt: string) {
const detail = forge.pki.certificateFromPem(crt); const cached = await util.cache.get(crt);
const expires = detail.notAfter; if (cached) {
return { detail, expires }; return cached;
}
const res = await ReadCertDetail(crt);
await util.cache.set(crt, res);
return res;
} }
export function getAllDomainsFromCrt(crt: string) { export async function getAllDomainsFromCrt(crt: string) {
const { detail } = readCertDetail(crt); const { detail } = await readCertDetail(crt);
const domains = []; const altNames = detail.domains.altNames;
const commonName = detail.domains.commonName;
// 1. 提取SAN中的DNS名称 if (altNames.includes(commonName)) {
const sanExtension = detail.extensions.find((ext: any) => ext.name === "subjectAltName"); return altNames;
if (sanExtension) {
sanExtension.altNames.forEach((altName: any) => {
if (altName.type === 2) {
// type=2 表示DNS名称
domains.push(altName.value);
}
});
} }
return [commonName, ...altNames];
// 2. 如果没有SAN回退到CN通用名称
if (domains.length === 0) {
const cnAttr = detail.subject.attributes.find((attr: any) => attr.name === "commonName");
if (cnAttr) {
domains.push(cnAttr.value);
}
}
return domains;
} }

View File

@ -76,6 +76,7 @@ onMounted(() => {
iframeClient.register("updateLicense", async req => { iframeClient.register("updateLicense", async req => {
await api.UpdateLicense(req.data); await api.UpdateLicense(req.data);
await settingStore.init(); await settingStore.init();
await settingStore.doBindUrl();
notification.success({ notification.success({
message: "更新成功", message: "更新成功",
description: "专业版/商业版已激活", description: "专业版/商业版已激活",

View File

@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
### Bug Fixes
* 上传商用证书直接粘贴文本报错的问题修复无法上传ec加密证书的bug ([5750bb7](https://github.com/certd/certd/commit/5750bb706779da274d8e7a87e71416cb64d2df79))
* 修复下载证书时提示token已过期的问题 ([0e07ae6](https://github.com/certd/certd/commit/0e07ae6ce84dcb9279d3c44060d621566afa593c))
### Performance Improvements
* 新增部署到火山引擎ALB/CLB、上传到证书中心 ([c9a3e3d](https://github.com/certd/certd/commit/c9a3e3d9d26f964c7af7b56667936f1414fbf42a))
* 优化/api缓存为0 ([dc05cd4](https://github.com/certd/certd/commit/dc05cd481f186b13375192be965000e6b4b429a5))
* 优化华为cdn插件引用ccm证书 ([b565b4b](https://github.com/certd/certd/commit/b565b4b3b919b71b98ea2517670bc1ef00e00dc9))
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17) ## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
### Performance Improvements ### Performance Improvements

View File

@ -1,6 +1,6 @@
{ {
"name": "@certd/ui-server", "name": "@certd/ui-server",
"version": "1.33.5", "version": "1.33.6",
"description": "fast-server base midway", "description": "fast-server base midway",
"private": true, "private": true,
"type": "module", "type": "module",
@ -38,19 +38,19 @@
"@aws-sdk/client-acm": "^3.699.0", "@aws-sdk/client-acm": "^3.699.0",
"@aws-sdk/client-cloudfront": "^3.699.0", "@aws-sdk/client-cloudfront": "^3.699.0",
"@aws-sdk/client-s3": "^3.705.0", "@aws-sdk/client-s3": "^3.705.0",
"@certd/acme-client": "^1.33.5", "@certd/acme-client": "^1.33.6",
"@certd/basic": "^1.33.5", "@certd/basic": "^1.33.6",
"@certd/commercial-core": "^1.33.5", "@certd/commercial-core": "^1.33.6",
"@certd/jdcloud": "^1.33.5", "@certd/jdcloud": "^1.33.6",
"@certd/lib-huawei": "^1.33.5", "@certd/lib-huawei": "^1.33.6",
"@certd/lib-k8s": "^1.33.5", "@certd/lib-k8s": "^1.33.6",
"@certd/lib-server": "^1.33.5", "@certd/lib-server": "^1.33.6",
"@certd/midway-flyway-js": "^1.33.5", "@certd/midway-flyway-js": "^1.33.6",
"@certd/pipeline": "^1.33.5", "@certd/pipeline": "^1.33.6",
"@certd/plugin-cert": "^1.33.5", "@certd/plugin-cert": "^1.33.6",
"@certd/plugin-lib": "^1.33.5", "@certd/plugin-lib": "^1.33.6",
"@certd/plugin-plus": "^1.33.5", "@certd/plugin-plus": "^1.33.6",
"@certd/plus-core": "^1.33.5", "@certd/plus-core": "^1.33.6",
"@corsinvest/cv4pve-api-javascript": "^8.3.0", "@corsinvest/cv4pve-api-javascript": "^8.3.0",
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120", "@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120", "@huaweicloud/huaweicloud-sdk-core": "^3.1.120",

View File

@ -0,0 +1,53 @@
// 扫描目录,列出文件,然后加载为模块
import { join } from 'path';
import fs from 'fs'
import { pathToFileURL } from "node:url";
import path from 'path'
function scanDir(dir) {
const files = fs.readdirSync(dir);
const result = [];
// 扫描目录及子目录
for (const file of files) {
if (file.includes("index.js")) {
continue;
}
const filePath = join(dir, file);
const stat = fs.statSync(filePath);
if (stat.isDirectory()) {
result.push(...scanDir(filePath));
} else {
if (!file.endsWith(".js")) {
continue;
}
result.push(filePath);
}
}
return result
}
export default async function loadModules(dir) {
const files = scanDir(dir);
const modules = {}
for (const file of files) {
try {
// 转换为 file:// URLWindows 必需)
const moduleUrl = pathToFileURL(file).href
const module = await import(moduleUrl)
// 如果模块有默认导出,优先使用
modules[file] = module.default || module
} catch (err) {
console.error(`加载模块 ${file} 失败:`, err)
}
}
return modules;
}
const modules = await loadModules('./dist/plugins');
for (const key in modules) {
console.log(key)
}

View File

@ -96,7 +96,7 @@ export class MainConfiguration {
this.app.getMiddleware().insertFirst(async (ctx: IMidwayKoaContext, next: NextFunction) => { this.app.getMiddleware().insertFirst(async (ctx: IMidwayKoaContext, next: NextFunction) => {
await next(); await next();
if (ctx.path === '/' || ctx.path === '/index.html') { if (ctx.path === '/' || ctx.path === '/index.html' || ctx.path.startsWith("/api")) {
ctx.response.set('Cache-Control', 'public,max-age=0'); ctx.response.set('Cache-Control', 'public,max-age=0');
} }
}); });

View File

@ -28,7 +28,7 @@ export class LoginController extends BaseController {
} }
private writeTokenCookie(token: { expire: any; token: any }) { private writeTokenCookie(token: { expire: any; token: any }) {
this.ctx.cookies.set("token", token.token, { this.ctx.cookies.set("certd_token", token.token, {
maxAge: 1000 * token.expire maxAge: 1000 * token.expire
}); });
} }
@ -72,5 +72,10 @@ export class LoginController extends BaseController {
} }
@Post('/logout', { summary: Constants.per.authOnly }) @Post('/logout', { summary: Constants.per.authOnly })
public logout() {} public logout() {
this.ctx.cookies.set("certd_token", "", {
maxAge: 0
});
return this.ok();
}
} }

View File

@ -1,7 +1,8 @@
import { Controller, Inject, Post, Provide, Query } from '@midwayjs/core'; import {Body, Controller, Inject, Post, Provide, Query} from '@midwayjs/core';
import { PipelineService } from '../../../modules/pipeline/service/pipeline-service.js'; import { PipelineService } from '../../../modules/pipeline/service/pipeline-service.js';
import { BaseController, Constants } from '@certd/lib-server'; import { BaseController, Constants } from '@certd/lib-server';
import { StorageService } from '../../../modules/pipeline/service/storage-service.js'; import { StorageService } from '../../../modules/pipeline/service/storage-service.js';
import {CertReader} from "@certd/plugin-cert";
@Provide() @Provide()
@Controller('/api/pi/cert') @Controller('/api/pi/cert')
@ -18,4 +19,14 @@ export class CertController extends BaseController {
const privateVars = await this.storeService.getPipelinePrivateVars(id); const privateVars = await this.storeService.getPipelinePrivateVars(id);
return this.ok(privateVars.cert); return this.ok(privateVars.cert);
} }
@Post('/readCertDetail', { summary: Constants.per.authOnly })
async readCertDetail(@Body('crt') crt: string) {
if (!crt) {
throw new Error('crt is required');
}
const certDetail = CertReader.readCertDetail(crt)
return this.ok(certDetail);
}
} }

View File

@ -56,7 +56,20 @@ export class AuthorityMiddleware implements IWebMiddleware {
token = token.replace('Bearer ', '').trim(); token = token.replace('Bearer ', '').trim();
if (!token) { if (!token) {
//尝试从cookie中获取token //尝试从cookie中获取token
token = ctx.cookies.get('token') || ''; const cookie = ctx.headers.cookie;
if (cookie) {
const items = cookie.split(';');
for (const item of items) {
if (!item || !item.trim()) {
continue;
}
const [key, value] = item.split('=');
if (key.trim() === 'certd_token') {
token = value.trim();
break;
}
}
}
} }
if (!token) { if (!token) {
//尝试从query中获取token //尝试从query中获取token

View File

@ -19,7 +19,7 @@ import { CertApplyPluginNames} from '@certd/plugin-cert';
export class HauweiDeployCertToCDN extends AbstractTaskPlugin { export class HauweiDeployCertToCDN extends AbstractTaskPlugin {
@TaskInput({ @TaskInput({
title: '域名证书', title: '域名证书',
helper: '请选择前置任务输出的域名证书', helper: '请选择前置任务输出的域名证书\n如果你选择使用ccm证书ID则需要在[域名管理页面右上角开启SCM授权](https://console.huaweicloud.com/cdn/#/cdn/domain)',
component: { component: {
name: 'output-selector', name: 'output-selector',
from: [...CertApplyPluginNames,'HauweiUploadToCCM'], from: [...CertApplyPluginNames,'HauweiUploadToCCM'],
@ -62,16 +62,17 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin {
.withHttpsStatus('on') .withHttpsStatus('on')
.withCertificateType('server') .withCertificateType('server')
if(typeof this.cert === 'object'){ if(typeof this.cert === 'object'){
httpsConfig= httpsConfig.withCertificateSource(0) httpsConfig= httpsConfig.withCertificateSource(0)
.withCertificateName(this.appendTimeSuffix('certd')) .withCertificateName(this.appendTimeSuffix('certd'))
.withCertificateValue(this.cert.crt) .withCertificateValue(this.cert.crt)
.withPrivateKey(this.cert.key); .withPrivateKey(this.cert.key);
}else{ }else{
this.logger.info('使用已有域名证书:', this.cert); this.logger.info('使用已有域名证书:', this.cert);
httpsConfig= httpsConfig.withCertificateSource(2)//scm证书 httpsConfig= httpsConfig.withCertificateSource(2)//scm证书
.withScmCertificateId(this.cert) .withCertificateName(this.appendTimeSuffix('certd'))
} .withScmCertificateId(this.cert)
}
const config = new cdn.Configs().withHttps(httpsConfig); const config = new cdn.Configs().withHttps(httpsConfig);
const body = new cdn.ModifyDomainConfigRequestBody().withConfigs(config); const body = new cdn.ModifyDomainConfigRequestBody().withConfigs(config);

View File

@ -1 +1,4 @@
export * from './plugin-deploy-to-cdn.js' export * from './plugin-deploy-to-cdn.js'
export * from './plugin-deploy-to-clb.js'
export * from './plugin-upload-to-cert-center.js'
export * from './plugin-deploy-to-alb.js'

View File

@ -0,0 +1,220 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { VolcengineAccess } from "../access.js";
import { VolcengineClient } from "../ve-client.js";
@IsTaskPlugin({
name: "VolcengineDeployToALB",
title: "火山引擎-部署证书至ALB",
icon: "svg:icon-volcengine",
group: pluginGroups.volcengine.key,
desc: "部署至火山引擎应用负载均衡",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed
}
}
})
export class VolcengineDeployToALB extends AbstractTaskPlugin {
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames, "VolcengineUploadToCertCenter"]
},
required: true
})
cert!: CertInfo | string;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@TaskInput({
title: "Access授权",
helper: "火山引擎AccessKeyId、AccessKeySecret",
component: {
name: "access-selector",
type: "volcengine"
},
required: true
})
accessId!: string;
@TaskInput({
title: "Region",
helper: "地区选择",
component: {
name: "a-select",
options: [
/**
*
* 2
*
* cn-beijing
* 4
* Acn-beijing-a
* Bcn-beijing-b
* Ccn-beijing-c
* Dcn-beijing-d
*
* 2
*
* cn-shanghai
* 4
* Acn-shanghai-a
* Bcn-shanghai-b
* Ccn-shanghai-c
* Ecn-shanghai-e
*
* 1
* 广
* cn-guangzhou
* 3
* Acn-guangzhou-a
* Bcn-guangzhou-b
* Ccn-guangzhou-c
*
*
*
* cn-hongkong
* 2
* Acn-hongkong-a
* Bcn-hongkong-b
*
*
*
*
* ap-southeast-1
* 2
* Aap-southeast-1a
* Bap-southeast-1b
*
*
* ap-southeast-3
* 2
* Aap-southeast-3a
* Bap-southeast-3b
*
*/
{ label: "北京", value: "cn-beijing" },
{ label: "上海", value: "cn-shanghai" },
{ label: "广州", value: "cn-guangzhou" },
{ label: "香港", value: "cn-hongkong" },
{ label: "柔佛", value: "ap-southeast-1" },
{ label: "雅加达", value: "ap-southeast-3" }
]
},
value: "cn-beijing",
required: true
})
regionId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: "监听器列表",
helper: "选择要部署证书的监听器\n需要在监听器中选择证书中心进行跨服务访问授权",
action: VolcengineDeployToALB.prototype.onGetListenerList.name,
watches: ["certDomains", "accessId", "regionId"],
required: true
})
)
listenerList!: string | string[];
async onInstance() {
}
async execute(): Promise<void> {
this.logger.info("开始部署证书到火山引擎ALB");
const access = await this.getAccess<VolcengineAccess>(this.accessId);
const certService = await this.getCertService(access);
let certId = this.cert;
if (typeof certId !== "string") {
const certInfo = this.cert as CertInfo;
this.logger.info(`开始上传证书`);
certId = await certService.ImportCertificate({
certName:this.appendTimeSuffix("certd"),
cert:certInfo
});
this.logger.info(`上传证书成功:${certId}`);
} else {
this.logger.info(`使用已有证书ID:${certId}`);
}
const service = await this.getAlbService();
for (const listener of this.listenerList) {
this.logger.info(`开始部署监听器${listener}证书`);
await service.request({
action: "ModifyListenerAttributes",
query: {
ListenerId: listener,
CertificateSource: "cert_center",
CertCenterCertificateId: certId
}
});
this.logger.info(`部署监听器${listener}证书成功`);
}
this.logger.info("部署完成");
}
private async getCertService(access: VolcengineAccess) {
const client = new VolcengineClient({
logger: this.logger,
access,
http: this.http
});
return await client.getCertCenterService();
}
private async getAlbService() {
const access = await this.getAccess<VolcengineAccess>(this.accessId);
const client = new VolcengineClient({
logger: this.logger,
access,
http: this.http
});
const service = await client.getAlbService({
region: this.regionId
});
return service;
}
async onGetListenerList(data: any) {
if (!this.accessId) {
throw new Error("请选择Access授权");
}
const service = await this.getAlbService();
const res = await service.request({
action: "DescribeListeners",
method: "GET",
query: {
PageSize: 100,
Protocol: "HTTPS"
},
});
const list = res.Result.Listeners;
if (!list || list.length === 0) {
throw new Error("找不到HTTPS类型的负载均衡监听器您也可以手动输入监听器ID");
}
return list.map((item: any) => {
return {
value: item.ListenerId,
label: `${item.ListenerName}<${item.Description}:${item.ListenerId}>`
};
});
}
}
new VolcengineDeployToALB();

View File

@ -23,7 +23,7 @@ export class VolcengineDeployToCDN extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书', helper: '请选择前置任务输出的域名证书',
component: { component: {
name: 'output-selector', name: 'output-selector',
from: [...CertApplyPluginNames, 'VolcengineUploadCert'], from: [...CertApplyPluginNames, 'VolcengineUploadToCertCenter'],
}, },
required: true, required: true,
}) })
@ -82,11 +82,17 @@ export class VolcengineDeployToCDN extends AbstractTaskPlugin {
const client = await this.getClient(access) const client = await this.getClient(access)
const service = await client.getCdnClient() const service = await client.getCdnClient()
if (!this.cert) {
throw new Error('你还未选择证书');
}
let certId = this.cert let certId = this.cert
if (typeof certId !== 'string') { if (typeof certId !== 'string') {
const certInfo = this.cert as CertInfo const certInfo = this.cert as CertInfo
this.logger.info(`开始上传证书`) this.logger.info(`开始上传证书`)
certId = await client.uploadCert(certInfo, this.appendTimeSuffix('certd')) certId = await client.uploadCert(certInfo, this.appendTimeSuffix('certd'))
this.logger.info(`上传证书成功:${certId}`);
}else{
this.logger.info(`使用已有证书ID${certId}`);
} }
for (const domain of this.domainName) { for (const domain of this.domainName) {

View File

@ -0,0 +1,248 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { VolcengineAccess } from "../access.js";
import { VolcengineClient } from "../ve-client.js";
@IsTaskPlugin({
name: "VolcengineDeployToCLB",
title: "火山引擎-部署证书至CLB",
icon: "svg:icon-volcengine",
group: pluginGroups.volcengine.key,
desc: "部署至火山引擎负载均衡",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed
}
}
})
export class VolcengineDeployToCLB extends AbstractTaskPlugin {
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames, "VolcengineUploadToCertCenter"]
},
required: true
})
cert!: CertInfo | string;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@TaskInput({
title: "Access授权",
helper: "火山引擎AccessKeyId、AccessKeySecret",
component: {
name: "access-selector",
type: "volcengine"
},
required: true
})
accessId!: string;
@TaskInput({
title: "Region",
helper: "地区选择",
component: {
name: "a-select",
options: [
/**
*
* 2
*
* cn-beijing
* 4
* Acn-beijing-a
* Bcn-beijing-b
* Ccn-beijing-c
* Dcn-beijing-d
*
* 2
*
* cn-shanghai
* 4
* Acn-shanghai-a
* Bcn-shanghai-b
* Ccn-shanghai-c
* Ecn-shanghai-e
*
* 1
* 广
* cn-guangzhou
* 3
* Acn-guangzhou-a
* Bcn-guangzhou-b
* Ccn-guangzhou-c
*
*
*
* cn-hongkong
* 2
* Acn-hongkong-a
* Bcn-hongkong-b
*
*
*
*
* ap-southeast-1
* 2
* Aap-southeast-1a
* Bap-southeast-1b
*
*
* ap-southeast-3
* 2
* Aap-southeast-3a
* Bap-southeast-3b
*
*/
{ label: "北京", value: "cn-beijing" },
{ label: "上海", value: "cn-shanghai" },
{ label: "广州", value: "cn-guangzhou" },
{ label: "深圳", value: "cn-shenzhen" },
{ label: "杭州", value: "cn-hangzhou" },
{ label: "南京", value: "cn-north-1" },
{ label: "青岛", value: "cn-qingdao" },
{ label: "重庆", value: "cn-chengdu" },
{ label: "香港", value: "cn-hongkong" },
{ label: "柔佛", value: "ap-southeast-1" },
{ label: "雅加达", value: "ap-southeast-3" }
]
},
value: "cn-beijing",
required: true
})
regionId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: "监听器列表",
helper: "选择要部署证书的监听器\n需要在监听器中选择证书中心进行跨服务访问授权",
action: VolcengineDeployToCLB.prototype.onGetListenerList.name,
watches: ["certDomains", "accessId", "regionId"],
required: true
})
)
listenerList!: string | string[];
async onInstance() {
}
async execute(): Promise<void> {
this.logger.info("开始部署证书到火山引擎CLB");
const access = await this.getAccess<VolcengineAccess>(this.accessId);
const certService = await this.getCertService(access);
let certId = this.cert;
if (typeof certId !== "string") {
const certInfo = this.cert as CertInfo;
this.logger.info(`开始上传证书`);
certId = await certService.ImportCertificate({
certName:this.appendTimeSuffix("certd"),
cert:certInfo
});
this.logger.info(`上传证书成功:${certId}`);
} else {
this.logger.info(`使用已有证书ID:${certId}`);
}
const service = await this.getClbService();
for (const listener of this.listenerList) {
this.logger.info(`开始部署监听器${listener}证书`);
await service.request({
action: "ModifyListenerAttributes",
query: {
ListenerId: listener,
CertificateSource: "cert_center",
CertCenterCertificateId: certId
}
});
this.logger.info(`部署监听器${listener}证书成功`);
}
this.logger.info("部署完成");
}
private async getCertService(access: VolcengineAccess) {
const client = new VolcengineClient({
logger: this.logger,
access,
http: this.http
});
return await client.getCertCenterService();
}
async onGetClbList(data: any) {
if (!this.accessId) {
throw new Error("请选择Access授权");
}
const service = await this.getClbService();
const res = await service.request({
action: "DescribeLoadBalancers",
method: "GET",
query: {
PageSize: 100
},
});
const list = res.Result.LoadBalancers;
return list.map((item: any) => {
return {
value: item.LoadBalancerId,
label: `${item.LoadBalancerName}<${item.Description}>`
};
});
}
private async getClbService() {
const access = await this.getAccess<VolcengineAccess>(this.accessId);
const client = new VolcengineClient({
logger: this.logger,
access,
http: this.http
});
const service = await client.getClbService({
region: this.regionId
});
return service;
}
async onGetListenerList(data: any) {
if (!this.accessId) {
throw new Error("请选择Access授权");
}
const service = await this.getClbService();
const res = await service.request({
action: "DescribeListeners",
method: "GET",
query: {
PageSize: 100,
Protocol: "HTTPS"
},
});
const list = res.Result.Listeners;
if (!list || list.length === 0) {
throw new Error("找不到HTTPS类型的负载均衡监听器您也可以手动输入监听器ID");
}
return list.map((item: any) => {
return {
value: item.ListenerId,
label: `${item.ListenerName}<${item.Description}:${item.ListenerId}>`
};
});
}
}
new VolcengineDeployToCLB();

View File

@ -0,0 +1,77 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
import { createCertDomainGetterInputDefine } from "@certd/plugin-lib";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { VolcengineAccess } from "../access.js";
import { VolcengineClient } from "../ve-client.js";
@IsTaskPlugin({
name: 'VolcengineUploadToCertCenter',
title: '火山引擎-上传证书至证书中心',
icon: 'svg:icon-volcengine',
group: pluginGroups.volcengine.key,
desc: '上传证书至火山引擎证书中心',
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class VolcengineUploadToCertCenter extends AbstractTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: [...CertApplyPluginNames],
},
required: true,
})
cert!: CertInfo;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@TaskInput({
title: 'Access授权',
helper: '火山引擎AccessKeyId、AccessKeySecret',
component: {
name: 'access-selector',
type: 'volcengine',
},
required: true,
})
accessId!: string;
@TaskOutput({
title: '上传成功后的火山引擎证书Id',
})
volcengineCertId?: string;
async onInstance() {}
async execute(): Promise<void> {
this.logger.info('开始上传证书到证书中心');
const access = await this.getAccess<VolcengineAccess>(this.accessId);
const client = await this.getClient(access)
const service = await client.getCertCenterService()
const certInfo = this.cert
this.logger.info(`开始上传证书`)
this.volcengineCertId = await service.ImportCertificate({
certName: this.appendTimeSuffix('certd'),
cert: certInfo
})
this.logger.info(`上传完成:${this.volcengineCertId}`);
}
async getClient(access: VolcengineAccess) {
return new VolcengineClient({
logger: this.logger,
access,
http:this.http
})
}
}
new VolcengineUploadToCertCenter();

View File

@ -0,0 +1,122 @@
import { VolcengineAccess } from "./access.js";
import { HttpClient, ILogger } from "@certd/basic";
export type VolcengineOpts = {
access: VolcengineAccess
logger: ILogger
http: HttpClient
}
export class VolcengineClient {
opts: VolcengineOpts;
CommonService: any;
constructor(opts: VolcengineOpts) {
this.opts = opts;
}
async getCertCenterService() {
const CommonService = await this.getServiceCls();
const service = new CommonService({
serviceName: "certificate_service",
defaultVersion: "2024-10-01"
});
service.setAccessKeyId(this.opts.access.accessKeyId);
service.setSecretKey(this.opts.access.secretAccessKey);
service.setRegion("cn-beijing");
service.ImportCertificate = async (body: { certName: string, cert: any }) => {
const { certName, cert } = body;
const res = await service.request({
action: "ImportCertificate",
method: "POST",
body: {
Tag: certName,
Repeatable: false,
CertificateInfo: {
CertificateChain: cert.crt,
PrivateKey: cert.key
}
}
});
return res.Result.InstanceId || res.Result.RepeatId
};
return service;
}
async getClbService(opts: { region?: string }) {
const CommonService = await this.getServiceCls();
const service = new CommonService({
serviceName: "clb",
defaultVersion: "2020-04-01"
});
service.setAccessKeyId(this.opts.access.accessKeyId);
service.setSecretKey(this.opts.access.secretAccessKey);
service.setRegion(opts.region);
return service;
}
async getAlbService(opts: { region?: string }) {
const CommonService = await this.getServiceCls();
const service = new CommonService({
serviceName: "alb",
defaultVersion: "2020-04-01"
});
service.setAccessKeyId(this.opts.access.accessKeyId);
service.setSecretKey(this.opts.access.secretAccessKey);
service.setRegion(opts.region);
return service;
}
async getServiceCls() {
if (this.CommonService) {
return this.CommonService;
}
const { Service } = await import("@volcengine/openapi");
class CommonService extends Service {
Generic: any;
constructor(options: {
serviceName: string;
defaultVersion: string;
}) {
super(Object.assign({ host: "open.volcengineapi.com" }, options));
this.Generic = async (req: { action: string, body?: any, method?: string, query?: any }) => {
const { action, method, body, query } = req;
return await this.fetchOpenAPI({
Action: action,
Version: options.defaultVersion,
method: method as any,
headers: {
"content-type": "application/json"
},
query: query || {},
data: body
});
};
}
async request(req: { action: string, body?: any, method?: string, query?: any }) {
const res = await this.Generic(req);
if (res.errorcode) {
throw new Error(`${res.errorcode}:${res.message}`);
}
if (res.ResponseMetadata?.Error) {
throw new Error(res.ResponseMetadata?.Error?.Message);
}
return res;
}
}
this.CommonService = CommonService;
return CommonService;
}
}

View File

@ -46,7 +46,7 @@ importers:
packages/core/acme-client: packages/core/acme-client:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../basic version: link:../basic
'@peculiar/x509': '@peculiar/x509':
specifier: ^1.11.0 specifier: ^1.11.0
@ -204,10 +204,10 @@ importers:
packages/core/pipeline: packages/core/pipeline:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../basic version: link:../basic
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../pro/plus-core version: link:../../pro/plus-core
dayjs: dayjs:
specifier: ^1.11.7 specifier: ^1.11.7
@ -412,7 +412,7 @@ importers:
packages/libs/lib-k8s: packages/libs/lib-k8s:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/basic version: link:../../core/basic
'@kubernetes/client-node': '@kubernetes/client-node':
specifier: 0.21.0 specifier: 0.21.0
@ -452,16 +452,16 @@ importers:
packages/libs/lib-server: packages/libs/lib-server:
dependencies: dependencies:
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../pro/plus-core version: link:../../pro/plus-core
'@midwayjs/cache': '@midwayjs/cache':
specifier: ~3.14.0 specifier: ~3.14.0
@ -604,16 +604,16 @@ importers:
packages/plugins/plugin-cert: packages/plugins/plugin-cert:
dependencies: dependencies:
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../plugin-lib version: link:../plugin-lib
'@google-cloud/publicca': '@google-cloud/publicca':
specifier: ^1.3.0 specifier: ^1.3.0
@ -680,10 +680,10 @@ importers:
specifier: ^1.7.10 specifier: ^1.7.10
version: 1.8.0 version: 1.8.0
'@certd/basic': '@certd/basic':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@kubernetes/client-node': '@kubernetes/client-node':
specifier: 0.21.0 specifier: 0.21.0
@ -771,19 +771,19 @@ importers:
packages/pro/commercial-core: packages/pro/commercial-core:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/basic version: link:../../core/basic
'@certd/lib-server': '@certd/lib-server':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../libs/lib-server version: link:../../libs/lib-server
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-plus': '@certd/plugin-plus':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../plugin-plus version: link:../plugin-plus
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../plus-core version: link:../plus-core
'@midwayjs/core': '@midwayjs/core':
specifier: ~3.20.3 specifier: ~3.20.3
@ -868,22 +868,22 @@ importers:
specifier: ^1.0.2 specifier: ^1.0.2
version: 1.0.2 version: 1.0.2
'@certd/basic': '@certd/basic':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/basic version: link:../../core/basic
'@certd/lib-k8s': '@certd/lib-k8s':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../libs/lib-k8s version: link:../../libs/lib-k8s
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-cert': '@certd/plugin-cert':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../plugins/plugin-cert version: link:../../plugins/plugin-cert
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../plugins/plugin-lib version: link:../../plugins/plugin-lib
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../plus-core version: link:../plus-core
ali-oss: ali-oss:
specifier: ^6.21.0 specifier: ^6.21.0
@ -980,7 +980,7 @@ importers:
packages/pro/plus-core: packages/pro/plus-core:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/basic version: link:../../core/basic
dayjs: dayjs:
specifier: ^1.11.7 specifier: ^1.11.7
@ -1270,10 +1270,10 @@ importers:
version: 0.1.3(zod@3.24.2) version: 0.1.3(zod@3.24.2)
devDependencies: devDependencies:
'@certd/lib-iframe': '@certd/lib-iframe':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../libs/lib-iframe version: link:../../libs/lib-iframe
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@rollup/plugin-commonjs': '@rollup/plugin-commonjs':
specifier: ^25.0.7 specifier: ^25.0.7
@ -1453,43 +1453,43 @@ importers:
specifier: ^3.705.0 specifier: ^3.705.0
version: 3.758.0(aws-crt@1.25.3) version: 3.758.0(aws-crt@1.25.3)
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/basic version: link:../../core/basic
'@certd/commercial-core': '@certd/commercial-core':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../pro/commercial-core version: link:../../pro/commercial-core
'@certd/jdcloud': '@certd/jdcloud':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../libs/lib-jdcloud version: link:../../libs/lib-jdcloud
'@certd/lib-huawei': '@certd/lib-huawei':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../libs/lib-huawei version: link:../../libs/lib-huawei
'@certd/lib-k8s': '@certd/lib-k8s':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../libs/lib-k8s version: link:../../libs/lib-k8s
'@certd/lib-server': '@certd/lib-server':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../libs/lib-server version: link:../../libs/lib-server
'@certd/midway-flyway-js': '@certd/midway-flyway-js':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../libs/midway-flyway-js version: link:../../libs/midway-flyway-js
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-cert': '@certd/plugin-cert':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../plugins/plugin-cert version: link:../../plugins/plugin-cert
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../plugins/plugin-lib version: link:../../plugins/plugin-lib
'@certd/plugin-plus': '@certd/plugin-plus':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../pro/plugin-plus version: link:../../pro/plugin-plus
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.33.4 specifier: ^1.33.5
version: link:../../pro/plus-core version: link:../../pro/plus-core
'@corsinvest/cv4pve-api-javascript': '@corsinvest/cv4pve-api-javascript':
specifier: ^8.3.0 specifier: ^8.3.0
@ -20673,13 +20673,13 @@ snapshots:
resolve: 1.22.10 resolve: 1.22.10
semver: 6.3.1 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: dependencies:
eslint: 7.32.0 eslint: 7.32.0
prettier: 2.8.8 prettier: 2.8.8
prettier-linter-helpers: 1.0.0 prettier-linter-helpers: 1.0.0
optionalDependencies: 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): 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: dependencies:
@ -23393,7 +23393,7 @@ snapshots:
eslint: 7.32.0 eslint: 7.32.0
eslint-config-prettier: 8.10.0(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-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 execa: 5.1.1
inquirer: 7.3.3 inquirer: 7.3.3
json5: 2.2.3 json5: 2.2.3