diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..d19e47465
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,5 @@
+# These are supported funding model platforms
+
+github: greper
+buy_me_a_coffee: greper
+custom: ['https://afdian.com/a/greper']
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 317406481..40d3e5b3d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,56 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+### Bug Fixes
+
+* 修复导入插件对话框无法打开的bug,修复插件编辑页面打开多个代码编辑器消失的bug ([e5a080a](https://github.com/certd/certd/commit/e5a080aebe0d2f3e3c0f86bf863f75069c1bf7ab))
+* 修复ssl.com报EMAILADDRESS数量不对的bug ([c560cc5](https://github.com/certd/certd/commit/c560cc5adda6e15bf3a8865d874042550a6c2688))
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+### Bug Fixes
+
+* 修复商业版退出登录后,丢失站点个性化设置的bug ([d75dd05](https://github.com/certd/certd/commit/d75dd058d65c85f80c49e1fa7a910e6c6f08e824))
+* 修复授权类型和名称字段排到最后的bug ([43b7977](https://github.com/certd/certd/commit/43b79778ea9034065f6a15af3296274315597c6b))
+* 修复证书监控某些情况下报 options.lookup不能为null的bug ([d2ecfe5](https://github.com/certd/certd/commit/d2ecfe5491b2639eb30b5cae293af6062d58bb9f))
+* 修复证书手动托管时新上传的证书无效的bug ([506385e](https://github.com/certd/certd/commit/506385e5a2600887fe30854e0713583caaa2e689))
+* 修复secret patch 类型多了type:的bug ([d04f383](https://github.com/certd/certd/commit/d04f3831611011a90ec0594724b9694490d5edd0))
+
+### Performance Improvements
+
+* 登录支持极验验证码 ([370db62](https://github.com/certd/certd/commit/370db62bf0aece241859244927beabba32d6a257))
+* 登录注册、找回密码都支持极验验证码和图片验证码 ([7bdde68](https://github.com/certd/certd/commit/7bdde68ecea29fe2c570fd3cb082139db6c93d93))
+* 优化加量包展示效果 ([3c65f37](https://github.com/certd/certd/commit/3c65f37d84177ba107d4a6462648af12d2fc4b7a))
+* 证书到期剩余天数进度条根据实际证书有效期计算 ([#528](https://github.com/certd/certd/issues/528)) nicheng-he ([2d4586b](https://github.com/certd/certd/commit/2d4586b1c42c39f97d2a95b9453cca4bc8bfbe61))
+* add preferred chain option ([#519](https://github.com/certd/certd/issues/519)) @ZeroClover ([902359f](https://github.com/certd/certd/commit/902359f24ed12eee4f9b65178f1d6a60378351d2))
+* ssh配置增加脚本类型设置,bash还是sh ([ae41c60](https://github.com/certd/certd/commit/ae41c6038b27c9476e64a2402a8daf247c38a5b6))
+* start.sh增加sudo ([b7271d7](https://github.com/certd/certd/commit/b7271d7a464773a1bf87d7d1f24d933ba0f86915))
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+### Bug Fixes
+
+* 前置任务输出不存在时输出警告提示 ([b59052c](https://github.com/certd/certd/commit/b59052cc43b7b070fabd8b8e914e4c2a5e0ad61c))
+* 修复批量流水线执行时日志显示错乱的问题 ([4372adc](https://github.com/certd/certd/commit/4372adc703b9a4c785664054ab2a533626d815a8))
+* 修复远程数据选择无法过滤的bug ([6cbb073](https://github.com/certd/certd/commit/6cbb0739f8428d51b0712f718fe4d236cc087cf9))
+* 修复mysql下购买套餐加量包无效的bug ([c26ad4c](https://github.com/certd/certd/commit/c26ad4c8075f0606d45b8da13915737968d6191a))
+
+### Performance Improvements
+
+* 创建证书时支持选择通知时机 ([0e96bfd](https://github.com/certd/certd/commit/0e96bfdfa377824d204e72923d1176408ae6b300))
+* 创建k8s secret 时设置type为tls ([79ebabf](https://github.com/certd/certd/commit/79ebabfcfb9e5a534049c84f5f1a642b357fc856))
+* 去掉宝塔url后面的斜杠 ([8a0c2b9](https://github.com/certd/certd/commit/8a0c2b9b13628da750c25757e0cb8ed3038775ba))
+* 商业版隐藏文档相关链接 ([4443a1c](https://github.com/certd/certd/commit/4443a1c0308fa6b95a05efd73d15d24b65d641c9))
+* 商业版隐藏文档相关链接 ([db89561](https://github.com/certd/certd/commit/db8956148083bc4f988226ccf719940d08158a27))
+* 增加健康检查探针 /health/liveliness 和 /health/readiness ([44019e1](https://github.com/certd/certd/commit/44019e104289fedd32a867db00e9c6cb71b389cc))
+* 支持根据id更新证书(证书Id不变接口),不过该接口为白名单功能,普通腾讯云账户无法使用 ([fe9c4f3](https://github.com/certd/certd/commit/fe9c4f3391ff07c01dd9a252225f69a129c39050))
+* 支持godaddy ([b7980aa](https://github.com/certd/certd/commit/b7980aad5ab50f58662eaddf5d84aa82876a98eb))
+* 支持ssl.com证书颁发机构 ([27b6dfa](https://github.com/certd/certd/commit/27b6dfa4d2ab3bddd284c3a34511a72e1a513a4c))
+* 子域名托管说明 ([39a0223](https://github.com/certd/certd/commit/39a02235cf4416bb5bd1acd3831241efeaa2f602))
+* ssh 增加超时断开连接,默认10分钟超时 ([c24a040](https://github.com/certd/certd/commit/c24a040c19cacafc79228d7a7649af93837d94a1))
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
### Bug Fixes
diff --git a/README.md b/README.md
index fc5be4673..f0960aafe 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Certd
-[English](./README_en.md) | [中文](./README.md)
+中文 | [English](./README_en.md)
Certd® 是一个免费的全自动证书管理系统,让你的网站证书永不过期。
后缀d取自linux守护进程的命名风格,意为证书守护进程
@@ -152,7 +152,7 @@ https://certd.handfree.work/
## 八、捐赠
************************
-支持开源,为爱发电,我已入驻爱发电
+支持开源,为爱发电,我已入驻爱发电
https://afdian.com/a/greper
发电权益:
@@ -171,6 +171,7 @@ https://afdian.com/a/greper
| 自动部署插件 | 阿里云CDN、腾讯云、七牛CDN、主机部署、宝塔、1Panel等大部分插件 | 群晖 |
| 通知 | 邮件通知、自定义webhook | 邮件免配置、企微、钉钉、飞书、anpush、server酱等 |
+************************
************************
diff --git a/README_en.md b/README_en.md
index 57f8ea3aa..d665a9825 100644
--- a/README_en.md
+++ b/README_en.md
@@ -1,6 +1,6 @@
# Certd
-[English](./README_en.md) | [中文](./README.md)
+[中文](./README.md) | English
Certd® is a free, fully automated certificate management system that ensures your website certificates never expire. The suffix 'd' is inspired by the naming convention of Linux daemons, representing a certificate daemon.
@@ -134,6 +134,8 @@ You can also add the author as a friend.
| QR Code |
|
## 8. Donation
+************************
+ [](https://github.com/sponsors/greper)
************************
Support open-source projects and contribute with love. I've joined Afdian.
https://afdian.com/a/greper
diff --git a/build.trigger b/build.trigger
index bacbe4702..33594b85c 100644
--- a/build.trigger
+++ b/build.trigger
@@ -1 +1 @@
-00:43
+21:09
diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts
index 8ac252506..1ecd238c2 100644
--- a/docs/.vitepress/config.ts
+++ b/docs/.vitepress/config.ts
@@ -107,7 +107,6 @@ export default defineConfig({
text: "常见问题",
items: [
{text: "QA", link: "/guide/qa/use.md"},
- {text: "常见报错处理", link: "/guide/qa/"},
{text: "群晖证书部署", link: "/guide/use/synology/"},
{text: "腾讯云密钥获取", link: "/guide/use/tencent/"},
{text: "连接windows主机", link: "/guide/use/host/windows.md"},
@@ -120,6 +119,7 @@ export default defineConfig({
{text: "邮箱配置", link: "/guide/use/email/index.md"},
{text: "IPv6支持", link: "/guide/use/setting/ipv6.md"},
{text: "ESXi", link: "/guide/use/ESXi/index.md"},
+ {text: "宝塔动态IP白名单", link: "/guide/use/baota/white_list.md"},
{text: "子域名托管", link: "/guide/use/cert/subdomain.md"},
]
},
diff --git a/docs/guide/changelogs/CHANGELOG.md b/docs/guide/changelogs/CHANGELOG.md
index 317406481..40d3e5b3d 100644
--- a/docs/guide/changelogs/CHANGELOG.md
+++ b/docs/guide/changelogs/CHANGELOG.md
@@ -3,6 +3,56 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+### Bug Fixes
+
+* 修复导入插件对话框无法打开的bug,修复插件编辑页面打开多个代码编辑器消失的bug ([e5a080a](https://github.com/certd/certd/commit/e5a080aebe0d2f3e3c0f86bf863f75069c1bf7ab))
+* 修复ssl.com报EMAILADDRESS数量不对的bug ([c560cc5](https://github.com/certd/certd/commit/c560cc5adda6e15bf3a8865d874042550a6c2688))
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+### Bug Fixes
+
+* 修复商业版退出登录后,丢失站点个性化设置的bug ([d75dd05](https://github.com/certd/certd/commit/d75dd058d65c85f80c49e1fa7a910e6c6f08e824))
+* 修复授权类型和名称字段排到最后的bug ([43b7977](https://github.com/certd/certd/commit/43b79778ea9034065f6a15af3296274315597c6b))
+* 修复证书监控某些情况下报 options.lookup不能为null的bug ([d2ecfe5](https://github.com/certd/certd/commit/d2ecfe5491b2639eb30b5cae293af6062d58bb9f))
+* 修复证书手动托管时新上传的证书无效的bug ([506385e](https://github.com/certd/certd/commit/506385e5a2600887fe30854e0713583caaa2e689))
+* 修复secret patch 类型多了type:的bug ([d04f383](https://github.com/certd/certd/commit/d04f3831611011a90ec0594724b9694490d5edd0))
+
+### Performance Improvements
+
+* 登录支持极验验证码 ([370db62](https://github.com/certd/certd/commit/370db62bf0aece241859244927beabba32d6a257))
+* 登录注册、找回密码都支持极验验证码和图片验证码 ([7bdde68](https://github.com/certd/certd/commit/7bdde68ecea29fe2c570fd3cb082139db6c93d93))
+* 优化加量包展示效果 ([3c65f37](https://github.com/certd/certd/commit/3c65f37d84177ba107d4a6462648af12d2fc4b7a))
+* 证书到期剩余天数进度条根据实际证书有效期计算 ([#528](https://github.com/certd/certd/issues/528)) nicheng-he ([2d4586b](https://github.com/certd/certd/commit/2d4586b1c42c39f97d2a95b9453cca4bc8bfbe61))
+* add preferred chain option ([#519](https://github.com/certd/certd/issues/519)) @ZeroClover ([902359f](https://github.com/certd/certd/commit/902359f24ed12eee4f9b65178f1d6a60378351d2))
+* ssh配置增加脚本类型设置,bash还是sh ([ae41c60](https://github.com/certd/certd/commit/ae41c6038b27c9476e64a2402a8daf247c38a5b6))
+* start.sh增加sudo ([b7271d7](https://github.com/certd/certd/commit/b7271d7a464773a1bf87d7d1f24d933ba0f86915))
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+### Bug Fixes
+
+* 前置任务输出不存在时输出警告提示 ([b59052c](https://github.com/certd/certd/commit/b59052cc43b7b070fabd8b8e914e4c2a5e0ad61c))
+* 修复批量流水线执行时日志显示错乱的问题 ([4372adc](https://github.com/certd/certd/commit/4372adc703b9a4c785664054ab2a533626d815a8))
+* 修复远程数据选择无法过滤的bug ([6cbb073](https://github.com/certd/certd/commit/6cbb0739f8428d51b0712f718fe4d236cc087cf9))
+* 修复mysql下购买套餐加量包无效的bug ([c26ad4c](https://github.com/certd/certd/commit/c26ad4c8075f0606d45b8da13915737968d6191a))
+
+### Performance Improvements
+
+* 创建证书时支持选择通知时机 ([0e96bfd](https://github.com/certd/certd/commit/0e96bfdfa377824d204e72923d1176408ae6b300))
+* 创建k8s secret 时设置type为tls ([79ebabf](https://github.com/certd/certd/commit/79ebabfcfb9e5a534049c84f5f1a642b357fc856))
+* 去掉宝塔url后面的斜杠 ([8a0c2b9](https://github.com/certd/certd/commit/8a0c2b9b13628da750c25757e0cb8ed3038775ba))
+* 商业版隐藏文档相关链接 ([4443a1c](https://github.com/certd/certd/commit/4443a1c0308fa6b95a05efd73d15d24b65d641c9))
+* 商业版隐藏文档相关链接 ([db89561](https://github.com/certd/certd/commit/db8956148083bc4f988226ccf719940d08158a27))
+* 增加健康检查探针 /health/liveliness 和 /health/readiness ([44019e1](https://github.com/certd/certd/commit/44019e104289fedd32a867db00e9c6cb71b389cc))
+* 支持根据id更新证书(证书Id不变接口),不过该接口为白名单功能,普通腾讯云账户无法使用 ([fe9c4f3](https://github.com/certd/certd/commit/fe9c4f3391ff07c01dd9a252225f69a129c39050))
+* 支持godaddy ([b7980aa](https://github.com/certd/certd/commit/b7980aad5ab50f58662eaddf5d84aa82876a98eb))
+* 支持ssl.com证书颁发机构 ([27b6dfa](https://github.com/certd/certd/commit/27b6dfa4d2ab3bddd284c3a34511a72e1a513a4c))
+* 子域名托管说明 ([39a0223](https://github.com/certd/certd/commit/39a02235cf4416bb5bd1acd3831241efeaa2f602))
+* ssh 增加超时断开连接,默认10分钟超时 ([c24a040](https://github.com/certd/certd/commit/c24a040c19cacafc79228d7a7649af93837d94a1))
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
### Bug Fixes
diff --git a/docs/guide/install/baota/index.md b/docs/guide/install/baota/index.md
index ff4512451..bf173b930 100644
--- a/docs/guide/install/baota/index.md
+++ b/docs/guide/install/baota/index.md
@@ -21,13 +21,13 @@
#### 2.2 容器编排方式部署
-1. 打开`docker-compose.yaml`,整个内容复制下来
+1. 打开`docker-compose.yaml`,整个内容复制下来
https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml
-然后到宝塔里面进到docker->容器编排->添加容器编排
-
-点击确定,等待启动完成
+然后到宝塔里面进到docker->容器编排->添加容器编排
+
+点击确定,等待启动完成

> certd默认使用sqlite数据库,另外支持`mysql`和`postgresql`数据库,[点我了解如何切换其他数据库](../database)
@@ -35,16 +35,16 @@
## 二、访问应用
-http://ip:7001
-https://ip:7002
-默认账号密码
-admin/123456
+http://ip:7001
+https://ip:7002
+默认账号密码
+admin/123456
登录后请及时修改密码
## 三、如何升级
宝塔升级certd非常简单
-打开容器页面: `docker`->`容器编排`->`左侧选择Certd`->`更新镜像`
+打开容器页面: `docker`->`容器编排`->`左侧选择Certd`->`更新镜像`

@@ -80,5 +80,8 @@ admin/123456
### 1. 无法访问Certd
1. 确认服务器的安全规则,是否放开了对应端口
2. 确认宝塔防火墙是否放开对应端口
-3. 尝试将Certd容器加入宝塔的`bridge`网络
-
\ No newline at end of file
+3. 尝试将Certd容器加入宝塔的`bridge`网络
+
+
+### 2. 动态IP无法加白名单问题
+[Nginx代理解决方案](../../use/baota/white_list.md)
\ No newline at end of file
diff --git a/docs/guide/install/source/index.md b/docs/guide/install/source/index.md
index 8f404264b..3c25c889d 100644
--- a/docs/guide/install/source/index.md
+++ b/docs/guide/install/source/index.md
@@ -12,7 +12,7 @@ git clone https://github.com/certd/certd --depth=1
# git checkout v1.x.x # 当v2主干分支代码无法正常启动时,可以尝试此命令,1.x.x换成最新版本号
cd certd
# 启动服务
-./start.sh
+./start.sh
```
>如果是windows,请先安装`git for windows` ,然后右键,选择`open git bash here`打开终端,再执行`./start.sh`命令
@@ -21,9 +21,9 @@ cd certd
### 访问测试
-http://your_server_ip:7001
-https://your_server_ip:7002
-默认账号密码:admin/123456
+http://your_server_ip:7001
+https://your_server_ip:7002
+默认账号密码:admin/123456
记得修改密码
@@ -37,7 +37,7 @@ cp -rf ./packages/ui/certd-server/data ../certd-data-backup
git pull
# 如果提示pull失败,可以尝试强制更新
-# git checkout v2 -f && git pull
+# git checkout v2 -f && git pull
# 先停止旧的服务,7001是certd的默认端口
kill -9 $(lsof -t -i:7001)
@@ -45,16 +45,31 @@ kill -9 $(lsof -t -i:7001)
./start.sh
```
-::: warning
-升级certd版本前,切记切记先备份一下数据
+::: warning
+升级certd版本前,切记切记先备份一下数据
:::
## 三、数据备份
-> 数据默认保存在 `./packages/ui/certd-server/data` 目录下
+> 数据默认保存在 `./packages/ui/certd-server/data` 目录下
> 建议配置一条[数据库备份流水线](../../use/backup/) 自动备份
## 四、备份恢复
将备份的`db.sqlite`及同目录下的其他文件覆盖到原来的位置,重启certd即可
+
+## 六、常见问题
+
+### 1. npm install better-sqlite3 时,提示node-gyp需要vscode环境编译
+
+1. 首先确保node版本为22以上
+2. 将下面两行加到 ~/.npmrc 里面
+3. 重新install
+> better_sqlite3_binary_host=https://registry.npmmirror.com/-/binary/better-sqlite3
+> better_sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/better-sqlite3
+
+
+
+
+
diff --git a/docs/guide/qa/use.md b/docs/guide/qa/use.md
index 36f3ce4db..c75407dd6 100644
--- a/docs/guide/qa/use.md
+++ b/docs/guide/qa/use.md
@@ -1,4 +1,4 @@
-# 使用问题
+# 常见问题
## 1. 是否支持IP证书
@@ -7,8 +7,27 @@
## 2. 建议设置多长时间运行一次流水线
-建议每天运行一次,检查证书过期时间
+建议每天运行一次,检查证书过期时间
当证书没过期时,自动跳过部署
当证书到期前35天(创建流水线时可以修改),将会自动重新申请证书,自动部署
+## 3. too many certificates 错误
+当出现如下报错时,说明相同的域名短时间内申请超过5次
+解决方案:可以加多一个子域名,重新执行就可以规避次错误
+```
+"detail": too many certificates (5) already issued for this exact set of idantifiers in the last 168hm0s
+```
+
+## ssl.com报错 CAA record does not include ssl.com which is required to issue the certificate
+ssl.com申请证书要求必须设置CAA记录,表示允许ssl.com为该域名颁发证书
+请按如下格式添加CAA记录
+
+| 示例 | 类型 | 域名前缀 | flag | tag | 值 |
+|-------|-----| -- |-----------|--------|----------------------|
+| 顶级域名 | CAA | @ | 0 | issue | "ssl.com" (注意有双引号) |
+| 一级泛域名 | CAA | * | 0 | issue/issuewild | "ssl.com" |
+| 固定子域名 | CAA | sub | 0 | issue |"ssl.com" |
+
+
+
diff --git a/docs/guide/use/baota/images/white-1.png b/docs/guide/use/baota/images/white-1.png
new file mode 100644
index 000000000..f72b7bdaa
Binary files /dev/null and b/docs/guide/use/baota/images/white-1.png differ
diff --git a/docs/guide/use/baota/images/white-2.png b/docs/guide/use/baota/images/white-2.png
new file mode 100644
index 000000000..e23da95a6
Binary files /dev/null and b/docs/guide/use/baota/images/white-2.png differ
diff --git a/docs/guide/use/baota/images/white-3.png b/docs/guide/use/baota/images/white-3.png
new file mode 100644
index 000000000..33ba42630
Binary files /dev/null and b/docs/guide/use/baota/images/white-3.png differ
diff --git a/docs/guide/use/baota/images/white-4.png b/docs/guide/use/baota/images/white-4.png
new file mode 100644
index 000000000..db7380655
Binary files /dev/null and b/docs/guide/use/baota/images/white-4.png differ
diff --git a/docs/guide/use/baota/images/white-5.png b/docs/guide/use/baota/images/white-5.png
new file mode 100644
index 000000000..d151d7796
Binary files /dev/null and b/docs/guide/use/baota/images/white-5.png differ
diff --git a/docs/guide/use/baota/images/white-6.png b/docs/guide/use/baota/images/white-6.png
new file mode 100644
index 000000000..93fb84c8b
Binary files /dev/null and b/docs/guide/use/baota/images/white-6.png differ
diff --git a/docs/guide/use/baota/images/white-safe-1.png b/docs/guide/use/baota/images/white-safe-1.png
new file mode 100644
index 000000000..818ee68de
Binary files /dev/null and b/docs/guide/use/baota/images/white-safe-1.png differ
diff --git a/docs/guide/use/baota/images/white-safe-2.png b/docs/guide/use/baota/images/white-safe-2.png
new file mode 100644
index 000000000..8876a335f
Binary files /dev/null and b/docs/guide/use/baota/images/white-safe-2.png differ
diff --git a/docs/guide/use/baota/white_list.md b/docs/guide/use/baota/white_list.md
new file mode 100644
index 000000000..778e225d3
--- /dev/null
+++ b/docs/guide/use/baota/white_list.md
@@ -0,0 +1,98 @@
+# 宝塔IP白名单与动态IP问题
+调用宝塔接口需要添加IP白名单,但当certd部署在动态IP环境下时,IP白名单就不好添加
+本章节提供两种解决方案:
+1. 小范围网段放开(简单)
+2. nginx代理
+
+## 一、放开小范围网段
+
+家庭网络IP虽然会变动,但是只会在小范围变的。
+
+你可以分析规律,将变动的部分,设置成网段即可
+
+> 比如出现过: 100.25.1.5 , 100.25.1.8
+>
+> 那么你可以配置 100.25.1.1-100.25.1.255
+
+
+> 如果出现过: 100.25.1.5 , 100.25.4.8
+>
+> 可以尝试配置 100.25.*.5
+
+## 二、nginx代理方案
+
+通过在宝塔中配置一个nginx反向代理,代理宝塔自己的地址
+
+然后在nginx中配置放开certd需要的接口,缩小影响范围
+
+让nginx来充当防火墙
+
+架构图如下:
+```
+ 只要将127.0.0.1加入白名单即可
+ ↓
+certd --------> nginx -------> 宝塔
+ ↑
+ 拦截除更新证书之外的地址
+```
+
+### 1. 添加nginx反向代理
+
+
+### 2. 域名和代理目标
+
+
+### 3. 设置放开哪些接口
+
+
+将如下脚本填入上方文本域中,保存
+```nginx configuration
+set $allow_access false;
+
+ # 检查请求的URI是否在白名单中
+ if ($request_uri ~* "^/(site\?action=get_site_types)") {
+ # 允许测试
+ set $allow_access true;
+ }
+ if ($request_uri ~* "^/(config\?action=SavePanelSSL)") {
+ # 允许部署到宝塔面板本身证书
+ set $allow_access true;
+ }
+
+ if ($request_uri ~* "^/(mod/docker/com/set_ssl|site\?action=SetSSL|ssl\?action=GetSiteDomain|mod/docker/com/get_site_list)") {
+ # 允许部署宝塔网站证书
+ set $allow_access true;
+ }
+
+ if ($request_uri ~* "^/(ssl?action=remove_cloud_cert|ssl\?action=get_cert_list)") {
+ # 允许删除宝塔过期证书
+ set $allow_access true;
+ }
+
+ if ($request_uri ~* "^/(datalist/get_data_list|site/set_site_ssl)") {
+ set $allow_access true;
+ }
+
+ # 如果不在白名单,返回403禁止访问
+ if ($allow_access = false) {
+ return 405;
+ }
+
+```
+
+
+### 4. 接口IP白名单添加127.0.0.1
+ 
+
+### 5. certd中宝塔授权配置改成新的这个域名地址
+
+
+点击测试检查是否ok ,到这里就可以正常部署证书了
+
+### 6. 安全加强(将请求地址改成https)
+在宝塔中配置证书部署任务,选择刚才新建的这个网站,给他部署证书
+勾选强制https
+
+更换443端口【可选】
+
+禁止http访问
diff --git a/lerna.json b/lerna.json
index 26a7f3583..59fb7d523 100644
--- a/lerna.json
+++ b/lerna.json
@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
- "version": "1.36.18"
+ "version": "1.36.21"
}
diff --git a/packages/core/acme-client/CHANGELOG.md b/packages/core/acme-client/CHANGELOG.md
index 329ae74ae..af65c4ac9 100644
--- a/packages/core/acme-client/CHANGELOG.md
+++ b/packages/core/acme-client/CHANGELOG.md
@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/publishlab/node-acme-client/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+**Note:** Version bump only for package @certd/acme-client
+
+## [1.36.20](https://github.com/publishlab/node-acme-client/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+**Note:** Version bump only for package @certd/acme-client
+
+## [1.36.19](https://github.com/publishlab/node-acme-client/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+### Performance Improvements
+
+* 支持ssl.com证书颁发机构 ([27b6dfa](https://github.com/publishlab/node-acme-client/commit/27b6dfa4d2ab3bddd284c3a34511a72e1a513a4c))
+
## [1.36.18](https://github.com/publishlab/node-acme-client/compare/v1.36.17...v1.36.18) (2025-08-28)
**Note:** Version bump only for package @certd/acme-client
diff --git a/packages/core/acme-client/package.json b/packages/core/acme-client/package.json
index d77cfec87..c5e9cf774 100644
--- a/packages/core/acme-client/package.json
+++ b/packages/core/acme-client/package.json
@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
- "version": "1.36.18",
+ "version": "1.36.21",
"type": "module",
"module": "scr/index.js",
"main": "src/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
- "@certd/basic": "^1.36.18",
+ "@certd/basic": "^1.36.21",
"@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5",
"axios": "^1.7.2",
@@ -69,5 +69,5 @@
"bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues"
},
- "gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
+ "gitHead": "3cedef4974708d828fb972acc54af0515e3ec3a0"
}
diff --git a/packages/core/acme-client/src/client.js b/packages/core/acme-client/src/client.js
index f6b389fa3..b2ff14dc5 100644
--- a/packages/core/acme-client/src/client.js
+++ b/packages/core/acme-client/src/client.js
@@ -502,7 +502,7 @@ class AcmeClient {
await verify[challenge.type](authz, challenge, keyAuthorization);
};
- log('Waiting for ACME challenge verification(等待ACME挑战验证)', this.backoffOpts);
+ log('Waiting for ACME challenge verification(等待ACME挑战验证)');
return util.retry(verifyFn, this.backoffOpts);
}
diff --git a/packages/core/acme-client/src/index.js b/packages/core/acme-client/src/index.js
index 99cb041fc..a40d9544d 100644
--- a/packages/core/acme-client/src/index.js
+++ b/packages/core/acme-client/src/index.js
@@ -25,6 +25,10 @@ export const directory = {
staging: 'https://acme.zerossl.com/v2/DV90',
production: 'https://acme.zerossl.com/v2/DV90',
},
+ sslcom:{
+ staging: 'https://acme.ssl.com/sslcom-dv-rsa',
+ production: 'https://acme.ssl.com/sslcom-dv-rsa',
+ }
};
/**
diff --git a/packages/core/basic/CHANGELOG.md b/packages/core/basic/CHANGELOG.md
index d8c3f8a14..82852c212 100644
--- a/packages/core/basic/CHANGELOG.md
+++ b/packages/core/basic/CHANGELOG.md
@@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+**Note:** Version bump only for package @certd/basic
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+**Note:** Version bump only for package @certd/basic
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+### Bug Fixes
+
+* 修复批量流水线执行时日志显示错乱的问题 ([4372adc](https://github.com/certd/certd/commit/4372adc703b9a4c785664054ab2a533626d815a8))
+
+### Performance Improvements
+
+* 去掉宝塔url后面的斜杠 ([8a0c2b9](https://github.com/certd/certd/commit/8a0c2b9b13628da750c25757e0cb8ed3038775ba))
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
**Note:** Version bump only for package @certd/basic
diff --git a/packages/core/basic/build.md b/packages/core/basic/build.md
index 4316bd657..c98fede4d 100644
--- a/packages/core/basic/build.md
+++ b/packages/core/basic/build.md
@@ -1 +1 @@
-00:39
+20:59
diff --git a/packages/core/basic/package.json b/packages/core/basic/package.json
index def44d2fe..3cc51a061 100644
--- a/packages/core/basic/package.json
+++ b/packages/core/basic/package.json
@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
- "version": "1.36.18",
+ "version": "1.36.21",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -45,5 +45,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
- "gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
+ "gitHead": "3cedef4974708d828fb972acc54af0515e3ec3a0"
}
diff --git a/packages/core/basic/src/utils/util.log.ts b/packages/core/basic/src/utils/util.log.ts
index 6f6709aad..fbcc87e2e 100644
--- a/packages/core/basic/src/utils/util.log.ts
+++ b/packages/core/basic/src/utils/util.log.ts
@@ -1,22 +1,4 @@
-import log4js, { LoggingEvent, Logger } from "log4js";
-
-const OutputAppender = {
- configure: (config: any, layouts: any, findAppender: any, levels: any) => {
- let layout = layouts.basicLayout;
- if (config.layout) {
- layout = layouts.layout(config.layout.type, config.layout);
- }
- function customAppender(layout: any, timezoneOffset: any) {
- return (loggingEvent: LoggingEvent) => {
- if (loggingEvent.context.outputHandler?.write) {
- const text = `${layout(loggingEvent, timezoneOffset)}\n`;
- loggingEvent.context.outputHandler.write(text);
- }
- };
- }
- return customAppender(layout, config.timezoneOffset);
- },
-};
+import log4js, { CallStack, Level } from "log4js";
let logFilePath = "./logs/app.log";
export function resetLogConfigure() {
@@ -24,7 +6,6 @@ export function resetLogConfigure() {
log4js.configure({
appenders: {
std: { type: "stdout" },
- output: { type: OutputAppender },
file: {
type: "dateFile",
filename: logFilePath,
@@ -33,7 +14,7 @@ export function resetLogConfigure() {
numBackups: 3,
},
},
- categories: { default: { appenders: ["std", "file"], level: "info" }, pipeline: { appenders: ["std", "file", "output"], level: "info" } },
+ categories: { default: { appenders: ["std", "file"], level: "info" }, pipeline: { appenders: ["std", "file"], level: "info" } },
});
}
resetLogConfigure();
@@ -44,15 +25,98 @@ export function resetLogFilePath(filePath: string) {
resetLogConfigure();
}
export function buildLogger(write: (text: string) => void) {
- const logger = log4js.getLogger("pipeline");
- const _secrets: string[] = [];
- //@ts-ignore
- logger.addSecret = (secret: string) => {
- _secrets.push(secret);
- };
- logger.addContext("outputHandler", {
- write: (text: string) => {
- for (const item of _secrets) {
+ return new PipelineLogger("pipeline", write);
+}
+
+export type ILogger = {
+ readonly category: string;
+ level: Level | string;
+ log(level: Level | string, ...args: any[]): void;
+
+ isLevelEnabled(level?: string): boolean;
+
+ isTraceEnabled(): boolean;
+ isDebugEnabled(): boolean;
+ isInfoEnabled(): boolean;
+ isWarnEnabled(): boolean;
+ isErrorEnabled(): boolean;
+ isFatalEnabled(): boolean;
+
+ _log(level: Level, data: any): void;
+
+ addContext(key: string, value: any): void;
+
+ removeContext(key: string): void;
+
+ clearContext(): void;
+
+ /**
+ * Replace the basic parse function with a new custom one
+ * - Note that linesToSkip will be based on the origin of the Error object in addition to the callStackLinesToSkip (at least 1)
+ * @param parseFunction the new parseFunction. Use `undefined` to reset to the base implementation
+ */
+ setParseCallStackFunction(parseFunction: (error: Error, linesToSkip: number) => CallStack | undefined): void;
+
+ /**
+ * Adjust the value of linesToSkip when the parseFunction is called.
+ *
+ * Cannot be less than 0.
+ */
+ callStackLinesToSkip: number;
+
+ trace(message: any, ...args: any[]): void;
+
+ debug(message: any, ...args: any[]): void;
+
+ info(message: any, ...args: any[]): void;
+
+ warn(message: any, ...args: any[]): void;
+
+ error(message: any, ...args: any[]): void;
+
+ fatal(message: any, ...args: any[]): void;
+
+ mark(message: any, ...args: any[]): void;
+};
+
+const locale = Intl.DateTimeFormat().resolvedOptions().locale;
+const formatter = new Intl.DateTimeFormat(locale, {
+ year: "numeric",
+ month: "2-digit",
+ day: "2-digit",
+ hour: "2-digit",
+ minute: "2-digit",
+ second: "2-digit",
+ hour12: false,
+});
+function formatDateIntl(date = new Date()) {
+ const milliseconds = date.getMilliseconds(); // 获取毫秒
+ const formattedMilliseconds = milliseconds.toString().padStart(3, "0");
+ return formatter.format(date) + "." + formattedMilliseconds;
+}
+
+// @ts-ignore
+export class PipelineLogger implements ILogger {
+ callStackLinesToSkip: number = 3;
+ readonly category: string = "pipeline";
+ level: Level | string = "info";
+ _secrets: string[] = [];
+ logger: ILogger;
+ customWriter!: (text: string) => void;
+
+ constructor(name: string, write: (text: string) => void) {
+ this.customWriter = write;
+ this.logger = log4js.getLogger(name);
+ }
+
+ addSecret(secret: string) {
+ this._secrets.push(secret);
+ }
+
+ _doLog(level: string, ...args: any[]) {
+ let text = args.join(" ");
+ if (this.customWriter) {
+ for (const item of this._secrets) {
if (item == null) {
continue;
}
@@ -66,10 +130,88 @@ export function buildLogger(write: (text: string) => void) {
text = text.replaceAll(item, "*".repeat(item.length));
}
}
- write(text);
- },
- });
- return logger;
-}
+ text = `[${formatDateIntl()}] [${level.toUpperCase()}] - ${text} \n`;
+ this.customWriter(text);
+ }
+ // @ts-ignore
+ this.logger[level](...args);
+ }
-export type ILogger = Logger;
+ _log(level: Level, data: any): void {}
+
+ addContext(key: string, value: any): void {}
+
+ clearContext(): void {}
+
+ debug(message: any, ...args: any[]): void {
+ if (this.isDebugEnabled()) {
+ this._doLog("debug", message, ...args);
+ }
+ }
+
+ error(message: any, ...args: any[]): void {
+ if (this.isErrorEnabled()) {
+ this._doLog("error", message, ...args);
+ }
+ }
+
+ fatal(message: any, ...args: any[]): void {
+ if (this.isFatalEnabled()) {
+ this._doLog("fatal", message, ...args);
+ }
+ }
+
+ info(message: any, ...args: any[]): void {
+ if (this.isInfoEnabled()) {
+ this._doLog("info", message, ...args);
+ }
+ }
+
+ trace(message: any, ...args: any[]): void {
+ if (this.isTraceEnabled()) {
+ this._doLog("trace", message, ...args);
+ }
+ }
+
+ warn(message: any, ...args: any[]): void {
+ if (this.isWarnEnabled()) {
+ this._doLog("warn", message, ...args);
+ }
+ }
+
+ isDebugEnabled(): boolean {
+ return logger.isDebugEnabled();
+ }
+
+ isErrorEnabled(): boolean {
+ return logger.isErrorEnabled();
+ }
+
+ isFatalEnabled(): boolean {
+ return logger.isFatalEnabled();
+ }
+
+ isInfoEnabled(): boolean {
+ return logger.isInfoEnabled();
+ }
+
+ isLevelEnabled(level?: string): boolean {
+ return logger.isLevelEnabled();
+ }
+
+ isTraceEnabled(): boolean {
+ return logger.isTraceEnabled();
+ }
+
+ isWarnEnabled(): boolean {
+ return logger.isWarnEnabled();
+ }
+
+ log(level: Level | string, ...args: any[]): void {}
+
+ mark(message: any, ...args: any[]): void {}
+
+ removeContext(key: string): void {}
+
+ setParseCallStackFunction(parseFunction: (error: Error, linesToSkip: number) => CallStack | undefined): void {}
+}
diff --git a/packages/core/basic/src/utils/util.request.ts b/packages/core/basic/src/utils/util.request.ts
index 39f41a2ad..ea7d83027 100644
--- a/packages/core/basic/src/utils/util.request.ts
+++ b/packages/core/basic/src/utils/util.request.ts
@@ -1,6 +1,5 @@
import axios, { AxiosHeaders, AxiosRequestConfig } from "axios";
import { ILogger, logger } from "./util.log.js";
-import { Logger } from "log4js";
import { HttpProxyAgent } from "http-proxy-agent";
import { HttpsProxyAgent } from "https-proxy-agent";
import nodeHttp from "http";
@@ -8,6 +7,13 @@ import * as https from "node:https";
import { merge } from "lodash-es";
import { safePromise } from "./util.promise.js";
import fs from "fs";
+
+const errorMap: Record = {
+ "ssl3_get_record:wrong version number": "http协议错误,服务端要求http协议,请检查是否使用了https请求",
+ "getaddrinfo EAI_AGAIN": "无法解析域名,请检查网络连接或dns配置,更换docker-compose.yaml中dns配置",
+ "self-signed certificate": "目标站点为自签名证书,请勾选忽略证书校验",
+};
+
export class HttpError extends Error {
status?: number;
statusText?: string;
@@ -22,11 +28,12 @@ export class HttpError extends Error {
super(error.message || error.response?.statusText);
const message = error?.message;
- if (message && typeof message === "string") {
- if (message.indexOf && message.indexOf("ssl3_get_record:wrong version number") >= 0) {
- this.message = `${message}(http协议错误,服务端要求http协议,请检查是否使用了https请求)`;
- } else if (message.indexOf("getaddrinfo EAI_AGAIN") >= 0) {
- this.message = `${message}(无法解析域名,请检查网络连接或dns配置,更换docker-compose.yaml中dns配置)`;
+ if (message && typeof message === "string" && message.indexOf) {
+ for (const key in errorMap) {
+ if (message.indexOf(key) > -1) {
+ this.message = `${this.message}(${errorMap[key]})`;
+ break;
+ }
}
}
@@ -84,7 +91,7 @@ export function getGlobalAgents() {
/**
* @description 创建请求实例
*/
-export function createAxiosService({ logger }: { logger: Logger }) {
+export function createAxiosService({ logger }: { logger: ILogger }) {
// 创建一个 axios 实例
const service = axios.create();
diff --git a/packages/core/pipeline/CHANGELOG.md b/packages/core/pipeline/CHANGELOG.md
index d954ea085..e8f742a0e 100644
--- a/packages/core/pipeline/CHANGELOG.md
+++ b/packages/core/pipeline/CHANGELOG.md
@@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+**Note:** Version bump only for package @certd/pipeline
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+**Note:** Version bump only for package @certd/pipeline
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+### Bug Fixes
+
+* 前置任务输出不存在时输出警告提示 ([b59052c](https://github.com/certd/certd/commit/b59052cc43b7b070fabd8b8e914e4c2a5e0ad61c))
+
+### Performance Improvements
+
+* 支持godaddy ([b7980aa](https://github.com/certd/certd/commit/b7980aad5ab50f58662eaddf5d84aa82876a98eb))
+* 支持ssl.com证书颁发机构 ([27b6dfa](https://github.com/certd/certd/commit/27b6dfa4d2ab3bddd284c3a34511a72e1a513a4c))
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
**Note:** Version bump only for package @certd/pipeline
diff --git a/packages/core/pipeline/package.json b/packages/core/pipeline/package.json
index 0dcf7704e..2f8914a81 100644
--- a/packages/core/pipeline/package.json
+++ b/packages/core/pipeline/package.json
@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
- "version": "1.36.18",
+ "version": "1.36.21",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -17,8 +17,8 @@
"pub": "npm publish"
},
"dependencies": {
- "@certd/basic": "^1.36.18",
- "@certd/plus-core": "^1.36.18",
+ "@certd/basic": "^1.36.21",
+ "@certd/plus-core": "^1.36.21",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"reflect-metadata": "^0.1.13"
@@ -44,5 +44,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
- "gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
+ "gitHead": "3cedef4974708d828fb972acc54af0515e3ec3a0"
}
diff --git a/packages/core/pipeline/src/context/index.ts b/packages/core/pipeline/src/context/index.ts
index 0dd6b147e..2d987bc02 100644
--- a/packages/core/pipeline/src/context/index.ts
+++ b/packages/core/pipeline/src/context/index.ts
@@ -21,9 +21,9 @@ export type PageRes = {
export class Pager {
pageNo: number;
pageSize: number;
- constructor(req: PageSearch) {
- this.pageNo = req.pageNo ?? 1;
- this.pageSize = req.pageSize || 50;
+ constructor(req?: PageSearch) {
+ this.pageNo = req?.pageNo ?? 1;
+ this.pageSize = req?.pageSize || 50;
}
getOffset() {
diff --git a/packages/core/pipeline/src/core/executor.ts b/packages/core/pipeline/src/core/executor.ts
index 81e4f78da..cb6f4d335 100644
--- a/packages/core/pipeline/src/core/executor.ts
+++ b/packages/core/pipeline/src/core/executor.ts
@@ -314,7 +314,7 @@ export class Executor {
const outputKey = arr[2];
input[key] = this.currentStatusMap.get(id)?.status?.output[outputKey] ?? this.lastStatusMap.get(id)?.status?.output[outputKey];
if (input[key] == null) {
- this.logger.warn(`${item.title}的配置未找到对应的输出值,请确认对应的前置任务是否存在或者是否执行正确`);
+ currentLogger.warn(`${item.title}的配置未找到对应的输出值,请确认对应的前置任务是否存在或者是否执行正确`);
}
}
}
diff --git a/packages/core/pipeline/src/plugin/api.ts b/packages/core/pipeline/src/plugin/api.ts
index eee946f7b..8cb10d199 100644
--- a/packages/core/pipeline/src/plugin/api.ts
+++ b/packages/core/pipeline/src/plugin/api.ts
@@ -253,9 +253,9 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
return name + "_" + dayjs().format("YYYYMMDDHHmmssSSS");
}
- buildCertName(domain: string) {
+ buildCertName(domain: string, prefix = "") {
domain = domain.replaceAll("*", "_").replaceAll(".", "_");
- return `${domain}_${dayjs().format("YYYYMMDDHHmmssSSS")}`;
+ return `${prefix}_${domain}_${dayjs().format("YYYYMMDDHHmmssSSS")}`;
}
async onRequest(req: PluginRequestHandleReq) {
diff --git a/packages/core/pipeline/src/registry/registry.ts b/packages/core/pipeline/src/registry/registry.ts
index b466bcc9f..3322d7080 100644
--- a/packages/core/pipeline/src/registry/registry.ts
+++ b/packages/core/pipeline/src/registry/registry.ts
@@ -69,9 +69,15 @@ export class Registry {
return this.storage;
}
- getDefineList() {
+ getDefineList(prefix?: string) {
let list = [];
+ if (prefix) {
+ prefix = prefix + ":";
+ }
for (const key in this.storage) {
+ if (prefix && !key.startsWith(prefix)) {
+ continue;
+ }
const define = this.getDefine(key);
if (define) {
if (define?.deprecated) {
@@ -90,7 +96,10 @@ export class Registry {
return list;
}
- getDefine(key: string) {
+ getDefine(key: string, prefix?: string) {
+ if (prefix) {
+ key = prefix + ":" + key;
+ }
const item = this.storage[key];
if (!item) {
return;
diff --git a/packages/libs/lib-huawei/CHANGELOG.md b/packages/libs/lib-huawei/CHANGELOG.md
index 5e582f6bd..1d653c0ab 100644
--- a/packages/libs/lib-huawei/CHANGELOG.md
+++ b/packages/libs/lib-huawei/CHANGELOG.md
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+**Note:** Version bump only for package @certd/lib-huawei
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+**Note:** Version bump only for package @certd/lib-huawei
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+**Note:** Version bump only for package @certd/lib-huawei
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
**Note:** Version bump only for package @certd/lib-huawei
diff --git a/packages/libs/lib-huawei/package.json b/packages/libs/lib-huawei/package.json
index bb3a33df8..b13af8f60 100644
--- a/packages/libs/lib-huawei/package.json
+++ b/packages/libs/lib-huawei/package.json
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
- "version": "1.36.18",
+ "version": "1.36.21",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -24,5 +24,5 @@
"prettier": "^2.8.8",
"tslib": "^2.8.1"
},
- "gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
+ "gitHead": "3cedef4974708d828fb972acc54af0515e3ec3a0"
}
diff --git a/packages/libs/lib-iframe/CHANGELOG.md b/packages/libs/lib-iframe/CHANGELOG.md
index f2a0a6938..3f16bd93d 100644
--- a/packages/libs/lib-iframe/CHANGELOG.md
+++ b/packages/libs/lib-iframe/CHANGELOG.md
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+**Note:** Version bump only for package @certd/lib-iframe
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+**Note:** Version bump only for package @certd/lib-iframe
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+**Note:** Version bump only for package @certd/lib-iframe
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
**Note:** Version bump only for package @certd/lib-iframe
diff --git a/packages/libs/lib-iframe/package.json b/packages/libs/lib-iframe/package.json
index 8466cce21..6456e9878 100644
--- a/packages/libs/lib-iframe/package.json
+++ b/packages/libs/lib-iframe/package.json
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
- "version": "1.36.18",
+ "version": "1.36.21",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -31,5 +31,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
- "gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
+ "gitHead": "3cedef4974708d828fb972acc54af0515e3ec3a0"
}
diff --git a/packages/libs/lib-jdcloud/CHANGELOG.md b/packages/libs/lib-jdcloud/CHANGELOG.md
index ffaf02e09..d261d92d3 100644
--- a/packages/libs/lib-jdcloud/CHANGELOG.md
+++ b/packages/libs/lib-jdcloud/CHANGELOG.md
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+**Note:** Version bump only for package @certd/jdcloud
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+**Note:** Version bump only for package @certd/jdcloud
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+**Note:** Version bump only for package @certd/jdcloud
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
**Note:** Version bump only for package @certd/jdcloud
diff --git a/packages/libs/lib-jdcloud/package.json b/packages/libs/lib-jdcloud/package.json
index df4e817ef..257c73c17 100644
--- a/packages/libs/lib-jdcloud/package.json
+++ b/packages/libs/lib-jdcloud/package.json
@@ -1,6 +1,6 @@
{
"name": "@certd/jdcloud",
- "version": "1.36.18",
+ "version": "1.36.21",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
@@ -61,5 +61,5 @@
"fetch"
]
},
- "gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
+ "gitHead": "3cedef4974708d828fb972acc54af0515e3ec3a0"
}
diff --git a/packages/libs/lib-k8s/CHANGELOG.md b/packages/libs/lib-k8s/CHANGELOG.md
index f7570468a..0c9f1d6cc 100644
--- a/packages/libs/lib-k8s/CHANGELOG.md
+++ b/packages/libs/lib-k8s/CHANGELOG.md
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+**Note:** Version bump only for package @certd/lib-k8s
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+### Bug Fixes
+
+* 修复secret patch 类型多了type:的bug ([d04f383](https://github.com/certd/certd/commit/d04f3831611011a90ec0594724b9694490d5edd0))
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+### Bug Fixes
+
+* 修复远程数据选择无法过滤的bug ([6cbb073](https://github.com/certd/certd/commit/6cbb0739f8428d51b0712f718fe4d236cc087cf9))
+
+### Performance Improvements
+
+* 创建k8s secret 时设置type为tls ([79ebabf](https://github.com/certd/certd/commit/79ebabfcfb9e5a534049c84f5f1a642b357fc856))
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
### Performance Improvements
diff --git a/packages/libs/lib-k8s/package.json b/packages/libs/lib-k8s/package.json
index 2ae6f74a0..fe978315a 100644
--- a/packages/libs/lib-k8s/package.json
+++ b/packages/libs/lib-k8s/package.json
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-k8s",
"private": false,
- "version": "1.36.18",
+ "version": "1.36.21",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -17,7 +17,7 @@
"pub": "npm publish"
},
"dependencies": {
- "@certd/basic": "^1.36.18",
+ "@certd/basic": "^1.36.21",
"@kubernetes/client-node": "0.21.0"
},
"devDependencies": {
@@ -32,5 +32,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
- "gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
+ "gitHead": "3cedef4974708d828fb972acc54af0515e3ec3a0"
}
diff --git a/packages/libs/lib-k8s/src/lib/k8s.client.ts b/packages/libs/lib-k8s/src/lib/k8s.client.ts
index 0e2fac476..d5f8f6bd0 100644
--- a/packages/libs/lib-k8s/src/lib/k8s.client.ts
+++ b/packages/libs/lib-k8s/src/lib/k8s.client.ts
@@ -1,7 +1,7 @@
import { CoreV1Api, KubeConfig, NetworkingV1Api, V1Ingress, V1Secret } from "@kubernetes/client-node";
import dns from "dns";
import { ILogger } from "@certd/basic";
-import _ from "lodash-es";
+import { merge } from "lodash-es";
export type K8sClientOpts = {
kubeConfigStr: string;
@@ -85,7 +85,6 @@ export class K8sClient {
/**
* 创建Secret
* @param opts {namespace:default, body:yamlStr}
- * @returns {Promise<*>}
*/
async createSecret(opts: { namespace: string; body: V1Secret }) {
const namespace = opts.namespace || "default";
@@ -119,7 +118,13 @@ export class K8sClient {
this.logger.warn(`secret ${secretName} 不存在`);
if (opts.createOnNotFound) {
//没有找到,则创建
- const res = await this.createSecret({ namespace, body: opts.body });
+ const body = merge(
+ {
+ type: "kubernetes.io/tls",
+ },
+ opts.body
+ );
+ const res = await this.createSecret({ namespace, body });
this.logger.info(`secret ${secretName} 已创建`);
return res;
}
@@ -127,7 +132,7 @@ export class K8sClient {
throw e;
}
- const newSecret = _.merge(oldSecret.body, opts.body);
+ const newSecret = merge(oldSecret.body, opts.body);
const res = await this.client.replaceNamespacedSecret(secretName, namespace, newSecret);
this.logger.info(`secret ${secretName} 已更新`);
return res.body;
@@ -161,7 +166,7 @@ export class K8sClient {
this.logger.info("patch ingress:", ingressName, namespace);
const client = this.kubeconfig.makeApiClient(NetworkingV1Api);
const oldIngress = await client.readNamespacedIngress(ingressName, namespace);
- const newIngress = _.merge(oldIngress.body, opts.body);
+ const newIngress = merge(oldIngress.body, opts.body);
const res = await client.replaceNamespacedIngress(ingressName, namespace, newIngress);
this.logger.info("ingress patched", opts.body);
return res;
diff --git a/packages/libs/lib-server/CHANGELOG.md b/packages/libs/lib-server/CHANGELOG.md
index 213e6268e..09d08cfdf 100644
--- a/packages/libs/lib-server/CHANGELOG.md
+++ b/packages/libs/lib-server/CHANGELOG.md
@@ -3,6 +3,21 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+**Note:** Version bump only for package @certd/lib-server
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+### Performance Improvements
+
+* 登录支持极验验证码 ([370db62](https://github.com/certd/certd/commit/370db62bf0aece241859244927beabba32d6a257))
+* 登录注册、找回密码都支持极验验证码和图片验证码 ([7bdde68](https://github.com/certd/certd/commit/7bdde68ecea29fe2c570fd3cb082139db6c93d93))
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+**Note:** Version bump only for package @certd/lib-server
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
**Note:** Version bump only for package @certd/lib-server
diff --git a/packages/libs/lib-server/package.json b/packages/libs/lib-server/package.json
index 329dfa97d..90f08b2fc 100644
--- a/packages/libs/lib-server/package.json
+++ b/packages/libs/lib-server/package.json
@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
- "version": "1.36.18",
+ "version": "1.36.21",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -27,10 +27,10 @@
],
"license": "AGPL",
"dependencies": {
- "@certd/acme-client": "^1.36.18",
- "@certd/basic": "^1.36.18",
- "@certd/pipeline": "^1.36.18",
- "@certd/plus-core": "^1.36.18",
+ "@certd/acme-client": "^1.36.21",
+ "@certd/basic": "^1.36.21",
+ "@certd/pipeline": "^1.36.21",
+ "@certd/plus-core": "^1.36.21",
"@midwayjs/cache": "~3.14.0",
"@midwayjs/core": "~3.20.3",
"@midwayjs/i18n": "~3.20.3",
@@ -61,5 +61,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
- "gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
+ "gitHead": "3cedef4974708d828fb972acc54af0515e3ec3a0"
}
diff --git a/packages/libs/lib-server/src/index.ts b/packages/libs/lib-server/src/index.ts
index a6d3f603b..f4bfaf593 100644
--- a/packages/libs/lib-server/src/index.ts
+++ b/packages/libs/lib-server/src/index.ts
@@ -1,8 +1,9 @@
import { SysSettingsEntity } from './system/index.js';
import { AccessEntity } from './user/access/entity/access.js';
+import { AddonEntity } from "./user/index.js";
export * from './basic/index.js';
export * from './system/index.js';
export * from './user/index.js';
export { LibServerConfiguration as Configuration } from './configuration.js';
-export const libServerEntities = [SysSettingsEntity, AccessEntity];
+export const libServerEntities = [SysSettingsEntity, AccessEntity,AddonEntity];
diff --git a/packages/libs/lib-server/src/system/settings/service/models.ts b/packages/libs/lib-server/src/system/settings/service/models.ts
index c31f14fbe..af7ddf052 100644
--- a/packages/libs/lib-server/src/system/settings/service/models.ts
+++ b/packages/libs/lib-server/src/system/settings/service/models.ts
@@ -30,6 +30,13 @@ export class SysPublicSettings extends BaseSettings {
mpsNo?: string;
robots?: boolean = true;
aiChatEnabled = true;
+
+
+ //验证码是否开启
+ captchaEnabled = false;
+ //验证码类型
+ captchaType?: string;
+ captchaAddonId?:number;
}
export class SysPrivateSettings extends BaseSettings {
@@ -207,4 +214,3 @@ export class SysSafeSetting extends BaseSettings {
};
}
-
diff --git a/packages/libs/lib-server/src/user/addon/api/api.ts b/packages/libs/lib-server/src/user/addon/api/api.ts
new file mode 100644
index 000000000..4e65ce86c
--- /dev/null
+++ b/packages/libs/lib-server/src/user/addon/api/api.ts
@@ -0,0 +1,97 @@
+import { HttpClient, ILogger, utils } from "@certd/basic";
+import {upperFirst} from "lodash-es";
+import { FormItemProps, PluginRequestHandleReq, Registrable } from "@certd/pipeline";
+
+
+export type AddonRequestHandleReqInput = {
+ id?: number;
+ title?: string;
+ addon: T;
+};
+
+export type AddonRequestHandleReq = {
+ addonType: string;
+} &PluginRequestHandleReq>;
+
+export type AddonInputDefine = FormItemProps & {
+ title: string;
+ required?: boolean;
+};
+export type AddonDefine = Registrable & {
+ addonType: string;
+ needPlus?: boolean;
+ input?: {
+ [key: string]: AddonInputDefine;
+ };
+ showTest?: boolean;
+};
+
+export type AddonInstanceConfig = {
+ id: number;
+ addonType: string;
+ type: string;
+ name: string;
+ userId: number;
+ setting: {
+ [key: string]: any;
+ };
+};
+
+
+
+export interface IAddon {
+ ctx: AddonContext;
+ [key: string]: any;
+}
+
+export type AddonContext = {
+ http: HttpClient;
+ logger: ILogger;
+ utils: typeof utils;
+};
+
+export abstract class BaseAddon implements IAddon {
+ define!: AddonDefine;
+ ctx!: AddonContext;
+ http!: HttpClient;
+ logger!: ILogger;
+
+
+
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
+ async onInstance() {}
+ setCtx(ctx: AddonContext) {
+ this.ctx = ctx;
+ this.http = ctx.http;
+ this.logger = ctx.logger;
+ }
+ setDefine = (define:AddonDefine) => {
+ this.define = define;
+ };
+
+ async onRequest(req:AddonRequestHandleReq) {
+ if (!req.action) {
+ throw new Error("action is required");
+ }
+
+ let methodName = req.action;
+ if (!req.action.startsWith("on")) {
+ methodName = `on${upperFirst(req.action)}`;
+ }
+
+ // @ts-ignore
+ const method = this[methodName];
+ if (method) {
+ // @ts-ignore
+ return await this[methodName](req.data);
+ }
+ throw new Error(`action ${req.action} not found`);
+ }
+
+}
+
+
+export interface IAddonGetter {
+ getById(id: any): Promise;
+ getCommonById(id: any): Promise;
+}
diff --git a/packages/libs/lib-server/src/user/addon/api/decorator.ts b/packages/libs/lib-server/src/user/addon/api/decorator.ts
new file mode 100644
index 000000000..6a96b49ac
--- /dev/null
+++ b/packages/libs/lib-server/src/user/addon/api/decorator.ts
@@ -0,0 +1,65 @@
+// src/decorator/memoryCache.decorator.ts
+import * as _ from "lodash-es";
+import { merge } from "lodash-es";
+import { addonRegistry } from "./registry.js";
+import { AddonContext, AddonDefine, AddonInputDefine } from "./api.js";
+import { Decorator } from "@certd/pipeline";
+
+// 提供一个唯一 key
+export const ADDON_CLASS_KEY = "pipeline:addon";
+export const ADDON_INPUT_KEY = "pipeline:addon:input";
+
+export function IsAddon(define: AddonDefine): ClassDecorator {
+ return (target: any) => {
+ target = Decorator.target(target);
+
+ const inputs: any = {};
+ const properties = Decorator.getClassProperties(target);
+ for (const property in properties) {
+ const input = Reflect.getMetadata(ADDON_INPUT_KEY, target, property);
+ if (input) {
+ inputs[property] = input;
+ }
+ }
+ _.merge(define, { input: inputs });
+ Reflect.defineMetadata(ADDON_CLASS_KEY, define, target);
+ target.define = define;
+ const key = `${define.addonType}:${define.name}`;
+ addonRegistry.register(key, {
+ define,
+ target: async () => {
+ return target;
+ },
+ });
+ };
+}
+
+export function AddonInput(input?: AddonInputDefine): PropertyDecorator {
+ return (target, propertyKey) => {
+ target = Decorator.target(target, propertyKey);
+ // const _type = Reflect.getMetadata("design:type", target, propertyKey);
+ Reflect.defineMetadata(ADDON_INPUT_KEY, input, target, propertyKey);
+ };
+}
+
+export async function newAddon(addonType:string,type: string, input: any, ctx: AddonContext) {
+ const key = `${addonType}:${type}`
+ const register = addonRegistry.get(key);
+ if (register == null) {
+ throw new Error(`${addonType} ${type} not found`);
+ }
+ // @ts-ignore
+ const pluginCls = await register.target();
+ // @ts-ignore
+ const plugin = new pluginCls();
+ merge(plugin, input);
+ if (!ctx) {
+ throw new Error("ctx is required");
+ }
+ plugin.setDefine(register.define);
+ plugin.setCtx(ctx);
+ await plugin.onInstance();
+ return plugin;
+}
+
+
diff --git a/packages/libs/lib-server/src/user/addon/api/index.ts b/packages/libs/lib-server/src/user/addon/api/index.ts
new file mode 100644
index 000000000..9b9e3a489
--- /dev/null
+++ b/packages/libs/lib-server/src/user/addon/api/index.ts
@@ -0,0 +1,3 @@
+export * from "./api.js";
+export * from "./registry.js";
+export * from "./decorator.js";
diff --git a/packages/libs/lib-server/src/user/addon/api/registry.ts b/packages/libs/lib-server/src/user/addon/api/registry.ts
new file mode 100644
index 000000000..643de99cf
--- /dev/null
+++ b/packages/libs/lib-server/src/user/addon/api/registry.ts
@@ -0,0 +1,3 @@
+import { createRegistry } from "@certd/pipeline";
+
+export const addonRegistry = createRegistry("addon");
diff --git a/packages/libs/lib-server/src/user/addon/entity/addon.ts b/packages/libs/lib-server/src/user/addon/entity/addon.ts
new file mode 100644
index 000000000..4d16fb43b
--- /dev/null
+++ b/packages/libs/lib-server/src/user/addon/entity/addon.ts
@@ -0,0 +1,44 @@
+import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
+
+/**
+ */
+@Entity('cd_addon')
+export class AddonEntity {
+ @PrimaryGeneratedColumn()
+ id: number;
+ @Column({ name: 'user_id', comment: '用户id' })
+ userId: number;
+ @Column({ comment: '名称', length: 100 })
+ name: string;
+
+
+ @Column({ name: 'addon_type', comment: 'addon类型', length: 100 })
+ addonType: string;
+
+
+ @Column({ comment: '类型', length: 100 })
+ type: string;
+
+ @Column({ name: 'setting', comment: '设置', length: 10240, nullable: true })
+ setting: string;
+
+ @Column({ name: 'is_system', comment: '是否系统级别', nullable: false, default: false })
+ isSystem: boolean;
+
+ @Column({ name: 'is_default', comment: '是否默认', nullable: false, default: false })
+ isDefault: boolean;
+
+
+ @Column({
+ name: 'create_time',
+ comment: '创建时间',
+ default: () => 'CURRENT_TIMESTAMP',
+ })
+ createTime: Date;
+ @Column({
+ name: 'update_time',
+ comment: '修改时间',
+ default: () => 'CURRENT_TIMESTAMP',
+ })
+ updateTime: Date;
+}
diff --git a/packages/libs/lib-server/src/user/addon/index.ts b/packages/libs/lib-server/src/user/addon/index.ts
new file mode 100644
index 000000000..727232b4f
--- /dev/null
+++ b/packages/libs/lib-server/src/user/addon/index.ts
@@ -0,0 +1,5 @@
+export * from './api/index.js'
+export * from './entity/addon.js'
+export * from './service/addon-service.js'
+export * from './service/addon-getter.js'
+export * from './service/addon-sys-getter.js'
diff --git a/packages/libs/lib-server/src/user/addon/service/addon-getter.ts b/packages/libs/lib-server/src/user/addon/service/addon-getter.ts
new file mode 100644
index 000000000..8e91ad7e0
--- /dev/null
+++ b/packages/libs/lib-server/src/user/addon/service/addon-getter.ts
@@ -0,0 +1,18 @@
+import { IAddonGetter } from "../api/index.js";
+
+export class AddonGetter implements IAddonGetter {
+ userId: number;
+ getter: (id: any, userId?: number) => Promise;
+ constructor(userId: number, getter: (id: any, userId: number) => Promise) {
+ this.userId = userId;
+ this.getter = getter;
+ }
+
+ async getById(id: any) {
+ return await this.getter(id, this.userId);
+ }
+
+ async getCommonById(id: any) {
+ return await this.getter(id, 0);
+ }
+}
diff --git a/packages/libs/lib-server/src/user/addon/service/addon-service.ts b/packages/libs/lib-server/src/user/addon/service/addon-service.ts
new file mode 100644
index 000000000..d58d2453c
--- /dev/null
+++ b/packages/libs/lib-server/src/user/addon/service/addon-service.ts
@@ -0,0 +1,231 @@
+import { Provide, Scope, ScopeEnum } from "@midwayjs/core";
+import { InjectEntityModel } from "@midwayjs/typeorm";
+import { In, Repository } from "typeorm";
+import { AddonDefine, BaseService, PageReq, PermissionException, ValidateException } from "../../../index.js";
+import { addonRegistry, newAddon } from "../api/index.js";
+import { AddonEntity } from "../entity/addon.js";
+import { http, logger, utils } from "@certd/basic";
+
+/**
+ * Addon
+ */
+@Provide()
+@Scope(ScopeEnum.Request, {allowDowngrade: true})
+export class AddonService extends BaseService {
+ @InjectEntityModel(AddonEntity)
+ repository: Repository;
+
+ //@ts-ignore
+ getRepository() {
+ return this.repository;
+ }
+
+ async page(pageReq: PageReq) {
+ const res = await super.page(pageReq);
+ res.records = res.records.map(item => {
+ return item;
+ });
+ return res;
+ }
+
+ async add(param) {
+ let oldEntity = null;
+ if (param._copyFrom){
+ oldEntity = await this.info(param._copyFrom);
+ if (oldEntity == null) {
+ throw new ValidateException('该Addon配置不存在,请确认是否已被删除');
+ }
+ if (oldEntity.userId !== param.userId) {
+ throw new ValidateException('您无权查看该Addon配置');
+ }
+ }
+ if (!param.userId){
+ param.isSystem = true
+ }else{
+ param.isSystem = false
+ }
+ delete param._copyFrom
+ return await super.add(param);
+ }
+
+
+ /**
+ * 修改
+ * @param param 数据
+ */
+ async update(param) {
+ const oldEntity = await this.info(param.id);
+ if (oldEntity == null) {
+ throw new ValidateException('该Addon配置不存在,请确认是否已被删除');
+ }
+ return await super.update(param);
+ }
+
+ async getSimpleInfo(id: number) {
+ const entity = await this.info(id);
+ if (entity == null) {
+ throw new ValidateException('该Addon配置不存在,请确认是否已被删除');
+ }
+ return {
+ id: entity.id,
+ name: entity.name,
+ userId: entity.userId,
+ addonType: entity.addonType,
+ type: entity.type,
+ };
+ }
+
+ async getAddonById(id: any, checkUserId: boolean, userId?: number): Promise {
+ const ctx = {
+ http: http,
+ logger: logger,
+ utils: utils,
+ };
+
+
+ if (!id){
+ //使用图片验证码
+ return await newAddon("captcha", "image", {},ctx);
+ }
+ const entity = await this.info(id);
+ if (entity == null) {
+ //使用图片验证码
+ return await newAddon("captcha", "image", {},ctx);
+ }
+ if (checkUserId) {
+ if (userId == null) {
+ throw new ValidateException('userId不能为空');
+ }
+ if (userId !== entity.userId) {
+ throw new PermissionException('您对该Addon无访问权限');
+ }
+ }
+
+ const setting = JSON.parse(entity.setting ??"{}")
+ const input = {
+ id: entity.id,
+ ...setting,
+ };
+
+ return await newAddon(entity.addonType, entity.type, input,ctx);
+ }
+
+ async getById(id: any, userId: number): Promise {
+ return await this.getAddonById(id, true, userId);
+ }
+
+
+ getDefineList(addonType: string) {
+ return addonRegistry.getDefineList();
+ }
+
+ getDefineByType(type: string,prefix?: string) {
+ return addonRegistry.getDefine(type,prefix) as AddonDefine;
+ }
+
+
+ async getSimpleByIds(ids: number[], userId: any) {
+ if (ids.length === 0) {
+ return [];
+ }
+ if (!userId) {
+ return [];
+ }
+ return await this.repository.find({
+ where: {
+ id: In(ids),
+ userId,
+ },
+ select: {
+ id: true,
+ name: true,
+ addonType: true,
+ type: true,
+ userId:true,
+ isSystem: true,
+ },
+ });
+
+ }
+
+
+
+ async getDefault(userId: number,addonType: string): Promise {
+ const res = await this.repository.findOne({
+ where: {
+ userId,
+ addonType
+ },
+ order: {
+ isDefault: 'DESC',
+ },
+ });
+ if (!res) {
+ return null;
+ }
+ return this.buildAddonInstanceConfig(res);
+ }
+
+ private buildAddonInstanceConfig(res: AddonEntity) {
+ const setting = JSON.parse(res.setting);
+ return {
+ id: res.id,
+ addonType: res.addonType,
+ type: res.type,
+ name: res.name,
+ userId: res.userId,
+ setting,
+ };
+ }
+
+ async setDefault(id: number, userId: number,addonType:string) {
+ if (!id) {
+ throw new ValidateException('id不能为空');
+ }
+ if (!userId) {
+ throw new ValidateException('userId不能为空');
+ }
+ await this.repository.update(
+ {
+ userId,
+ addonType
+ },
+ {
+ isDefault: false,
+ }
+ );
+ await this.repository.update(
+ {
+ id,
+ userId,
+ addonType
+ },
+ {
+ isDefault: true,
+ }
+ );
+ }
+
+ async getOrCreateDefault(opts:{addonType:string,type:string, inputs: any, userId: any}) {
+ const {addonType,type,inputs,userId} = opts;
+
+ const addonDefine = this.getDefineByType( type,addonType)
+
+ const defaultConfig = await this.getDefault(userId,addonType);
+ if (defaultConfig) {
+ return defaultConfig;
+ }
+ const setting = {
+ ...inputs,
+ };
+ const res = await this.repository.save({
+ userId,
+ addonType,
+ type: type,
+ name: addonDefine.title,
+ setting: JSON.stringify(setting),
+ isDefault: true,
+ });
+ return this.buildAddonInstanceConfig(res);
+ }
+}
diff --git a/packages/libs/lib-server/src/user/addon/service/addon-sys-getter.ts b/packages/libs/lib-server/src/user/addon/service/addon-sys-getter.ts
new file mode 100644
index 000000000..773e1a7aa
--- /dev/null
+++ b/packages/libs/lib-server/src/user/addon/service/addon-sys-getter.ts
@@ -0,0 +1,17 @@
+import { IAccessService } from '@certd/pipeline';
+import { AddonService } from './addon-service.js';
+
+export class AddonSysGetter implements IAccessService {
+ addonService: AddonService;
+ constructor(addonService: AddonService) {
+ this.addonService = addonService;
+ }
+
+ async getById(id: any) {
+ return await this.addonService.getById(id, 0);
+ }
+
+ async getCommonById(id: any) {
+ return await this.addonService.getById(id, 0);
+ }
+}
diff --git a/packages/libs/lib-server/src/user/index.ts b/packages/libs/lib-server/src/user/index.ts
index 17e3af2c4..f0fce929a 100644
--- a/packages/libs/lib-server/src/user/index.ts
+++ b/packages/libs/lib-server/src/user/index.ts
@@ -1 +1,2 @@
export * from './access/index.js';
+export * from './addon/index.js';
diff --git a/packages/libs/midway-flyway-js/CHANGELOG.md b/packages/libs/midway-flyway-js/CHANGELOG.md
index 771525204..14b5cb040 100644
--- a/packages/libs/midway-flyway-js/CHANGELOG.md
+++ b/packages/libs/midway-flyway-js/CHANGELOG.md
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+**Note:** Version bump only for package @certd/midway-flyway-js
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+**Note:** Version bump only for package @certd/midway-flyway-js
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+**Note:** Version bump only for package @certd/midway-flyway-js
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
**Note:** Version bump only for package @certd/midway-flyway-js
diff --git a/packages/libs/midway-flyway-js/package.json b/packages/libs/midway-flyway-js/package.json
index ab01b2a23..1618ec4e8 100644
--- a/packages/libs/midway-flyway-js/package.json
+++ b/packages/libs/midway-flyway-js/package.json
@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
- "version": "1.36.18",
+ "version": "1.36.21",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -46,5 +46,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
- "gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
+ "gitHead": "3cedef4974708d828fb972acc54af0515e3ec3a0"
}
diff --git a/packages/plugins/plugin-cert/CHANGELOG.md b/packages/plugins/plugin-cert/CHANGELOG.md
index 3e8521a58..c565d39a9 100644
--- a/packages/plugins/plugin-cert/CHANGELOG.md
+++ b/packages/plugins/plugin-cert/CHANGELOG.md
@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+### Bug Fixes
+
+* 修复ssl.com报EMAILADDRESS数量不对的bug ([c560cc5](https://github.com/certd/certd/commit/c560cc5adda6e15bf3a8865d874042550a6c2688))
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+### Bug Fixes
+
+* 修复证书手动托管时新上传的证书无效的bug ([506385e](https://github.com/certd/certd/commit/506385e5a2600887fe30854e0713583caaa2e689))
+
+### Performance Improvements
+
+* 证书到期剩余天数进度条根据实际证书有效期计算 ([#528](https://github.com/certd/certd/issues/528)) nicheng-he ([2d4586b](https://github.com/certd/certd/commit/2d4586b1c42c39f97d2a95b9453cca4bc8bfbe61))
+* add preferred chain option ([#519](https://github.com/certd/certd/issues/519)) @ZeroClover ([902359f](https://github.com/certd/certd/commit/902359f24ed12eee4f9b65178f1d6a60378351d2))
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+### Performance Improvements
+
+* 支持ssl.com证书颁发机构 ([27b6dfa](https://github.com/certd/certd/commit/27b6dfa4d2ab3bddd284c3a34511a72e1a513a4c))
+* 子域名托管说明 ([39a0223](https://github.com/certd/certd/commit/39a02235cf4416bb5bd1acd3831241efeaa2f602))
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
### Performance Improvements
diff --git a/packages/plugins/plugin-cert/package.json b/packages/plugins/plugin-cert/package.json
index c6d56d933..7b15ccc4f 100644
--- a/packages/plugins/plugin-cert/package.json
+++ b/packages/plugins/plugin-cert/package.json
@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
- "version": "1.36.18",
+ "version": "1.36.21",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -16,10 +16,10 @@
"pub": "npm publish"
},
"dependencies": {
- "@certd/acme-client": "^1.36.18",
- "@certd/basic": "^1.36.18",
- "@certd/pipeline": "^1.36.18",
- "@certd/plugin-lib": "^1.36.18",
+ "@certd/acme-client": "^1.36.21",
+ "@certd/basic": "^1.36.21",
+ "@certd/pipeline": "^1.36.21",
+ "@certd/plugin-lib": "^1.36.21",
"@google-cloud/publicca": "^1.3.0",
"dayjs": "^1.11.7",
"jszip": "^3.10.1",
@@ -43,5 +43,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
- "gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
+ "gitHead": "3cedef4974708d828fb972acc54af0515e3ec3a0"
}
diff --git a/packages/plugins/plugin-cert/src/access/eab-access.ts b/packages/plugins/plugin-cert/src/access/eab-access.ts
index f5c7379f3..1162c4df9 100644
--- a/packages/plugins/plugin-cert/src/access/eab-access.ts
+++ b/packages/plugins/plugin-cert/src/access/eab-access.ts
@@ -12,7 +12,7 @@ export class EabAccess extends BaseAccess {
component: {
placeholder: "kid / keyId",
},
- helper: "EAB KID, google的叫 keyId",
+ helper: "EAB KID, google的叫 keyId,ssl.com的叫Account/ACME Key",
required: true,
encrypt: true,
})
diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts
index 3f55a27bf..5bb207519 100644
--- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts
+++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts
@@ -50,7 +50,7 @@ export type CertInfo = {
one?: string;
p7b?: string;
};
-export type SSLProvider = "letsencrypt" | "google" | "zerossl";
+export type SSLProvider = "letsencrypt" | "google" | "zerossl" | "sslcom";
export type PrivateKeyType = "rsa_1024" | "rsa_2048" | "rsa_3072" | "rsa_4096" | "ec_256" | "ec_384" | "ec_521";
type AcmeServiceOptions = {
userContext: IContext;
@@ -329,8 +329,9 @@ export class AcmeService {
isTest?: boolean;
privateKeyType?: string;
profile?: string;
+ preferredChain?: string;
}): Promise {
- const { email, isTest, csrInfo, dnsProvider, domainsVerifyPlan, profile } = options;
+ const { email, isTest, csrInfo, dnsProvider, domainsVerifyPlan, profile, preferredChain } = options;
const client: acme.Client = await this.getAcmeClient(email, isTest);
let domains = options.domains;
@@ -373,6 +374,7 @@ export class AcmeService {
commonName,
...csrInfo,
altNames,
+ // emailAddress: email,
},
privateKey
);
@@ -403,6 +405,7 @@ export class AcmeService {
},
signal: this.options.signal,
profile,
+ preferredChain,
});
const crtString = crt.toString();
diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/base-convert.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/base-convert.ts
index e9d593139..e8ad9d756 100644
--- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/base-convert.ts
+++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/base-convert.ts
@@ -99,6 +99,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
const cert: CertInfo = certReader.toCertInfo();
this.cert = cert;
+ this._result.pipelineVars.certEffectiveTime = dayjs(certReader.detail.notBefore).valueOf();
this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.notAfter).valueOf();
if (!this._result.pipelinePrivateVars) {
this._result.pipelinePrivateVars = {};
diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/cert-reader.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/cert-reader.ts
index df31264f1..8fbaa32d3 100644
--- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/cert-reader.ts
+++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/cert-reader.ts
@@ -35,6 +35,7 @@ export class CertReader {
detail: CertificateInfo;
//毫秒时间戳
+ effective: number;
expires: number;
constructor(certInfo: CertInfo) {
this.cert = certInfo;
@@ -52,8 +53,9 @@ export class CertReader {
}
try {
- const { detail, expires } = this.getCrtDetail(this.cert.crt);
+ const { detail, effective, expires } = this.getCrtDetail(this.cert.crt);
this.detail = detail;
+ this.effective = effective.getTime();
this.expires = expires.getTime();
} catch (e) {
throw new Error("证书解析失败:" + e.message);
@@ -102,8 +104,9 @@ export class CertReader {
static readCertDetail(crt: string) {
const detail = crypto.readCertificateInfo(crt.toString());
+ const effective = detail.notBefore;
const expires = detail.notAfter;
- return { detail, expires };
+ return { detail, effective, expires };
}
getAllDomains() {
@@ -221,10 +224,10 @@ export class CertReader {
return `${prefix}_${domain}_${timeStr}.${suffix}`;
}
- buildCertName() {
+ buildCertName(prefix: string = "") {
let domain = this.getMainDomain();
domain = domain.replaceAll(".", "_").replaceAll("*", "_");
- return `${domain}_${dayjs().format("YYYYMMDDHHmmssSSS")}`;
+ return `${prefix}_${domain}_${dayjs().format("YYYYMMDDHHmmssSSS")}`;
}
static appendTimeSuffix(name?: string) {
diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/custom/index.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/custom/index.ts
index d11a719cb..841b8914e 100644
--- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/custom/index.ts
+++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/custom/index.ts
@@ -118,7 +118,7 @@ export class CertApplyUploadPlugin extends CertApplyBaseConvertPlugin {
}
async execute(): Promise {
- const certReader = await this.getCertFromStore();
+ let certReader = await this.getCertFromStore();
const crtMd5 = this.ctx.utils.hash.md5(certReader.cert.crt);
const leftDays = dayjs(certReader.expires).diff(dayjs(), "day");
@@ -141,9 +141,13 @@ export class CertApplyUploadPlugin extends CertApplyBaseConvertPlugin {
this.logger.info("输入参数有变化,重新部署");
}
+ certReader = new CertReader(this.uploadCert);
this.clearLastStatus();
//输出证书MD5
- this.certMd5 = crtMd5;
+ this.certMd5 = this.ctx.utils.hash.md5(certReader.cert.crt);
+ const newLeftDays = dayjs(certReader.expires).diff(dayjs(), "day");
+ this.logger.info(`新证书过期时间${dayjs(certReader.expires).format("YYYY-MM-DD HH:mm:ss")},剩余${newLeftDays}天`);
+
await this.output(certReader, true);
//必须output之后执行
diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts
index 6c654cf17..e4d9ea539 100644
--- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts
+++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/index.ts
@@ -38,6 +38,53 @@ export type DomainsVerifyPlanInput = {
[key: string]: DomainVerifyPlanInput;
};
+const preferredChainConfigs = {
+ letsencrypt: {
+ helper: "如无特殊需求保持默认即可",
+ options: [
+ { value: "ISRG Root X1", label: "ISRG Root X1" },
+ { value: "ISRG Root X2", label: "ISRG Root X2" },
+ ],
+ },
+ google: {
+ helper: "GlobalSign 提供对老旧设备更好的兼容性,但证书链会变长",
+ options: [
+ { value: "GTS Root R1", label: "GTS Root R1" },
+ { value: "GlobalSign", label: "GlobalSign" },
+ ],
+ },
+} as const;
+
+const preferredChainSupportedProviders = Object.keys(preferredChainConfigs);
+
+const preferredChainMergeScript = (() => {
+ const configs = JSON.stringify(preferredChainConfigs);
+ const supportedProviders = JSON.stringify(preferredChainSupportedProviders);
+ const defaultProvider = JSON.stringify(preferredChainSupportedProviders[0]);
+ return `
+ const chainConfigs = ${configs};
+ const supportedProviders = ${supportedProviders};
+ const defaultProvider = ${defaultProvider};
+ const getConfig = (provider)=> chainConfigs[provider] || chainConfigs[defaultProvider];
+ return {
+ show: ctx.compute(({form})=> supportedProviders.includes(form.sslProvider)),
+ component: {
+ options: ctx.compute(({form})=> getConfig(form.sslProvider).options)
+ },
+ helper: ctx.compute(({form})=> getConfig(form.sslProvider).helper),
+ value: ctx.compute(({form})=>{
+ const { options } = getConfig(form.sslProvider);
+ const allowed = options.map(item=>item.value);
+ const current = form.preferredChain;
+ if(allowed.includes(current)){
+ return current;
+ }
+ return allowed[0];
+ })
+ };
+ `;
+})();
+
@IsTaskPlugin({
name: "CertApply",
title: "证书申请(JS版)",
@@ -89,9 +136,10 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
{ value: "letsencrypt", label: "Let's Encrypt", icon: "simple-icons:letsencrypt" },
{ value: "google", label: "Google", icon: "flat-color-icons:google" },
{ value: "zerossl", label: "ZeroSSL", icon: "emojione:digit-zero" },
+ { value: "sslcom", label: "SSL.com(仅主域名和www免费)", icon: "la:expeditedssl" },
],
},
- helper: "Let's Encrypt:申请最简单\nGoogle:大厂光环,兼容性好,仅首次需要翻墙获取EAB授权\nZeroSSL:需要EAB授权,无需翻墙",
+ helper: "Let's Encrypt:申请最简单\nGoogle:大厂光环,兼容性好,仅首次需要翻墙获取EAB授权\nZeroSSL:需要EAB授权,无需翻墙\nSSL.com:仅主域名和www免费,必须设置CAA记录",
required: true,
})
sslProvider!: SSLProvider;
@@ -104,7 +152,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
mergeScript: `
return {
show: ctx.compute(({form})=>{
- return form.challengeType === 'dns'
+ return form.challengeType === 'dns'
}),
component:{
onSelectedChange: ctx.compute(({form})=>{
@@ -137,7 +185,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
})
},
show: ctx.compute(({form})=>{
- return form.challengeType === 'dns'
+ return form.challengeType === 'dns'
})
}
`,
@@ -194,6 +242,13 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
})
zerosslCommonEabAccessId!: number;
+ @TaskInput({
+ title: "SSL.com公共EAB授权",
+ isSys: true,
+ show: false,
+ })
+ sslcomCommonEabAccessId!: number;
+
@TaskInput({
title: "EAB授权",
component: {
@@ -203,11 +258,16 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
maybeNeed: true,
required: false,
helper:
- "需要提供EAB授权\nZeroSSL:请前往[zerossl开发者中心](https://app.zerossl.com/developer),生成 'EAB Credentials'\n Google:请查看[google获取eab帮助文档](https://certd.docmirror.cn/guide/use/google/),用过一次后会绑定邮箱,后续复用EAB要用同一个邮箱",
+ "需要提供EAB授权" +
+ "\nZeroSSL:请前往[zerossl开发者中心](https://app.zerossl.com/developer),生成 'EAB Credentials'" +
+ "\nGoogle:请查看[google获取eab帮助文档](https://certd.docmirror.cn/guide/use/google/),用过一次后会绑定邮箱,后续复用EAB要用同一个邮箱" +
+ "\nSSL.com:[SSL.com账号页面](https://secure.ssl.com/account),然后点击api credentials链接,然后点击编辑按钮,查看Secret key和HMAC key",
mergeScript: `
return {
show: ctx.compute(({form})=>{
- return (form.sslProvider === 'zerossl' && !form.zerosslCommonEabAccessId) || (form.sslProvider === 'google' && !form.googleCommonEabAccessId)
+ return (form.sslProvider === 'zerossl' && !form.zerosslCommonEabAccessId)
+ || (form.sslProvider === 'google' && !form.googleCommonEabAccessId)
+ || (form.sslProvider === 'sslcom' && !form.sslcomCommonEabAccessId)
})
}
`,
@@ -279,6 +339,19 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
})
certProfile!: string;
+ @TaskInput({
+ title: "首选链",
+ component: {
+ name: "a-select",
+ vModel: "value",
+ options: preferredChainConfigs.letsencrypt.options,
+ },
+ helper: preferredChainConfigs.letsencrypt.helper,
+ required: false,
+ mergeScript: preferredChainMergeScript,
+ })
+ preferredChain!: string;
+
@TaskInput({
title: "使用代理",
value: false,
@@ -339,8 +412,8 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
async onInit() {
let eab: EabAccess = null;
- if (this.sslProvider === "google") {
- if (this.googleAccessId) {
+ if (this.sslProvider && this.sslProvider !== "letsencrypt") {
+ if (this.sslProvider === "google" && this.googleAccessId) {
this.logger.info("当前正在使用 google服务账号授权获取EAB");
const googleAccess = await this.getAccess(this.googleAccessId);
const googleClient = new GoogleClient({
@@ -348,24 +421,19 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
logger: this.logger,
});
eab = await googleClient.getEab();
- } else if (this.eabAccessId) {
- this.logger.info("当前正在使用 google EAB授权");
- eab = await this.getAccess(this.eabAccessId);
- } else if (this.googleCommonEabAccessId) {
- this.logger.info("当前正在使用 google 公共EAB授权");
- eab = await this.getAccess(this.googleCommonEabAccessId, true);
} else {
- throw new Error("google需要配置EAB授权或服务账号授权");
- }
- } else if (this.sslProvider === "zerossl") {
- if (this.eabAccessId) {
- this.logger.info("当前正在使用 zerossl EAB授权");
- eab = await this.getAccess(this.eabAccessId);
- } else if (this.zerosslCommonEabAccessId) {
- this.logger.info("当前正在使用 zerossl 公共EAB授权");
- eab = await this.getAccess(this.zerosslCommonEabAccessId, true);
- } else {
- throw new Error("zerossl需要配置EAB授权");
+ const getEab = async (type: string) => {
+ if (this.eabAccessId) {
+ this.logger.info(`当前正在使用 ${type} EAB授权`);
+ eab = await this.getAccess(this.eabAccessId);
+ } else if (this[`${type}CommonEabAccessId`]) {
+ this.logger.info(`当前正在使用 ${type} 公共EAB授权`);
+ eab = await this.getAccess(this[`${type}CommonEabAccessId`], true);
+ } else {
+ throw new Error(`${type}需要配置EAB授权`);
+ }
+ };
+ await getEab(this.sslProvider);
}
}
this.eab = eab;
@@ -397,12 +465,12 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
const csrInfo = _.merge(
{
- country: "CN",
- state: "GuangDong",
- locality: "ShengZhen",
- organization: "CertD Org.",
- organizationUnit: "IT Department",
- emailAddress: email,
+ // country: "CN",
+ // state: "GuangDong",
+ // locality: "ShengZhen",
+ // organization: "CertD Org.",
+ // organizationUnit: "IT Department",
+ // emailAddress: email,
},
this.csrInfo ? JSON.parse(this.csrInfo) : {}
);
@@ -430,6 +498,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
isTest: false,
privateKeyType: this.privateKeyType,
profile: this.certProfile,
+ preferredChain: this.preferredChain,
});
const certInfo = this.formatCerts(cert);
diff --git a/packages/plugins/plugin-lib/CHANGELOG.md b/packages/plugins/plugin-lib/CHANGELOG.md
index d2c403c37..1d40036c7 100644
--- a/packages/plugins/plugin-lib/CHANGELOG.md
+++ b/packages/plugins/plugin-lib/CHANGELOG.md
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+**Note:** Version bump only for package @certd/plugin-lib
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+### Performance Improvements
+
+* ssh配置增加脚本类型设置,bash还是sh ([ae41c60](https://github.com/certd/certd/commit/ae41c6038b27c9476e64a2402a8daf247c38a5b6))
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+### Performance Improvements
+
+* ssh 增加超时断开连接,默认10分钟超时 ([c24a040](https://github.com/certd/certd/commit/c24a040c19cacafc79228d7a7649af93837d94a1))
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
### Performance Improvements
diff --git a/packages/plugins/plugin-lib/package.json b/packages/plugins/plugin-lib/package.json
index 7d96007cf..e67ddeced 100644
--- a/packages/plugins/plugin-lib/package.json
+++ b/packages/plugins/plugin-lib/package.json
@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
- "version": "1.36.18",
+ "version": "1.36.21",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -21,8 +21,8 @@
"@alicloud/pop-core": "^1.7.10",
"@alicloud/tea-util": "^1.4.10",
"@aws-sdk/client-s3": "^3.787.0",
- "@certd/basic": "^1.36.18",
- "@certd/pipeline": "^1.36.18",
+ "@certd/basic": "^1.36.21",
+ "@certd/pipeline": "^1.36.21",
"@kubernetes/client-node": "0.21.0",
"ali-oss": "^6.22.0",
"basic-ftp": "^5.0.5",
@@ -53,5 +53,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
- "gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
+ "gitHead": "3cedef4974708d828fb972acc54af0515e3ec3a0"
}
diff --git a/packages/plugins/plugin-lib/src/ssh/ssh-access.ts b/packages/plugins/plugin-lib/src/ssh/ssh-access.ts
index 984a435cc..4d20696d8 100644
--- a/packages/plugins/plugin-lib/src/ssh/ssh-access.ts
+++ b/packages/plugins/plugin-lib/src/ssh/ssh-access.ts
@@ -64,6 +64,22 @@ export class SshAccess extends BaseAccess {
})
passphrase!: string;
+ @AccessInput({
+ title: "脚本类型",
+ helper: "bash 、sh 、fish",
+ component: {
+ name: "a-select",
+ vModel: "value",
+ options: [
+ { value: "default", label: "默认" },
+ { value: "sh", label: "sh" },
+ { value: "bash", label: "bash" },
+ { value: "fish", label: "fish(不支持set -e)" },
+ ],
+ },
+ })
+ scriptType: string;
+
@AccessInput({
title: "伪终端",
helper: "如果登录报错:all authentication methods failed,可以尝试开启伪终端模式进行keyboard-interactive方式登录\n开启后对日志输出有一定的影响",
@@ -86,6 +102,15 @@ export class SshAccess extends BaseAccess {
})
socksProxy!: string;
+ @AccessInput({
+ title: "超时时间",
+ helper: "执行命令的超时时间,单位秒,默认30分钟",
+ component: {
+ name: "a-input-number",
+ },
+ })
+ timeout: number;
+
@AccessInput({
title: "是否Windows",
helper: "如果是Windows主机,请勾选此项\n并且需要windows[安装OpenSSH](https://certd.docmirror.cn/guide/use/host/windows.html)",
@@ -136,9 +161,10 @@ export class SshAccess extends BaseAccess {
const { SshClient } = await import("./ssh.js");
const client = new SshClient(this.ctx.logger);
+ const script = ["echo hello", "exit"];
await client.exec({
connectConf: this,
- script: "echo hello",
+ script: script,
});
return "ok";
}
diff --git a/packages/plugins/plugin-lib/src/ssh/ssh.ts b/packages/plugins/plugin-lib/src/ssh/ssh.ts
index d0aa74e3c..197ebfc1d 100644
--- a/packages/plugins/plugin-lib/src/ssh/ssh.ts
+++ b/packages/plugins/plugin-lib/src/ssh/ssh.ts
@@ -469,7 +469,8 @@ export class SshClient {
async isCmd(conn: AsyncSsh2Client) {
const spec = await conn.exec("echo %COMSPEC% ");
- if (spec.toString().trim() === "%COMSPEC%") {
+ const ret = spec.toString().trim();
+ if (ret.includes("%COMSPEC%") && !ret.includes("echo %COMSPEC%")) {
return false;
} else {
return true;
@@ -542,8 +543,16 @@ export class SshClient {
}
}
- if (isLinux && options.stopOnError !== false) {
- script = "set -e\n" + script;
+ if (isLinux) {
+ if (options.connectConf.scriptType == "bash") {
+ script = "#!/usr/bin/env bash \n" + script;
+ } else if (options.connectConf.scriptType == "sh") {
+ script = "#!/bin/sh\n" + script;
+ }
+
+ if (options.connectConf.scriptType != "fish" && options.stopOnError !== false) {
+ script = "set -e\n" + script;
+ }
}
return await conn.exec(script as string, { throwOnStdErr });
@@ -587,10 +596,15 @@ export class SshClient {
}
throw e;
}
-
+ let timeoutId = null;
try {
+ timeoutId = setTimeout(() => {
+ this.logger.info("执行超时,断开连接");
+ conn.end();
+ }, 1000 * (connectConf.timeout || 1800));
return await callable(conn);
} finally {
+ clearTimeout(timeoutId);
conn.end();
}
}
diff --git a/packages/plugins/plugin-lib/src/tencent/lib/ssl-client.ts b/packages/plugins/plugin-lib/src/tencent/lib/ssl-client.ts
index 4846ee1ae..2075d4705 100644
--- a/packages/plugins/plugin-lib/src/tencent/lib/ssl-client.ts
+++ b/packages/plugins/plugin-lib/src/tencent/lib/ssl-client.ts
@@ -76,9 +76,26 @@ export class TencentSslClient {
return res;
}
- async DescribeCertificates(params: any) {
+ async DescribeHostUploadUpdateRecordDetail(params: any) {
const client = await this.getSslClient();
- const res = await client.DescribeCertificates(params);
+ const res = await client.request("DescribeHostUploadUpdateRecordDetail", params);
+ this.checkRet(res);
+ return res;
+ }
+
+ async UploadUpdateCertificateInstance(params: any) {
+ const client = await this.getSslClient();
+ const res = await client.request("UploadUpdateCertificateInstance", params);
+ this.checkRet(res);
+ return res;
+ }
+
+ async DescribeCertificates(params: { Limit?: number; Offset?: number; SearchKey?: string }) {
+ const client = await this.getSslClient();
+ const res = await client.DescribeCertificates({
+ ExpirationSort: "ASC",
+ ...params,
+ });
this.checkRet(res);
return res;
}
diff --git a/packages/ui/certd-client/CHANGELOG.md b/packages/ui/certd-client/CHANGELOG.md
index c91dfd093..abdba4681 100644
--- a/packages/ui/certd-client/CHANGELOG.md
+++ b/packages/ui/certd-client/CHANGELOG.md
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
+## [1.36.21](https://github.com/certd/certd/compare/v1.36.20...v1.36.21) (2025-09-15)
+
+### Bug Fixes
+
+* 修复导入插件对话框无法打开的bug,修复插件编辑页面打开多个代码编辑器消失的bug ([e5a080a](https://github.com/certd/certd/commit/e5a080aebe0d2f3e3c0f86bf863f75069c1bf7ab))
+
+## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13)
+
+### Bug Fixes
+
+* 修复商业版退出登录后,丢失站点个性化设置的bug ([d75dd05](https://github.com/certd/certd/commit/d75dd058d65c85f80c49e1fa7a910e6c6f08e824))
+* 修复授权类型和名称字段排到最后的bug ([43b7977](https://github.com/certd/certd/commit/43b79778ea9034065f6a15af3296274315597c6b))
+
+### Performance Improvements
+
+* 登录支持极验验证码 ([370db62](https://github.com/certd/certd/commit/370db62bf0aece241859244927beabba32d6a257))
+* 登录注册、找回密码都支持极验验证码和图片验证码 ([7bdde68](https://github.com/certd/certd/commit/7bdde68ecea29fe2c570fd3cb082139db6c93d93))
+* 优化加量包展示效果 ([3c65f37](https://github.com/certd/certd/commit/3c65f37d84177ba107d4a6462648af12d2fc4b7a))
+* 证书到期剩余天数进度条根据实际证书有效期计算 ([#528](https://github.com/certd/certd/issues/528)) nicheng-he ([2d4586b](https://github.com/certd/certd/commit/2d4586b1c42c39f97d2a95b9453cca4bc8bfbe61))
+
+## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05)
+
+### Bug Fixes
+
+* 修复批量流水线执行时日志显示错乱的问题 ([4372adc](https://github.com/certd/certd/commit/4372adc703b9a4c785664054ab2a533626d815a8))
+* 修复远程数据选择无法过滤的bug ([6cbb073](https://github.com/certd/certd/commit/6cbb0739f8428d51b0712f718fe4d236cc087cf9))
+* 修复mysql下购买套餐加量包无效的bug ([c26ad4c](https://github.com/certd/certd/commit/c26ad4c8075f0606d45b8da13915737968d6191a))
+
+### Performance Improvements
+
+* 创建证书时支持选择通知时机 ([0e96bfd](https://github.com/certd/certd/commit/0e96bfdfa377824d204e72923d1176408ae6b300))
+* 商业版隐藏文档相关链接 ([4443a1c](https://github.com/certd/certd/commit/4443a1c0308fa6b95a05efd73d15d24b65d641c9))
+* 商业版隐藏文档相关链接 ([db89561](https://github.com/certd/certd/commit/db8956148083bc4f988226ccf719940d08158a27))
+* 支持根据id更新证书(证书Id不变接口),不过该接口为白名单功能,普通腾讯云账户无法使用 ([fe9c4f3](https://github.com/certd/certd/commit/fe9c4f3391ff07c01dd9a252225f69a129c39050))
+* 子域名托管说明 ([39a0223](https://github.com/certd/certd/commit/39a02235cf4416bb5bd1acd3831241efeaa2f602))
+
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
### Bug Fixes
diff --git a/packages/ui/certd-client/index.html b/packages/ui/certd-client/index.html
index 1740a304a..d760b2f58 100644
--- a/packages/ui/certd-client/index.html
+++ b/packages/ui/certd-client/index.html
@@ -23,5 +23,6 @@
+