mirror of
https://github.com/certd/certd.git
synced 2026-04-13 11:50:57 +08:00
Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d89814795 | ||
|
|
f339bc9f7f | ||
|
|
bb80bc0c07 | ||
|
|
96677ff8bf | ||
|
|
c7b6a6df79 | ||
|
|
8bb7e8bfb2 | ||
|
|
02ab343e22 | ||
|
|
4d875a18de | ||
|
|
cff2336923 | ||
|
|
0e96bfdfa3 | ||
|
|
a24ef48ad1 | ||
|
|
fe9c4f3391 | ||
|
|
6cbb0739f8 | ||
|
|
79ebabfcfb | ||
|
|
0c8e3262fe | ||
|
|
c24a040c19 | ||
|
|
4f39cb8dfa | ||
|
|
cdd2816642 | ||
|
|
27b6dfa4d2 | ||
|
|
204cbd0209 | ||
|
|
b7980aad5a | ||
|
|
e175729e2c | ||
|
|
c26ad4c807 | ||
|
|
4372adc703 | ||
|
|
8a0c2b9b13 | ||
|
|
4443a1c030 | ||
|
|
39a02235cf | ||
|
|
db89561480 | ||
|
|
a4cbb11693 | ||
|
|
1ceeacc526 | ||
|
|
b59052cc43 | ||
|
|
44019e1042 | ||
|
|
fd0e1da4a2 | ||
|
|
f6c67b475a | ||
|
|
ea18a5ad15 | ||
|
|
4d0cd3f497 | ||
|
|
7dbdeaebe0 | ||
|
|
2085bcceb6 | ||
|
|
c09c962cb6 | ||
|
|
9108459ae4 | ||
|
|
992bac0b1f | ||
|
|
ebd6917a1d | ||
|
|
3e079e3b80 | ||
|
|
2ca20be197 | ||
|
|
17f23f3751 | ||
|
|
8e3d699856 | ||
|
|
f1a168fa53 | ||
|
|
3575113655 | ||
|
|
9feb9d04b3 | ||
|
|
5419b1439a | ||
|
|
e4489343fe | ||
|
|
d9f4a5793d | ||
|
|
70fcdc9ebb | ||
|
|
78e7a81638 | ||
|
|
58e82d5dbd | ||
|
|
06d15be43a | ||
|
|
e1e7011853 | ||
|
|
eff7645035 | ||
|
|
eb75e52278 | ||
|
|
15e6148272 | ||
|
|
ccd448a675 | ||
|
|
db54c019ad | ||
|
|
b762b4d72c | ||
|
|
2f8faa839d | ||
|
|
831c325c63 | ||
|
|
f4f73078c5 | ||
|
|
f7d43ad5af | ||
|
|
a77c777980 | ||
|
|
a34db7449e | ||
|
|
0283bd2f97 | ||
|
|
a8de2f8ae7 | ||
|
|
d5dee75df3 | ||
|
|
79cb5c0631 | ||
|
|
7d9901540f |
@@ -1,11 +1,11 @@
|
||||
---
|
||||
name: Plugin Apply
|
||||
about: 请求支持新部署插件
|
||||
about: 部署插件申请支持
|
||||
title: "[Plugin] "
|
||||
labels: feature
|
||||
---
|
||||
|
||||
> > 感谢您支持certd,请按如下规范提交issue
|
||||
> > 感谢您支持certd,请按如下规范提交issue
|
||||
> 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues)
|
||||
|
||||
# 新部署插件申请支持
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
name: DNS Provider Apply
|
||||
about: 请求支持新的域名提供商
|
||||
about: 域名提供商申请支持
|
||||
title: "[DNS] "
|
||||
labels: feature
|
||||
---
|
||||
|
||||
|
||||
> 感谢您支持certd,请按如下规范提交issue
|
||||
> 感谢您支持certd,请按如下规范提交issue
|
||||
> 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues)
|
||||
|
||||
# 新域名提供商支持申请
|
||||
@@ -14,23 +14,23 @@ labels: feature
|
||||
## 1. 基本信息
|
||||
请填写如下内容:
|
||||
|
||||
1. 域名提供商名称:
|
||||
1. 域名提供商名称:
|
||||
|
||||
|
||||
2. 管理页面地址:
|
||||
|
||||
|
||||
3. 是否有API接口,接口地址:
|
||||
3. 是否有API接口,接口地址:
|
||||
|
||||
|
||||
4. 如果没有API接口,网页登录是否有验证码:
|
||||
4. 如果没有API接口,网页登录是否有验证码:
|
||||
|
||||
|
||||
5. 是否可以提供测试账号?(如果可以请留下联系方式或者加作者好友)
|
||||
|
||||
|
||||
|
||||
## 2. 截图
|
||||
## 2. 截图
|
||||
|
||||
`域名管理页面截图`
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: 报告一个错误或问题
|
||||
about: 错误或问题报告
|
||||
title: "[BUG] "
|
||||
labels: bug
|
||||
---
|
||||
|
||||
|
||||
> 感谢您支持certd,请按如下规范提交issue
|
||||
> 感谢您支持certd,请按如下规范提交issue
|
||||
> 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues)
|
||||
|
||||
# bug提交
|
||||
@@ -1,15 +1,16 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: 新需求、新特性
|
||||
about: 新需求、新特性申请支持
|
||||
title: "[Feature] "
|
||||
labels: feature
|
||||
---
|
||||
|
||||
> > 感谢您支持certd,请按如下规范提交issue
|
||||
> > 感谢您支持certd,请按如下规范提交issue
|
||||
> 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues)
|
||||
|
||||
|
||||
# 新需求申请
|
||||
# 新特性申请
|
||||
>注意:这里仅供如果是要申请新的部署插件,请提交插件申请
|
||||
|
||||
## 1. 需求描述,需求背景
|
||||
`请在此处简要描述你所遇到的问题,必要时请贴出相关截图辅助理解`
|
||||
60
CHANGELOG.md
60
CHANGELOG.md
@@ -3,6 +3,66 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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
|
||||
|
||||
* 更新我爱云CDN域名地址,和部分目录结构 [@tyjsjxh](https://github.com/tyjsjxh) ([#514](https://github.com/certd/certd/issues/514)) ([78e7a81](https://github.com/certd/certd/commit/78e7a81638c2ee779f0ab6c3ba7e5c6f6e064151))
|
||||
* 修复cron选择组件星期显示错误的bug ([eb75e52](https://github.com/certd/certd/commit/eb75e52278f94a72643f7317e6740fb42666c68a))
|
||||
* 修复proxmox某些情况下执行卡住的bug ([ebd6917](https://github.com/certd/certd/commit/ebd6917a1d40ae4d94555c32b7e3c093d0599b94))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 部署到k8s支持自动创建secret ([c09c962](https://github.com/certd/certd/commit/c09c962cb676ca261610aa9f3e5105c9dae43f43))
|
||||
* 短信验证码支持腾讯云 ([9108459](https://github.com/certd/certd/commit/9108459ae42bcd95a59acba164a64e82e5f2cfe6))
|
||||
* 商业版支持自定义插件的参数配置 ([17f23f3](https://github.com/certd/certd/commit/17f23f37516af925d5049291d67d41e4271f81f8))
|
||||
* 腾讯云插件支持国际版 ([58e82d5](https://github.com/certd/certd/commit/58e82d5dbd4ebf089ef239578ef9b68454d17b30))
|
||||
* 腾讯云EO插件支持自动获取zoneid和域名列表 ([70fcdc9](https://github.com/certd/certd/commit/70fcdc9ebbfb7c883c0c8a2138f61a0776a9491b))
|
||||
* 支持部署到阿里云云原生API网关、AI网关 ([2ca20be](https://github.com/certd/certd/commit/2ca20be197720201fceabcce9d927f4dbc1cc872))
|
||||
* 支持部署到华为云obs ([9feb9d0](https://github.com/certd/certd/commit/9feb9d04b3c56ec95c06fcf4fd071eb0e88ffc6f))
|
||||
* 支持部署到dokploy ([7dbdeae](https://github.com/certd/certd/commit/7dbdeaebe0bfee7521a863fe5e6b4a712aec5876))
|
||||
* 支持删除宝塔证书夹中的过期证书 ([3575113](https://github.com/certd/certd/commit/3575113655be751d19f88c64491e98a89042d6a2))
|
||||
* 支持p7b证书格式 ([d9f4a57](https://github.com/certd/certd/commit/d9f4a5793d68a017a5d80ad5385cbda603c4e165))
|
||||
* lecdnv2支持api token ([e448934](https://github.com/certd/certd/commit/e4489343fee7754be07bcfc3323969dc3a30e90c))
|
||||
* openapi返回证书时挑选匹配范围最小的那一个;增加format参数,增加返回值p7b格式,增加detail返回 ([2085bcc](https://github.com/certd/certd/commit/2085bcceb61c3723c9bdfec4c4cc0917631ff5e5))
|
||||
* ssh 配置sudo免密提示 ([e1e7011](https://github.com/certd/certd/commit/e1e7011853ad0c5bd7b09c3690861d5aa34b2db4))
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复新部署的无法保存公共eab配置的bug ([d5dee75](https://github.com/certd/certd/commit/d5dee75df3bd635a597436e448b2de1407531f3a))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 阿里云 FC3.0 不在要求证书加密方式为旧版, 修复支持的协议类型可以正常选择 ([a34db74](https://github.com/certd/certd/commit/a34db7449eff6ad1dda01de673bf85579fa3865a))
|
||||
* 部署到腾讯云cdn,每个域名增加3每秒延迟 ([f7d43ad](https://github.com/certd/certd/commit/f7d43ad5af4663d4be369820a80d1fd9817ca4ab))
|
||||
* 腾讯云关闭证书通知增加开关选项,在腾讯云授权里面 ([a77c777](https://github.com/certd/certd/commit/a77c777980dd38d97d983124eeed1596879bba95))
|
||||
* 证书申请任务默认不发送申请成功通知 ([0283bd2](https://github.com/certd/certd/commit/0283bd2f978dbcd13d361129135e439dd9fbc180))
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1 +1 @@
|
||||
23:23
|
||||
00:43
|
||||
|
||||
@@ -45,7 +45,7 @@ services:
|
||||
# 设置环境变量即可自定义certd配置
|
||||
# 配置项见: packages/ui/certd-server/src/config/config.default.ts
|
||||
# 配置规则: certd_ + 配置项, 点号用_代替
|
||||
# #↓↓↓↓ ----------------------------- 如果忘记管理员密码,可以设置为true,重启之后,管理员密码将改成123456,然后请及时修改回false
|
||||
# #↓↓↓↓ ----------------------------- 如果忘记管理员密码,可以设置为true,docker compose up -d 重建容器之后,管理员密码将改成123456,然后请及时修改回false
|
||||
- certd_system_resetAdminPasswd=false
|
||||
|
||||
# 默认使用sqlite文件数据库,如果需要使用其他数据库,请设置以下环境变量
|
||||
|
||||
@@ -120,6 +120,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: "子域名托管", link: "/guide/use/cert/subdomain.md"},
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -3,6 +3,60 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 更新我爱云CDN域名地址,和部分目录结构 [@tyjsjxh](https://github.com/tyjsjxh) ([#514](https://github.com/certd/certd/issues/514)) ([78e7a81](https://github.com/certd/certd/commit/78e7a81638c2ee779f0ab6c3ba7e5c6f6e064151))
|
||||
* 修复cron选择组件星期显示错误的bug ([eb75e52](https://github.com/certd/certd/commit/eb75e52278f94a72643f7317e6740fb42666c68a))
|
||||
* 修复proxmox某些情况下执行卡住的bug ([ebd6917](https://github.com/certd/certd/commit/ebd6917a1d40ae4d94555c32b7e3c093d0599b94))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 部署到k8s支持自动创建secret ([c09c962](https://github.com/certd/certd/commit/c09c962cb676ca261610aa9f3e5105c9dae43f43))
|
||||
* 短信验证码支持腾讯云 ([9108459](https://github.com/certd/certd/commit/9108459ae42bcd95a59acba164a64e82e5f2cfe6))
|
||||
* 商业版支持自定义插件的参数配置 ([17f23f3](https://github.com/certd/certd/commit/17f23f37516af925d5049291d67d41e4271f81f8))
|
||||
* 腾讯云插件支持国际版 ([58e82d5](https://github.com/certd/certd/commit/58e82d5dbd4ebf089ef239578ef9b68454d17b30))
|
||||
* 腾讯云EO插件支持自动获取zoneid和域名列表 ([70fcdc9](https://github.com/certd/certd/commit/70fcdc9ebbfb7c883c0c8a2138f61a0776a9491b))
|
||||
* 支持部署到阿里云云原生API网关、AI网关 ([2ca20be](https://github.com/certd/certd/commit/2ca20be197720201fceabcce9d927f4dbc1cc872))
|
||||
* 支持部署到华为云obs ([9feb9d0](https://github.com/certd/certd/commit/9feb9d04b3c56ec95c06fcf4fd071eb0e88ffc6f))
|
||||
* 支持部署到dokploy ([7dbdeae](https://github.com/certd/certd/commit/7dbdeaebe0bfee7521a863fe5e6b4a712aec5876))
|
||||
* 支持删除宝塔证书夹中的过期证书 ([3575113](https://github.com/certd/certd/commit/3575113655be751d19f88c64491e98a89042d6a2))
|
||||
* 支持p7b证书格式 ([d9f4a57](https://github.com/certd/certd/commit/d9f4a5793d68a017a5d80ad5385cbda603c4e165))
|
||||
* lecdnv2支持api token ([e448934](https://github.com/certd/certd/commit/e4489343fee7754be07bcfc3323969dc3a30e90c))
|
||||
* openapi返回证书时挑选匹配范围最小的那一个;增加format参数,增加返回值p7b格式,增加detail返回 ([2085bcc](https://github.com/certd/certd/commit/2085bcceb61c3723c9bdfec4c4cc0917631ff5e5))
|
||||
* ssh 配置sudo免密提示 ([e1e7011](https://github.com/certd/certd/commit/e1e7011853ad0c5bd7b09c3690861d5aa34b2db4))
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复新部署的无法保存公共eab配置的bug ([d5dee75](https://github.com/certd/certd/commit/d5dee75df3bd635a597436e448b2de1407531f3a))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 阿里云 FC3.0 不在要求证书加密方式为旧版, 修复支持的协议类型可以正常选择 ([a34db74](https://github.com/certd/certd/commit/a34db7449eff6ad1dda01de673bf85579fa3865a))
|
||||
* 部署到腾讯云cdn,每个域名增加3每秒延迟 ([f7d43ad](https://github.com/certd/certd/commit/f7d43ad5af4663d4be369820a80d1fd9817ca4ab))
|
||||
* 腾讯云关闭证书通知增加开关选项,在腾讯云授权里面 ([a77c777](https://github.com/certd/certd/commit/a77c777980dd38d97d983124eeed1596879bba95))
|
||||
* 证书申请任务默认不发送申请成功通知 ([0283bd2](https://github.com/certd/certd/commit/0283bd2f978dbcd13d361129135e439dd9fbc180))
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复授权配置复制功能,无法复制已加密字段的问题 ([221e068](https://github.com/certd/certd/commit/221e068bac3af6cd5d1794f8cd4c2ec5c0bc3f45))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 百度云支持上传到证书托管,支持部署到负载均衡 ([798a48a](https://github.com/certd/certd/commit/798a48aa9686fd5d11cfffb6cd93eadfc40aacb3))
|
||||
* 部署到百度cdn支持自动获取域名列表选择 ([4e432ed](https://github.com/certd/certd/commit/4e432ed03f4fb564e85a2f284ee26b58400b82f5))
|
||||
* 验证码可重试次数设置为3次 ([1bdceee](https://github.com/certd/certd/commit/1bdceeecf4b5daecdd621a05a2596b6eb45ce8ea))
|
||||
* 增加找回密码的验证码可重试次数 [@nicheng-he](https://github.com/nicheng-he) ([#496](https://github.com/certd/certd/issues/496)) ([fe03f99](https://github.com/certd/certd/commit/fe03f9942b5662fb90cad86da10782f5dc3603f5))
|
||||
* 支持阿里云API网关 ([9e1e4ee](https://github.com/certd/certd/commit/9e1e4eeec2859759ca5b07834c9d24cf88a6ad33))
|
||||
* 支持部署到金山云CDN ([dfa74a6](https://github.com/certd/certd/commit/dfa74a69f7cbb9009d3e20c7eecfa1b905a00cf0))
|
||||
* 支持更新金山云cdn证书 ([462e22a](https://github.com/certd/certd/commit/462e22a3b0a94887462fe6aa68e4671a365e0737))
|
||||
* 支持apisix证书部署 ([9b63fb4](https://github.com/certd/certd/commit/9b63fb4ee2c6b56139160c5bf63482dab0869c2b))
|
||||
|
||||
## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
# 常见报错解决
|
||||
|
||||
## 1. getaddrinfo ENOTFOUND错误
|
||||
如果出现`getaddrinfo ENOTFOUND`/`getaddrinfo EAI_AGAIN`错误,可以尝试在`docker-compose.yaml`中设置dns
|
||||
```yaml
|
||||
version: '3.3' # 兼容旧版docker-compose
|
||||
services:
|
||||
certd:
|
||||
#↓↓↓↓ ------------ # 如果出现getaddrinfo ENOTFOUND 或 EAI_AGAIN错误,可以尝试设置dns
|
||||
dns:
|
||||
- 223.5.5.5 # 阿里云公共dns
|
||||
- 223.6.6.6
|
||||
# # ↓↓↓↓ ------- # 如果你服务器在腾讯云,可以用这个替换上面阿里云的公共dns
|
||||
# - 119.29.29.29 # 腾讯云公共dns
|
||||
# - 182.254.116.116
|
||||
# # ↓↓↓↓ ------- # 如果你服务器部署在国外,可以用这个替换上面阿里云的公共dns
|
||||
# - 8.8.8.8 # 谷歌公共dns
|
||||
# - 8.8.4.4
|
||||
```
|
||||
|
||||
如果仍然有问题,按如下步骤检查是否能够ping通域名
|
||||
```shell
|
||||
docker exec -it certd /bin/sh
|
||||
ping www.baidu.com
|
||||
ping gg.px.certd.handfree.work
|
||||
ping app.handfree.work
|
||||
```
|
||||
|
||||
如果您是宝塔部署的
|
||||
可以试试将容器网络加入brige网络,看是否解决问题
|
||||

|
||||
|
||||
如果还是不行,请联系我们
|
||||
|
||||
|
||||
## 2. 连接IPv6超时
|
||||
docker-compose 需要放开IPv6网络的配置
|
||||
```yaml
|
||||
services:
|
||||
certd:
|
||||
networks:
|
||||
- ip6net
|
||||
# ↓↓↓↓ -------------------------------------------------------------- 启用ipv6网络,还需要把上面networks的注释放开
|
||||
networks:
|
||||
ip6net:
|
||||
enable_ipv6: true
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 2001:db8::/64
|
||||
|
||||
```
|
||||
|
||||
## 3. SSL_CERT_NOT_MATCH_DOMAIN_ERROR
|
||||
部署证书任务报类似 `SSL_CERT_NOT_MATCH_DOMAIN_ERROR`错误
|
||||
这是由于当前流水线的证书域名与要部署的目标站点的域名不匹配导致的,在申请证书任务中,增加目标站点域名,重新运行流水线即可
|
||||
|
||||
|
||||
## 4. 没有服务器配置文件,请检查是否开启了外网映射!
|
||||
宝塔网站证书部署报错:`Error: 没有服务器配置文件,请检查是否开启了外网映射!`
|
||||
解决方案:先手动在宝塔网站中设置一次证书
|
||||
|
||||
|
||||
## 5. 如何查看容器日志
|
||||
```shell
|
||||
docker logs -f --tail 200 certd
|
||||
```
|
||||
|
||||
## 6. 容器内走时不准,或者时区不对
|
||||
走时不准确,慢慢偏差越来越大
|
||||
或者整个时区都不对
|
||||
可以尝试挂载localtime文件
|
||||
```yaml
|
||||
volumes:
|
||||
# ↓↓↓↓↓ -------------------- 如果走时不准,请尝试挂载localtime文件
|
||||
- /etc/localtime:/etc/localtime
|
||||
- /etc/timezone:/etc/timezone
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
BIN
docs/guide/use/cert/images/subdomain1.png
Normal file
BIN
docs/guide/use/cert/images/subdomain1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
BIN
docs/guide/use/cert/images/subdomain2.png
Normal file
BIN
docs/guide/use/cert/images/subdomain2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
10
docs/guide/use/cert/subdomain.md
Normal file
10
docs/guide/use/cert/subdomain.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# 二级子域名托管
|
||||
如果你的域名是免费的二级域名(比如:sub.handsfree.work),托管在CF或者阿里云上
|
||||
在使用DNS方式校验时需要设置子域名托管
|
||||
|
||||
[阿里云子域名托管说明](https://help.aliyun.com/zh/dns/pubz-subdomain-management)
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
@@ -9,5 +9,5 @@
|
||||
}
|
||||
},
|
||||
"npmClient": "pnpm",
|
||||
"version": "1.36.16"
|
||||
"version": "1.36.19"
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"init": "lerna run build",
|
||||
"init:dev": "lerna run build",
|
||||
"docs:dev": "vitepress dev docs",
|
||||
"docs:build": "vitepress build docs",
|
||||
"docs:build": "npm run copylogs && vitepress build docs",
|
||||
"docs:preview": "vitepress preview docs",
|
||||
"pub": "echo 1"
|
||||
},
|
||||
|
||||
@@ -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.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
|
||||
|
||||
## [1.36.17](https://github.com/publishlab/node-acme-client/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.36.16](https://github.com/publishlab/node-acme-client/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "Simple and unopinionated ACME client",
|
||||
"private": false,
|
||||
"author": "nmorsman",
|
||||
"version": "1.36.16",
|
||||
"version": "1.36.19",
|
||||
"type": "module",
|
||||
"module": "scr/index.js",
|
||||
"main": "src/index.js",
|
||||
@@ -18,7 +18,7 @@
|
||||
"types"
|
||||
],
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.36.16",
|
||||
"@certd/basic": "^1.36.19",
|
||||
"@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": "fb7341f1f7d05d05c5439a36594665e3855d6a00"
|
||||
"gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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.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
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
@@ -1 +1 @@
|
||||
12:46
|
||||
00:30
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/basic",
|
||||
"private": false,
|
||||
"version": "1.36.16",
|
||||
"version": "1.36.19",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -45,5 +45,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00"
|
||||
"gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
@@ -84,7 +83,7 @@ export function getGlobalAgents() {
|
||||
/**
|
||||
* @description 创建请求实例
|
||||
*/
|
||||
export function createAxiosService({ logger }: { logger: Logger }) {
|
||||
export function createAxiosService({ logger }: { logger: ILogger }) {
|
||||
// 创建一个 axios 实例
|
||||
const service = axios.create();
|
||||
|
||||
|
||||
@@ -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.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
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/pipeline",
|
||||
"private": false,
|
||||
"version": "1.36.16",
|
||||
"version": "1.36.19",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -17,8 +17,8 @@
|
||||
"pub": "npm publish"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.36.16",
|
||||
"@certd/plus-core": "^1.36.16",
|
||||
"@certd/basic": "^1.36.19",
|
||||
"@certd/plus-core": "^1.36.19",
|
||||
"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": "fb7341f1f7d05d05c5439a36594665e3855d6a00"
|
||||
"gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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}的配置未找到对应的输出值,请确认对应的前置任务是否存在或者是否执行正确`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<any>) {
|
||||
|
||||
@@ -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.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
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-huawei",
|
||||
"private": false,
|
||||
"version": "1.36.16",
|
||||
"version": "1.36.19",
|
||||
"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": "fb7341f1f7d05d05c5439a36594665e3855d6a00"
|
||||
"gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
|
||||
}
|
||||
|
||||
@@ -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.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
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-iframe",
|
||||
"private": false,
|
||||
"version": "1.36.16",
|
||||
"version": "1.36.19",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -31,5 +31,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00"
|
||||
"gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
|
||||
}
|
||||
|
||||
@@ -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.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
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/jdcloud",
|
||||
"version": "1.36.16",
|
||||
"version": "1.36.19",
|
||||
"description": "jdcloud openApi sdk",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
@@ -61,5 +61,5 @@
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
"gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00"
|
||||
"gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
|
||||
}
|
||||
|
||||
@@ -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.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
|
||||
|
||||
* 部署到k8s支持自动创建secret ([c09c962](https://github.com/certd/certd/commit/c09c962cb676ca261610aa9f3e5105c9dae43f43))
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-k8s",
|
||||
"private": false,
|
||||
"version": "1.36.16",
|
||||
"version": "1.36.19",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -17,7 +17,7 @@
|
||||
"pub": "npm publish"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.36.16",
|
||||
"@certd/basic": "^1.36.19",
|
||||
"@kubernetes/client-node": "0.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -32,5 +32,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00"
|
||||
"gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -90,7 +90,7 @@ export class K8sClient {
|
||||
async createSecret(opts: { namespace: string; body: V1Secret }) {
|
||||
const namespace = opts.namespace || "default";
|
||||
const created = await this.client.createNamespacedSecret(namespace, opts.body);
|
||||
this.logger.info("new secrets:", opts.body);
|
||||
this.logger.info("new secrets:", opts.body.metadata);
|
||||
return created.body;
|
||||
}
|
||||
|
||||
@@ -103,17 +103,39 @@ export class K8sClient {
|
||||
// return await this.client.replaceNamespacedSecret(secretName, namespace, opts.body);
|
||||
// }
|
||||
|
||||
async patchSecret(opts: { namespace: string; secretName: string; body: V1Secret }) {
|
||||
async patchSecret(opts: { namespace: string; secretName: string; body: V1Secret; createOnNotFound?: boolean }) {
|
||||
const namespace = opts.namespace || "default";
|
||||
const secretName = opts.secretName;
|
||||
if (secretName == null) {
|
||||
throw new Error("secretName 不能为空");
|
||||
}
|
||||
this.logger.info("patch secret:", secretName, namespace);
|
||||
const oldSecret = await this.client.readNamespacedSecret(secretName, namespace);
|
||||
const newSecret = _.merge(oldSecret.body, opts.body);
|
||||
let oldSecret: any = null;
|
||||
try {
|
||||
oldSecret = await this.client.readNamespacedSecret(secretName, namespace);
|
||||
} catch (e) {
|
||||
//@ts-ignore
|
||||
if (e.response?.body?.code === 404) {
|
||||
this.logger.warn(`secret ${secretName} 不存在`);
|
||||
if (opts.createOnNotFound) {
|
||||
//没有找到,则创建
|
||||
const body = merge(
|
||||
{
|
||||
type: "type: kubernetes.io/tls",
|
||||
},
|
||||
opts.body
|
||||
);
|
||||
const res = await this.createSecret({ namespace, body });
|
||||
this.logger.info(`secret ${secretName} 已创建`);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
const newSecret = merge(oldSecret.body, opts.body);
|
||||
const res = await this.client.replaceNamespacedSecret(secretName, namespace, newSecret);
|
||||
this.logger.info("secret updated");
|
||||
this.logger.info(`secret ${secretName} 已更新`);
|
||||
return res.body;
|
||||
}
|
||||
|
||||
@@ -145,7 +167,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;
|
||||
|
||||
@@ -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.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
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/lib-server",
|
||||
"version": "1.36.16",
|
||||
"version": "1.36.19",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -27,10 +27,10 @@
|
||||
],
|
||||
"license": "AGPL",
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.36.16",
|
||||
"@certd/basic": "^1.36.16",
|
||||
"@certd/pipeline": "^1.36.16",
|
||||
"@certd/plus-core": "^1.36.16",
|
||||
"@certd/acme-client": "^1.36.19",
|
||||
"@certd/basic": "^1.36.19",
|
||||
"@certd/pipeline": "^1.36.19",
|
||||
"@certd/plus-core": "^1.36.19",
|
||||
"@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": "fb7341f1f7d05d05c5439a36594665e3855d6a00"
|
||||
"gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
|
||||
}
|
||||
|
||||
@@ -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.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
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/midway-flyway-js",
|
||||
"version": "1.36.16",
|
||||
"version": "1.36.19",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -46,5 +46,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00"
|
||||
"gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
|
||||
}
|
||||
|
||||
@@ -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.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
|
||||
|
||||
* 支持p7b证书格式 ([d9f4a57](https://github.com/certd/certd/commit/d9f4a5793d68a017a5d80ad5385cbda603c4e165))
|
||||
* openapi返回证书时挑选匹配范围最小的那一个;增加format参数,增加返回值p7b格式,增加detail返回 ([2085bcc](https://github.com/certd/certd/commit/2085bcceb61c3723c9bdfec4c4cc0917631ff5e5))
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 证书申请任务默认不发送申请成功通知 ([0283bd2](https://github.com/certd/certd/commit/0283bd2f978dbcd13d361129135e439dd9fbc180))
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-cert",
|
||||
"private": false,
|
||||
"version": "1.36.16",
|
||||
"version": "1.36.19",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -16,10 +16,10 @@
|
||||
"pub": "npm publish"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.36.16",
|
||||
"@certd/basic": "^1.36.16",
|
||||
"@certd/pipeline": "^1.36.16",
|
||||
"@certd/plugin-lib": "^1.36.16",
|
||||
"@certd/acme-client": "^1.36.19",
|
||||
"@certd/basic": "^1.36.19",
|
||||
"@certd/pipeline": "^1.36.19",
|
||||
"@certd/plugin-lib": "^1.36.19",
|
||||
"@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": "fb7341f1f7d05d05c5439a36594665e3855d6a00"
|
||||
"gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -48,8 +48,9 @@ export type CertInfo = {
|
||||
der?: string;
|
||||
jks?: string;
|
||||
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;
|
||||
@@ -372,6 +373,7 @@ export class AcmeService {
|
||||
commonName,
|
||||
...csrInfo,
|
||||
altNames,
|
||||
emailAddress: email,
|
||||
},
|
||||
privateKey
|
||||
);
|
||||
|
||||
@@ -28,7 +28,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||
"2、子域名被通配符包含的不要填写,例如:www.foo.com已经被*.foo.com包含,不要填写www.foo.com\n" +
|
||||
"3、泛域名只能通配*号那一级(*.foo.com的证书不能用于xxx.yyy.foo.com、不能用于foo.com)\n" +
|
||||
"4、输入一个,空格之后,再输入下一个 \n" +
|
||||
"5、如果您配置了子域托管解析,请先[设置托管子域名](#/certd/pipeline/subDomain)",
|
||||
"5、如果设置了子域托管解析(比如免费的二级域名托管在CF或者阿里云),请先[设置托管子域名](#/certd/pipeline/subDomain)",
|
||||
})
|
||||
domains!: string[];
|
||||
|
||||
@@ -125,6 +125,10 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||
cert.jks = res.jks;
|
||||
}
|
||||
|
||||
if (cert.p7b == null && res.p7b) {
|
||||
cert.p7b = res.p7b;
|
||||
}
|
||||
|
||||
this.logger.info("转换证书格式成功");
|
||||
} catch (e) {
|
||||
this.logger.error("转换证书格式失败", e);
|
||||
@@ -150,6 +154,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||
zip.file("intermediate.crt", cert.ic);
|
||||
zip.file("origin.crt", cert.oc);
|
||||
zip.file("one.pem", cert.one);
|
||||
zip.file("cert.p7b", cert.p7b);
|
||||
if (cert.pfx) {
|
||||
zip.file("cert.pfx", Buffer.from(cert.pfx, "base64"));
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
|
||||
|
||||
@TaskInput({
|
||||
title: "证书申请成功通知",
|
||||
value: true,
|
||||
value: false,
|
||||
component: {
|
||||
name: "a-switch",
|
||||
vModel: "checked",
|
||||
@@ -41,7 +41,7 @@ export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
|
||||
order: 100,
|
||||
helper: "证书申请成功后是否发送通知,优先使用默认通知渠道",
|
||||
})
|
||||
successNotify = true;
|
||||
successNotify = false;
|
||||
|
||||
// @TaskInput({
|
||||
// title: "CsrInfo",
|
||||
|
||||
@@ -17,9 +17,19 @@ export type CertReaderHandleContext = {
|
||||
tmpIcPath?: string;
|
||||
tmpJksPath?: string;
|
||||
tmpOnePath?: string;
|
||||
tmpP7bPath?: string;
|
||||
};
|
||||
export type CertReaderHandle = (ctx: CertReaderHandleContext) => Promise<void>;
|
||||
export type HandleOpts = { logger: ILogger; handle: CertReaderHandle };
|
||||
|
||||
const formats = {
|
||||
pem: ["crt", "key", "ic"],
|
||||
one: ["one"],
|
||||
pfx: ["pfx"],
|
||||
der: ["der"],
|
||||
jks: ["jks"],
|
||||
p7b: ["p7b", "key"],
|
||||
};
|
||||
export class CertReader {
|
||||
cert: CertInfo;
|
||||
|
||||
@@ -73,8 +83,17 @@ export class CertReader {
|
||||
return arr[0] + endStr;
|
||||
}
|
||||
|
||||
toCertInfo(): CertInfo {
|
||||
return this.cert;
|
||||
toCertInfo(format?: string): CertInfo {
|
||||
if (!format) {
|
||||
return this.cert;
|
||||
}
|
||||
|
||||
const formatArr = formats[format];
|
||||
const res: any = {};
|
||||
formatArr.forEach((key: string) => {
|
||||
res[key] = this.cert[key];
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
getCrtDetail(crt: string = this.cert.crt) {
|
||||
@@ -124,7 +143,7 @@ export class CertReader {
|
||||
return domain;
|
||||
}
|
||||
|
||||
saveToFile(type: "crt" | "key" | "pfx" | "der" | "oc" | "one" | "ic" | "jks", filepath?: string) {
|
||||
saveToFile(type: "crt" | "key" | "pfx" | "der" | "oc" | "one" | "ic" | "jks" | "p7b", filepath?: string) {
|
||||
if (!this.cert[type]) {
|
||||
return;
|
||||
}
|
||||
@@ -138,7 +157,7 @@ export class CertReader {
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
if (type === "crt" || type === "key" || type === "ic" || type === "oc" || type === "one") {
|
||||
if (type === "crt" || type === "key" || type === "ic" || type === "oc" || type === "one" || type === "p7b") {
|
||||
fs.writeFileSync(filepath, this.cert[type]);
|
||||
} else {
|
||||
fs.writeFileSync(filepath, Buffer.from(this.cert[type], "base64"));
|
||||
@@ -157,17 +176,19 @@ export class CertReader {
|
||||
const tmpDerPath = this.saveToFile("der");
|
||||
const tmpJksPath = this.saveToFile("jks");
|
||||
const tmpOnePath = this.saveToFile("one");
|
||||
const tmpP7bPath = this.saveToFile("p7b");
|
||||
logger.info("本地文件写入成功");
|
||||
try {
|
||||
return await opts.handle({
|
||||
reader: this,
|
||||
tmpCrtPath: tmpCrtPath,
|
||||
tmpKeyPath: tmpKeyPath,
|
||||
tmpPfxPath: tmpPfxPath,
|
||||
tmpDerPath: tmpDerPath,
|
||||
tmpIcPath: tmpIcPath,
|
||||
tmpJksPath: tmpJksPath,
|
||||
tmpOcPath: tmpOcPath,
|
||||
tmpCrtPath,
|
||||
tmpKeyPath,
|
||||
tmpPfxPath,
|
||||
tmpDerPath,
|
||||
tmpIcPath,
|
||||
tmpJksPath,
|
||||
tmpOcPath,
|
||||
tmpP7bPath,
|
||||
tmpOnePath,
|
||||
});
|
||||
} catch (err) {
|
||||
@@ -189,6 +210,7 @@ export class CertReader {
|
||||
removeFile(tmpIcPath);
|
||||
removeFile(tmpJksPath);
|
||||
removeFile(tmpOnePath);
|
||||
removeFile(tmpP7bPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,10 +221,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) {
|
||||
|
||||
@@ -18,11 +18,13 @@ export class CertConverter {
|
||||
pfx: string;
|
||||
der: string;
|
||||
jks: string;
|
||||
p7b: string;
|
||||
}> {
|
||||
const certReader = new CertReader(opts.cert);
|
||||
let pfx: string;
|
||||
let der: string;
|
||||
let jks: string;
|
||||
let p7b: string;
|
||||
const handle = async (ctx: CertReaderHandleContext) => {
|
||||
// 调用openssl 转pfx
|
||||
pfx = await this.convertPfx(ctx, opts.pfxPassword, opts.pfxArgs);
|
||||
@@ -31,6 +33,8 @@ export class CertConverter {
|
||||
der = await this.convertDer(ctx);
|
||||
|
||||
jks = await this.convertJks(ctx, opts.pfxPassword);
|
||||
|
||||
p7b = await this.convertP7b(ctx);
|
||||
};
|
||||
|
||||
await certReader.readCertFile({ logger: this.logger, handle });
|
||||
@@ -39,6 +43,7 @@ export class CertConverter {
|
||||
pfx,
|
||||
der,
|
||||
jks,
|
||||
p7b,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -95,6 +100,23 @@ export class CertConverter {
|
||||
return derCert;
|
||||
}
|
||||
|
||||
async convertP7b(opts: CertReaderHandleContext) {
|
||||
const { tmpCrtPath } = opts;
|
||||
const p7bPath = path.join(os.tmpdir(), "/certd/tmp/", Math.floor(Math.random() * 1000000) + `_cert.p7b`);
|
||||
const dir = path.dirname(p7bPath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
//openssl crl2pkcs7 -nocrl \
|
||||
// -certfile your_domain.crt \
|
||||
// -certfile intermediate.crt \
|
||||
// -out chain.p7b
|
||||
await this.exec(`openssl crl2pkcs7 -nocrl -certfile ${tmpCrtPath} -out ${p7bPath}`);
|
||||
const fileBuffer = fs.readFileSync(p7bPath);
|
||||
const p7bCert = fileBuffer.toString();
|
||||
fs.unlinkSync(p7bPath);
|
||||
return p7bCert;
|
||||
}
|
||||
async convertJks(opts: CertReaderHandleContext, pfxPassword = "") {
|
||||
const jksPassword = pfxPassword || "123456";
|
||||
try {
|
||||
@@ -113,9 +135,7 @@ export class CertConverter {
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
await this.exec(
|
||||
`keytool -importkeystore -srckeystore ${p12Path} -srcstoretype PKCS12 -srcstorepass "${jksPassword}" -destkeystore ${jksPath} -deststoretype PKCS12 -deststorepass "${jksPassword}" `
|
||||
);
|
||||
await this.exec(`keytool -importkeystore -srckeystore ${p12Path} -srcstoretype PKCS12 -srcstorepass "${jksPassword}" -destkeystore ${jksPath} -deststoretype PKCS12 -deststorepass "${jksPassword}" `);
|
||||
fs.unlinkSync(p12Path);
|
||||
|
||||
const fileBuffer = fs.readFileSync(jksPath);
|
||||
|
||||
@@ -89,6 +89,7 @@ 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授权,无需翻墙",
|
||||
@@ -104,7 +105,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 +138,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||
})
|
||||
},
|
||||
show: ctx.compute(({form})=>{
|
||||
return form.challengeType === 'dns'
|
||||
return form.challengeType === 'dns'
|
||||
})
|
||||
}
|
||||
`,
|
||||
@@ -194,6 +195,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 +211,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)
|
||||
})
|
||||
}
|
||||
`,
|
||||
@@ -339,8 +352,8 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||
async onInit() {
|
||||
let eab: EabAccess = null;
|
||||
|
||||
if (this.sslProvider === "google") {
|
||||
if (this.googleAccessId) {
|
||||
if (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 +361,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 +405,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) : {}
|
||||
);
|
||||
|
||||
@@ -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.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
|
||||
|
||||
* 腾讯云插件支持国际版 ([58e82d5](https://github.com/certd/certd/commit/58e82d5dbd4ebf089ef239578ef9b68454d17b30))
|
||||
* 支持部署到阿里云云原生API网关、AI网关 ([2ca20be](https://github.com/certd/certd/commit/2ca20be197720201fceabcce9d927f4dbc1cc872))
|
||||
* ssh 配置sudo免密提示 ([e1e7011](https://github.com/certd/certd/commit/e1e7011853ad0c5bd7b09c3690861d5aa34b2db4))
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 腾讯云关闭证书通知增加开关选项,在腾讯云授权里面 ([a77c777](https://github.com/certd/certd/commit/a77c777980dd38d97d983124eeed1596879bba95))
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-lib",
|
||||
"private": false,
|
||||
"version": "1.36.16",
|
||||
"version": "1.36.19",
|
||||
"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.16",
|
||||
"@certd/pipeline": "^1.36.16",
|
||||
"@certd/basic": "^1.36.19",
|
||||
"@certd/pipeline": "^1.36.19",
|
||||
"@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": "fb7341f1f7d05d05c5439a36594665e3855d6a00"
|
||||
"gitHead": "ea18a5ad151b296fda54fb5bcbe64c7d80cdff2f"
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ export type AliyunClientV2Req = {
|
||||
// 接口 HTTP 方法
|
||||
method?: "GET" | "POST";
|
||||
authType?: "AK";
|
||||
style?: "RPC";
|
||||
style?: "RPC" | "ROA";
|
||||
// 接口 PATH
|
||||
pathname?: `/`;
|
||||
pathname?: string;
|
||||
|
||||
data?: any;
|
||||
};
|
||||
@@ -63,10 +63,10 @@ export class AliyunClientV2 {
|
||||
protocol: "HTTPS",
|
||||
// 接口 HTTP 方法
|
||||
method: req.method ?? "POST",
|
||||
authType: "AK",
|
||||
style: "RPC",
|
||||
authType: req.authType ?? "AK",
|
||||
style: req.style ?? "RPC",
|
||||
// 接口 PATH
|
||||
pathname: `/`,
|
||||
pathname: req.pathname ?? `/`,
|
||||
// 接口请求体内容格式
|
||||
reqBodyType: "json",
|
||||
// 接口响应体内容格式
|
||||
|
||||
@@ -9,7 +9,8 @@ export type AliyunCertInfo = {
|
||||
export type AliyunSslClientOpts = {
|
||||
access: AliyunAccess;
|
||||
logger: ILogger;
|
||||
endpoint: string;
|
||||
endpoint?: string;
|
||||
region?: string;
|
||||
};
|
||||
|
||||
export type AliyunSslGetResourceListReq = {
|
||||
@@ -48,10 +49,19 @@ export class AliyunSslClient {
|
||||
async getClient() {
|
||||
const access = this.opts.access;
|
||||
const client = new AliyunClient({ logger: this.opts.logger });
|
||||
|
||||
let endpoint = this.opts.endpoint || "cas.aliyuncs.com";
|
||||
if (this.opts.endpoint == null && this.opts.region) {
|
||||
if (this.opts.region === "cn-hangzhou") {
|
||||
endpoint = "cas.aliyuncs.com";
|
||||
} else {
|
||||
endpoint = `cas.${this.opts.region}.aliyuncs.com`;
|
||||
}
|
||||
}
|
||||
await client.init({
|
||||
accessKeyId: access.accessKeyId,
|
||||
accessKeySecret: access.accessKeySecret,
|
||||
endpoint: `https://${this.opts.endpoint || "cas.aliyuncs.com"}`,
|
||||
endpoint: `https://${endpoint}`,
|
||||
apiVersion: "2020-04-07",
|
||||
});
|
||||
return client;
|
||||
|
||||
@@ -5,6 +5,7 @@ import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline";
|
||||
title: "天翼云授权",
|
||||
desc: "",
|
||||
icon: "ant-design:aliyun-outlined",
|
||||
order: 2,
|
||||
})
|
||||
export class CtyunAccess extends BaseAccess {
|
||||
@AccessInput({
|
||||
|
||||
@@ -6,6 +6,7 @@ import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline";
|
||||
desc: "",
|
||||
icon: "svg:icon-qiniuyun",
|
||||
input: {},
|
||||
order: 2,
|
||||
})
|
||||
export class QiniuAccess extends BaseAccess {
|
||||
@AccessInput({
|
||||
|
||||
@@ -5,6 +5,7 @@ import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline";
|
||||
desc: "",
|
||||
icon: "clarity:host-line",
|
||||
input: {},
|
||||
order: 0,
|
||||
})
|
||||
export class SshAccess extends BaseAccess {
|
||||
@AccessInput({
|
||||
@@ -85,6 +86,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)",
|
||||
@@ -135,9 +145,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";
|
||||
}
|
||||
|
||||
@@ -247,6 +247,9 @@ export class AsyncSsh2Client {
|
||||
const err = this.convert(iconv, ret);
|
||||
stdErr += err;
|
||||
hasErrorLog = true;
|
||||
if (err.includes("sudo: a password is required")) {
|
||||
this.logger.warn("请配置sudo免密,否则命令无法执行");
|
||||
}
|
||||
this.logger.error(`[${this.connConf.host}][error]: ` + err.trimEnd());
|
||||
});
|
||||
});
|
||||
@@ -466,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;
|
||||
@@ -584,10 +588,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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline";
|
||||
name: "tencent",
|
||||
title: "腾讯云",
|
||||
icon: "svg:icon-tencentcloud",
|
||||
order: 0,
|
||||
})
|
||||
export class TencentAccess extends BaseAccess {
|
||||
@AccessInput({
|
||||
@@ -46,7 +47,21 @@ export class TencentAccess extends BaseAccess {
|
||||
})
|
||||
accountType: string;
|
||||
|
||||
@AccessInput({
|
||||
title: "关闭证书过期通知",
|
||||
value: true,
|
||||
component: {
|
||||
name: "a-switch",
|
||||
vModel: "checked",
|
||||
},
|
||||
})
|
||||
closeExpiresNotify: boolean = true;
|
||||
|
||||
isIntl() {
|
||||
return this.accountType === "intl";
|
||||
}
|
||||
|
||||
intlDomain() {
|
||||
return this.isIntl() ? "intl." : "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export class TencentSslClient {
|
||||
region: this.region,
|
||||
profile: {
|
||||
httpProfile: {
|
||||
endpoint: "ssl.tencentcloudapi.com",
|
||||
endpoint: this.access.isIntl() ? "ssl.intl.tencentcloudapi.com" : "ssl.tencentcloudapi.com",
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -50,7 +50,10 @@ export class TencentSslClient {
|
||||
const ret = await client.UploadCertificate(params);
|
||||
this.checkRet(ret);
|
||||
this.logger.info(`证书[${opts.certName}]上传成功:tencentCertId=`, ret.CertificateId);
|
||||
await this.switchCertNotify([ret.CertificateId], true);
|
||||
if (this.access.closeExpiresNotify) {
|
||||
await this.switchCertNotify([ret.CertificateId], true);
|
||||
}
|
||||
|
||||
return ret.CertificateId;
|
||||
}
|
||||
|
||||
@@ -73,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;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,39 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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
|
||||
|
||||
* 修复cron选择组件星期显示错误的bug ([eb75e52](https://github.com/certd/certd/commit/eb75e52278f94a72643f7317e6740fb42666c68a))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 短信验证码支持腾讯云 ([9108459](https://github.com/certd/certd/commit/9108459ae42bcd95a59acba164a64e82e5f2cfe6))
|
||||
* 商业版支持自定义插件的参数配置 ([17f23f3](https://github.com/certd/certd/commit/17f23f37516af925d5049291d67d41e4271f81f8))
|
||||
* 支持p7b证书格式 ([d9f4a57](https://github.com/certd/certd/commit/d9f4a5793d68a017a5d80ad5385cbda603c4e165))
|
||||
* openapi返回证书时挑选匹配范围最小的那一个;增加format参数,增加返回值p7b格式,增加detail返回 ([2085bcc](https://github.com/certd/certd/commit/2085bcceb61c3723c9bdfec4c4cc0917631ff5e5))
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/ui-client
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-client",
|
||||
"version": "1.36.16",
|
||||
"version": "1.36.19",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --open",
|
||||
@@ -30,6 +30,7 @@
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"@aws-sdk/client-s3": "^3.535.0",
|
||||
"@aws-sdk/s3-request-presigner": "^3.535.0",
|
||||
"@certd/vue-js-cron-light": "^4.0.14",
|
||||
"@ctrl/tinycolor": "^4.1.0",
|
||||
"@fast-crud/fast-crud": "^1.25.13",
|
||||
"@fast-crud/fast-extends": "^1.25.13",
|
||||
@@ -43,7 +44,6 @@
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@tanstack/vue-store": "^0.7.0",
|
||||
"@vee-validate/zod": "^4.15.0",
|
||||
"@vue-js-cron/light": "^4.0.5",
|
||||
"@vue/shared": "^3.5.13",
|
||||
"@vueuse/core": "^10.11.0",
|
||||
"ant-design-vue": "^4.2.6",
|
||||
@@ -103,8 +103,8 @@
|
||||
"zod-defaults": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/lib-iframe": "^1.36.16",
|
||||
"@certd/pipeline": "^1.36.16",
|
||||
"@certd/lib-iframe": "^1.36.19",
|
||||
"@certd/pipeline": "^1.36.19",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/chai": "^4.3.12",
|
||||
|
||||
@@ -4,8 +4,8 @@ import vip from "./vip-button/install.js";
|
||||
import { CheckCircleOutlined, InfoCircleOutlined, UndoOutlined } from "@ant-design/icons-vue";
|
||||
import CronEditor from "./cron-editor/index.vue";
|
||||
import FoldBox from "./fold-box.vue";
|
||||
import { CronLight } from "@vue-js-cron/light";
|
||||
import "@vue-js-cron/light/dist/light.css";
|
||||
import { CronLight } from "@certd/vue-js-cron-light";
|
||||
import "@certd/vue-js-cron-light/dist/light.css";
|
||||
import Plugins from "./plugins/index";
|
||||
import LoadingButton from "./loading-button.vue";
|
||||
import IconSelect from "./icon-select.vue";
|
||||
|
||||
@@ -66,7 +66,7 @@ const getOptions = async () => {
|
||||
const input = (pluginType === "plugin" ? form?.input : form) || {};
|
||||
|
||||
for (let key in define.input) {
|
||||
const inWatches = props.watches.includes(key);
|
||||
const inWatches = props.watches?.includes(key);
|
||||
const inputDefine = define.input[key];
|
||||
if (inWatches && inputDefine.required) {
|
||||
const value = input[key];
|
||||
|
||||
@@ -105,7 +105,7 @@ const getOptions = async () => {
|
||||
const input = (pluginType === "plugin" ? form?.input : form) || {};
|
||||
|
||||
for (let key in define.input) {
|
||||
const inWatches = props.watches.includes(key);
|
||||
const inWatches = props.watches?.includes(key);
|
||||
const inputDefine = define.input[key];
|
||||
if (inWatches && inputDefine.required) {
|
||||
const value = input[key];
|
||||
@@ -169,7 +169,7 @@ const getOptions = async () => {
|
||||
};
|
||||
|
||||
const filterOption = (input: string, option: any) => {
|
||||
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0 || String(option.value).toLowerCase().indexOf(input.toLowerCase());
|
||||
return option.label.toLowerCase().includes(input.toLowerCase()) || String(option.value).toLowerCase().includes(input.toLowerCase());
|
||||
};
|
||||
|
||||
async function onClick() {
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<div class="remote-select">
|
||||
<div class="flex flex-row">
|
||||
<a-tree-select class="remote-tree-select-input" :tree-data="optionsRef" :value="value" tree-checkable allow-clear v-bind="attrs" @click="onClick" @update:value="emit('update:value', $event)"> </a-tree-select>
|
||||
<div class="ml-5">
|
||||
<fs-button :loading="loading" title="刷新选项" icon="ion:refresh-outline" @click="refreshOptions"></fs-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="helper" :class="{ error: hasError }">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
||||
import { defineComponent, inject, ref, useAttrs, watch, Ref } from "vue";
|
||||
import { PluginDefine } from "@certd/pipeline";
|
||||
|
||||
defineOptions({
|
||||
name: "RemoteTreeSelect",
|
||||
});
|
||||
|
||||
const props = defineProps<
|
||||
{
|
||||
watches: string[];
|
||||
search?: boolean;
|
||||
pager?: boolean;
|
||||
} & ComponentPropsType
|
||||
>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:value": any;
|
||||
}>();
|
||||
|
||||
const attrs = useAttrs();
|
||||
|
||||
const getCurrentPluginDefine: any = inject("getCurrentPluginDefine", () => {
|
||||
return {};
|
||||
});
|
||||
const getScope: any = inject("get:scope", () => {
|
||||
return {};
|
||||
});
|
||||
const getPluginType: any = inject("get:plugin:type", () => {
|
||||
return "plugin";
|
||||
});
|
||||
|
||||
const searchKeyRef = ref("");
|
||||
const optionsRef = ref([]);
|
||||
const message = ref("");
|
||||
const hasError = ref(false);
|
||||
const loading = ref(false);
|
||||
const pagerRef: Ref = ref({
|
||||
current: 1,
|
||||
});
|
||||
const getOptions = async () => {
|
||||
debugger;
|
||||
if (loading.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!getCurrentPluginDefine) {
|
||||
return;
|
||||
}
|
||||
|
||||
const define: PluginDefine = getCurrentPluginDefine()?.value;
|
||||
if (!define) {
|
||||
return;
|
||||
}
|
||||
const pluginType = getPluginType();
|
||||
const { form } = getScope();
|
||||
const input = (pluginType === "plugin" ? form?.input : form) || {};
|
||||
|
||||
for (let key in define.input) {
|
||||
const inWatches = props.watches?.includes(key);
|
||||
const inputDefine = define.input[key];
|
||||
if (inWatches && inputDefine.required) {
|
||||
const value = input[key];
|
||||
if (value == null || value === "") {
|
||||
console.log("remote-select required", key);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message.value = "";
|
||||
hasError.value = false;
|
||||
loading.value = true;
|
||||
const pageNo = pagerRef.value.pageNo;
|
||||
const pageSize = pagerRef.value.pageSize;
|
||||
try {
|
||||
const res = await doRequest(
|
||||
{
|
||||
type: pluginType,
|
||||
typeName: form.type,
|
||||
action: props.action,
|
||||
input,
|
||||
data: {
|
||||
searchKey: props.search ? searchKeyRef.value : "",
|
||||
pageNo,
|
||||
pageSize,
|
||||
},
|
||||
},
|
||||
{
|
||||
onError(err: any) {
|
||||
hasError.value = true;
|
||||
message.value = `获取选项出错:${err.message}`;
|
||||
},
|
||||
showErrorNotify: false,
|
||||
}
|
||||
);
|
||||
const list = res?.list || res || [];
|
||||
if (list.length > 0) {
|
||||
message.value = "获取数据成功,请从下拉框中选择";
|
||||
} else {
|
||||
message.value = "获取数据成功,没有数据";
|
||||
}
|
||||
optionsRef.value = list;
|
||||
pagerRef.value.total = list.length;
|
||||
if (props.pager) {
|
||||
if (res.pageNo != null) {
|
||||
pagerRef.value.pageNo = res.pageNo ?? 1;
|
||||
}
|
||||
if (res.pageSize != null) {
|
||||
pagerRef.value.pageSize = res.pageSize ?? 100;
|
||||
}
|
||||
if (res.total != null) {
|
||||
pagerRef.value.total = res.total ?? list.length;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
async function onClick() {
|
||||
if (optionsRef.value?.length === 0) {
|
||||
await refreshOptions();
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshOptions() {
|
||||
await getOptions();
|
||||
}
|
||||
|
||||
async function doSearch() {
|
||||
pagerRef.value.pageNo = 1;
|
||||
await refreshOptions();
|
||||
}
|
||||
|
||||
watch(
|
||||
() => {
|
||||
const pluginType = getPluginType();
|
||||
const { form, key } = getScope();
|
||||
const input = (pluginType === "plugin" ? form?.input : form) || {};
|
||||
const watches = {};
|
||||
for (const key of props.watches) {
|
||||
//@ts-ignore
|
||||
watches[key] = input[key];
|
||||
}
|
||||
return {
|
||||
form: watches,
|
||||
key,
|
||||
};
|
||||
},
|
||||
async (value, oldValue) => {
|
||||
const { form } = value;
|
||||
const oldForm: any = oldValue?.form;
|
||||
let changed = oldForm == null || optionsRef.value.length == 0;
|
||||
for (const key of props.watches) {
|
||||
//@ts-ignore
|
||||
if (oldForm && form[key] != oldForm[key]) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
await getOptions();
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
async function onPageChange(current: any) {
|
||||
await refreshOptions();
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less"></style>
|
||||
@@ -2,6 +2,7 @@ import SynologyIdDeviceGetter from "./synology/device-id-getter.vue";
|
||||
import RemoteAutoComplete from "./common/remote-auto-complete.vue";
|
||||
import RemoteSelect from "./common/remote-select.vue";
|
||||
import RemoteInput from "./common/remote-input.vue";
|
||||
import RemoteTreeSelect from "./common/remote-tree-select.vue";
|
||||
import CertDomainsGetter from "./common/cert-domains-getter.vue";
|
||||
import OutputSelector from "/@/components/plugins/common/output-selector/index.vue";
|
||||
import DnsProviderSelector from "/@/components/plugins/cert/dns-provider-selector/index.vue";
|
||||
@@ -24,6 +25,7 @@ export default {
|
||||
app.component("SynologyDeviceIdGetter", SynologyIdDeviceGetter);
|
||||
app.component("RemoteAutoComplete", RemoteAutoComplete);
|
||||
app.component("RemoteSelect", RemoteSelect);
|
||||
app.component("RemoteTreeSelect", RemoteTreeSelect);
|
||||
app.component("RemoteInput", RemoteInput);
|
||||
app.component("CertDomainsGetter", CertDomainsGetter);
|
||||
app.component("InputPassword", InputPassword);
|
||||
|
||||
@@ -21,6 +21,7 @@ export default {
|
||||
pipeline: "Pipeline",
|
||||
domain: "Domain",
|
||||
deployTimes: "Deployments",
|
||||
monitorCount: "DomainMonitors",
|
||||
duration: "Duration",
|
||||
price: "Price",
|
||||
paymentMethod: "Payment Method",
|
||||
@@ -218,6 +219,7 @@ export default {
|
||||
triggerCronHelper:
|
||||
"Click the button above to choose a daily execution time.\nIt is recommended to trigger once per day. The task will be skipped if the certificate has not expired and will not be executed repeatedly.",
|
||||
notificationTitle: "Failure Notification",
|
||||
notificationWhen: "Notification Timing",
|
||||
notificationHelper: "Get real-time alerts when the task fails",
|
||||
groupIdTitle: "Pipeline Group",
|
||||
},
|
||||
@@ -564,7 +566,7 @@ export default {
|
||||
ipv6Priority: "IPv6 Priority",
|
||||
dualStackNetworkHelper: "If IPv6 priority is selected, enable IPv6 in docker-compose.yaml",
|
||||
enableCommonCnameService: "Enable Public CNAME Service",
|
||||
commonCnameHelper: "Allow use of public CNAME service. If disabled and no <router-link to='/sys/cname/provider'>custom CNAME service</router-link> is set, CNAME proxy certificate application will not work.",
|
||||
commonCnameHelper: "Allow use of public CNAME service. If disabled and no <a href='#/sys/cname/provider'>custom CNAME service</a> is set, CNAME proxy certificate application will not work.",
|
||||
enableCommonSelfServicePasswordRetrieval: "Enable self-service password recovery",
|
||||
saveButton: "Save",
|
||||
stopSuccess: "Stopped successfully",
|
||||
@@ -587,6 +589,7 @@ export default {
|
||||
commFeature: "Commercial feature",
|
||||
smsProvider: "SMS provider",
|
||||
aliyunSms: "Aliyun SMS",
|
||||
tencentSms: "Tencent SMS",
|
||||
yfySms: "YFY SMS",
|
||||
smsTest: "SMS test",
|
||||
testMobilePlaceholder: "Enter test mobile number",
|
||||
|
||||
@@ -25,6 +25,7 @@ export default {
|
||||
pipeline: "流水线",
|
||||
domain: "域名",
|
||||
deployTimes: "部署次数",
|
||||
monitorCount: "域名监控数",
|
||||
duration: "时长",
|
||||
price: "价格",
|
||||
paymentMethod: "支付方式",
|
||||
@@ -223,6 +224,7 @@ export default {
|
||||
triggerCronTitle: "定时触发",
|
||||
triggerCronHelper: "点击上面的按钮,选择每天几点定时执行。\n建议设置为每天触发一次,证书未到期之前任务会跳过,不会重复执行",
|
||||
notificationTitle: "失败通知",
|
||||
notificationWhen: "通知时机",
|
||||
notificationHelper: "任务执行失败实时提醒",
|
||||
groupIdTitle: "流水线分组",
|
||||
},
|
||||
@@ -570,7 +572,7 @@ export default {
|
||||
ipv6Priority: "IPV6优先",
|
||||
dualStackNetworkHelper: "如果选择IPv6优先,需要在docker-compose.yaml中启用ipv6",
|
||||
enableCommonCnameService: "启用公共CNAME服务",
|
||||
commonCnameHelper: "是否可以使用公共CNAME服务,如果禁用,且没有设置<router-link to='/sys/cname/provider'>自定义CNAME服务</router-link>,则无法使用CNAME代理方式申请证书",
|
||||
commonCnameHelper: "是否可以使用公共CNAME服务,如果禁用,且没有设置<a href='#/sys/cname/provider'>自定义CNAME服务</a>,则无法使用CNAME代理方式申请证书",
|
||||
enableCommonSelfServicePasswordRetrieval: "启用自助找回密码",
|
||||
saveButton: "保存",
|
||||
stopSuccess: "停止成功",
|
||||
@@ -593,6 +595,7 @@ export default {
|
||||
commFeature: "商业版功能",
|
||||
smsProvider: "短信提供商",
|
||||
aliyunSms: "阿里云短信",
|
||||
tencentSms: "腾讯云短信",
|
||||
yfySms: "易发云短信",
|
||||
smsTest: "短信测试",
|
||||
testMobilePlaceholder: "输入测试手机号",
|
||||
|
||||
12
packages/ui/certd-client/src/plugin/directive/comm-show.ts
Normal file
12
packages/ui/certd-client/src/plugin/directive/comm-show.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { useSettingStore } from "/@/store/settings";
|
||||
|
||||
export default {
|
||||
mounted(el: any, binding: any, vnode: any) {
|
||||
const settingStore = useSettingStore();
|
||||
const isComm = settingStore.isComm;
|
||||
const { value } = binding;
|
||||
if ((value === false && isComm) || (value === true && !isComm)) {
|
||||
el.parentNode && el.parentNode.removeChild(el);
|
||||
}
|
||||
},
|
||||
};
|
||||
8
packages/ui/certd-client/src/plugin/directive/index.ts
Normal file
8
packages/ui/certd-client/src/plugin/directive/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import comm from "./comm-show.js";
|
||||
const install = function (app: any) {
|
||||
app.directive("comm", comm);
|
||||
};
|
||||
|
||||
export default {
|
||||
install,
|
||||
};
|
||||
@@ -4,9 +4,12 @@ import FastCrud from "./fast-crud";
|
||||
import permission from "./permission";
|
||||
import { App } from "vue";
|
||||
import "./validator/index.js";
|
||||
import directives from "./directive/index";
|
||||
|
||||
function install(app: App, options: any = {}) {
|
||||
app.use(FastCrud, options);
|
||||
app.use(permission);
|
||||
app.use(directives);
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -133,7 +133,7 @@ export const sysResources = [
|
||||
title: "certd.sysResources.sysPluginConfig",
|
||||
name: "SysPluginConfig",
|
||||
path: "/sys/plugin/config",
|
||||
component: "/sys/plugin/config.vue",
|
||||
component: "/sys/plugin/config-common.vue",
|
||||
meta: {
|
||||
show: () => {
|
||||
const settingStore = useSettingStore();
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { defineStore } from "pinia";
|
||||
import * as api from "./api.plugin";
|
||||
import { DynamicType, FormItemProps } from "@fast-crud/fast-crud";
|
||||
import { DynamicType, FormItemProps, useMerge } from "@fast-crud/fast-crud";
|
||||
import { i18n } from "/src/locales/i18n";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
interface PluginState {
|
||||
group?: PluginGroups;
|
||||
originGroup?: PluginGroups;
|
||||
}
|
||||
|
||||
export type PluginGroup = {
|
||||
@@ -32,14 +34,17 @@ export class PluginGroups {
|
||||
groups!: { [key: string]: PluginGroup };
|
||||
map!: { [key: string]: PluginDefine };
|
||||
t: any;
|
||||
constructor(groups: { [key: string]: PluginGroup }) {
|
||||
mergeSetting?: boolean;
|
||||
constructor(groups: { [key: string]: PluginGroup }, opts?: { mergeSetting?: boolean }) {
|
||||
this.groups = groups;
|
||||
this.t = i18n.global.t;
|
||||
this.mergeSetting = opts?.mergeSetting ?? false;
|
||||
this.initGroup(groups);
|
||||
this.initMap();
|
||||
}
|
||||
|
||||
private initGroup(groups: { [p: string]: PluginGroup }) {
|
||||
const { merge } = useMerge();
|
||||
const all: PluginGroup = {
|
||||
key: "all",
|
||||
title: this.t("certd.all"),
|
||||
@@ -48,6 +53,14 @@ export class PluginGroups {
|
||||
icon: "material-symbols:border-all-rounded",
|
||||
};
|
||||
for (const key in groups) {
|
||||
if (this.mergeSetting) {
|
||||
for (const plugin of groups[key].plugins) {
|
||||
if (plugin.sysSetting) {
|
||||
merge(plugin.input, plugin.sysSetting.metadata?.input || {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
all.plugins.push(...groups[key].plugins);
|
||||
}
|
||||
this.groups = {
|
||||
@@ -132,11 +145,15 @@ export const usePluginStore = defineStore({
|
||||
id: "app.plugin",
|
||||
state: (): PluginState => ({
|
||||
group: null,
|
||||
originGroup: null,
|
||||
}),
|
||||
actions: {
|
||||
async reload() {
|
||||
const groups = await api.GetGroups({});
|
||||
this.group = new PluginGroups(groups);
|
||||
this.group = new PluginGroups(groups, { mergeSetting: true });
|
||||
this.originGroup = new PluginGroups(cloneDeep(groups));
|
||||
console.log("group", this.group);
|
||||
console.log("originGroup", this.originGroup);
|
||||
},
|
||||
async init() {
|
||||
if (!this.group) {
|
||||
@@ -150,6 +167,7 @@ export const usePluginStore = defineStore({
|
||||
},
|
||||
async clear() {
|
||||
this.group = null;
|
||||
this.originGroup = null
|
||||
},
|
||||
async getList(): Promise<PluginDefine[]> {
|
||||
await this.init();
|
||||
@@ -159,6 +177,10 @@ export const usePluginStore = defineStore({
|
||||
await this.init();
|
||||
return this.group.get(name);
|
||||
},
|
||||
async getPluginDefineFromOrigin(name: string): Promise<PluginDefine> {
|
||||
await this.init();
|
||||
return this.originGroup.get(name);
|
||||
},
|
||||
async getPluginConfig(query: any) {
|
||||
return await api.GetPluginConfig(query);
|
||||
},
|
||||
|
||||
@@ -304,3 +304,11 @@ h6 {
|
||||
padding: 10px;
|
||||
color: #6e6e6e;
|
||||
}
|
||||
|
||||
.ant-modal-body{
|
||||
.fs-form-body{
|
||||
max-height: 66vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -84,6 +84,7 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
|
||||
component: {
|
||||
color: "auto",
|
||||
},
|
||||
order: -1,
|
||||
},
|
||||
form: {
|
||||
component: {
|
||||
|
||||
@@ -82,6 +82,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
},
|
||||
column: {
|
||||
width: 300,
|
||||
order: -11,
|
||||
},
|
||||
},
|
||||
from: {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="title">
|
||||
{{ t("certd.cnameRecord") }}
|
||||
<span class="sub">
|
||||
<a href="https://certd.docmirror.cn/guide/feature/cname/" target="_blank">
|
||||
<a v-comm="false" href="https://certd.docmirror.cn/guide/feature/cname/" target="_blank">
|
||||
{{ t("certd.cname_feature_guide") }}
|
||||
</a>
|
||||
</span>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<template #header>
|
||||
<div class="title">开放接口密钥管理</div>
|
||||
<div class="more">
|
||||
<a :href="OPEN_API_DOC" target="_blank">开放接口文档</a>
|
||||
<a v-comm="false" :href="OPEN_API_DOC" target="_blank">开放接口文档</a>
|
||||
</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
|
||||
@@ -22,7 +22,7 @@ export function fillPipelineByDefaultForm(pipeline: any, form: any) {
|
||||
if (form.notification != null) {
|
||||
notifications.push({
|
||||
type: "custom",
|
||||
when: ["error", "turnToSuccess", "success"],
|
||||
when: form.notificationWhen || ["error", "turnToSuccess"],
|
||||
notificationId: form.notification,
|
||||
title: form.notificationTarget?.name || "自定义通知",
|
||||
});
|
||||
@@ -138,6 +138,7 @@ export function useCertPipelineCreator() {
|
||||
form: {
|
||||
doSubmit,
|
||||
wrapper: {
|
||||
wrapClassName: "cert_pipeline_create_form",
|
||||
width: 1350,
|
||||
saveRemind: false,
|
||||
title: t("certd.pipelineForm.createTitle"),
|
||||
@@ -222,6 +223,25 @@ export function useCertPipelineCreator() {
|
||||
helper: t("certd.pipelineForm.notificationHelper"),
|
||||
},
|
||||
},
|
||||
notificationWhen: {
|
||||
title: t("certd.pipelineForm.notificationWhen"),
|
||||
type: "text",
|
||||
form: {
|
||||
value: ["error", "turnToSuccess"],
|
||||
component: {
|
||||
name: "a-select",
|
||||
vModel: "value",
|
||||
mode: "multiple",
|
||||
options: [
|
||||
{ value: "start", label: t("certd.start_time") },
|
||||
{ value: "success", label: t("certd.success_time") },
|
||||
{ value: "turnToSuccess", label: t("certd.fail_to_success_time") },
|
||||
{ value: "error", label: t("certd.fail_time") },
|
||||
],
|
||||
},
|
||||
order: 102,
|
||||
},
|
||||
},
|
||||
groupId: {
|
||||
title: t("certd.pipelineForm.groupIdTitle"),
|
||||
type: "dict-select",
|
||||
@@ -267,7 +287,7 @@ export function useCertPipelineCreator() {
|
||||
async function doSubmit({ form }: any) {
|
||||
// const certDetail = readCertDetail(form.cert.crt);
|
||||
// 添加certd pipeline
|
||||
const pluginInput = omit(form, ["triggerCron", "notification", "notificationTarget", "certApplyPlugin", "groupId"]);
|
||||
const pluginInput = omit(form, ["triggerCron", "notification", "notificationTarget", "notificationWhen", "certApplyPlugin", "groupId"]);
|
||||
let pipeline: any = {
|
||||
title: form.domains[0] + "证书自动化",
|
||||
runnableType: "pipeline",
|
||||
|
||||
@@ -115,4 +115,13 @@ function batchRerun() {
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.cert_pipeline_create_form {
|
||||
.ant-collapse {
|
||||
margin: 10px;
|
||||
}
|
||||
.ant-collapse-header {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -313,6 +313,8 @@ function useStepForm() {
|
||||
};
|
||||
|
||||
const stepDelete = () => {
|
||||
//检查输出依赖
|
||||
|
||||
Modal.confirm({
|
||||
title: "确认",
|
||||
content: `确定要删除此步骤吗?`,
|
||||
|
||||
@@ -279,7 +279,7 @@
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="tsx">
|
||||
import { computed, defineComponent, onMounted, onUnmounted, provide, ref, Ref, watch } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import PiTaskForm from "./component/task-form/index.vue";
|
||||
@@ -758,9 +758,12 @@ export default defineComponent({
|
||||
|
||||
//检查输出的stepid是否存在
|
||||
let hasError = false;
|
||||
let errorMessage = "";
|
||||
let errorMessages: any = [];
|
||||
let errorIndex = 1;
|
||||
eachSteps(pp, (step: any, task: any, stage: any) => {
|
||||
stepIds.push(step.id);
|
||||
if (step.disabled !== true) {
|
||||
stepIds.push(step.id);
|
||||
}
|
||||
if (step.input) {
|
||||
for (const key in step.input) {
|
||||
const value = step.input[key];
|
||||
@@ -775,21 +778,36 @@ export default defineComponent({
|
||||
const paramName = arr[2];
|
||||
if (!stepIds.includes(stepId)) {
|
||||
hasError = true;
|
||||
const message = `任务${step.title}的前置输出步骤${paramName}不存在,请重新修改此任务`;
|
||||
const message = `${step.title}的前置输出步骤${paramName}不存在或已被禁用`;
|
||||
errorIndex++;
|
||||
addValidateError(task.id, {
|
||||
message,
|
||||
});
|
||||
addValidateError(step.id, {
|
||||
message,
|
||||
});
|
||||
errorMessage += message + ";";
|
||||
errorMessages.push(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (hasError) {
|
||||
notification.error({ message: errorMessage });
|
||||
notification.error({
|
||||
message: () => {
|
||||
const nodes = [];
|
||||
let i = 0;
|
||||
for (const error of errorMessages) {
|
||||
i++;
|
||||
nodes.push(
|
||||
<div>
|
||||
{i}.{error}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return nodes;
|
||||
},
|
||||
});
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,14 @@
|
||||
<div class="title">
|
||||
{{ t("certd.subdomainHosting") }}
|
||||
<span class="sub">
|
||||
{{ t("certd.subdomainHostingHint") }}; {{ t("certd.subdomainHelpText") }}
|
||||
<a href="https://help.aliyun.com/zh/dns/subdomain-management" target="_blank">
|
||||
{{ t("certd.subdomainManagement") }}
|
||||
</a>
|
||||
{{ t("certd.subdomainHostingHint") }};
|
||||
|
||||
<span v-comm="false">
|
||||
{{ t("certd.subdomainHelpText") }}
|
||||
<a href="https://certd.docmirror.cn/guide/use/cert/subdomain.html" target="_blank">
|
||||
{{ t("certd.subdomainManagement") }}
|
||||
</a>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<a-modal v-model:open="openRef" class="order-modal" :title="$t('certd.order.confirmTitle')" @ok="orderCreate">
|
||||
<a-modal v-model:open="openRef" class="order-modal" :title="$t('certd.order.confirmTitle')" :width="670" @ok="orderCreate">
|
||||
<div v-if="product" class="order-box">
|
||||
<div class="flex-o mt-5">
|
||||
<span class="label">{{ $t("certd.order.package") }}:</span>{{ product.title }}
|
||||
@@ -13,6 +13,7 @@
|
||||
<span class="flex-o"> {{ $t("certd.order.pipeline") }}<suite-value class="ml-5" :model-value="product.content.maxPipelineCount" :unit="$t('certd.order.unit.pieces')" />; </span>
|
||||
<span class="flex-o"> {{ $t("certd.order.domain") }}<suite-value class="ml-5" :model-value="product.content.maxDomainCount" :unit="$t('certd.order.unit.count')" />; </span>
|
||||
<span class="flex-o"> {{ $t("certd.order.deployTimes") }}<suite-value class="ml-5" :model-value="product.content.maxDeployCount" :unit="$t('certd.order.unit.times')" />; </span>
|
||||
<span class="flex-o"> {{ $t("certd.order.monitorCount") }}<suite-value class="ml-5" :model-value="product.content.maxMonitorCount" :unit="$t('certd.order.unit.times')" />; </span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
@finish="handleFinish"
|
||||
@finish-failed="handleFinishFailed"
|
||||
>
|
||||
<a-tabs v-model:active-key="forgotPasswordType" :destroyInactiveTabPane="true">
|
||||
<a-tabs v-model:active-key="forgotPasswordType" :destroy-inactive-tab-pane="true">
|
||||
<a-tab-pane key="email" tab="邮箱找回">
|
||||
<a-form-item has-feedback name="input" label="邮箱">
|
||||
<a-input v-model:value="formState.input" placeholder="邮箱" size="large" autocomplete="off">
|
||||
@@ -21,13 +21,7 @@
|
||||
</a-input>
|
||||
</a-form-item>
|
||||
<a-form-item has-feedback name="validateCode" label="邮件验证码">
|
||||
<email-code
|
||||
v-model:value="formState.validateCode"
|
||||
:img-code="formState.imgCode"
|
||||
:email="formState.input"
|
||||
:random-str="formState.randomStr"
|
||||
verification-type="forgotPassword"
|
||||
/>
|
||||
<email-code v-model:value="formState.validateCode" :img-code="formState.imgCode" :email="formState.input" :random-str="formState.randomStr" verification-type="forgotPassword" />
|
||||
</a-form-item>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="mobile" tab="手机号找回">
|
||||
@@ -72,7 +66,7 @@
|
||||
<a-form-item>
|
||||
<a-button type="primary" size="large" html-type="submit" class="submit-button"> 找回密码</a-button>
|
||||
|
||||
<div class="mt-2">
|
||||
<div v-comm="false" class="mt-2">
|
||||
<a href="https://certd.docmirror.cn/guide/use/forgotpasswd/" target="_blank"> 管理员无绑定通信方式或MFA丢失找回 </a>
|
||||
</div>
|
||||
</a-form-item>
|
||||
@@ -87,7 +81,7 @@ import EmailCode from "/@/views/framework/register/email-code.vue";
|
||||
import SmsCode from "/@/views/framework/login/sms-code.vue";
|
||||
import { utils } from "@fast-crud/fast-crud";
|
||||
import { useUserStore } from "/@/store/user";
|
||||
|
||||
import { useSettingStore } from "/@/store/settings";
|
||||
defineOptions({
|
||||
name: "ForgotPasswordPage",
|
||||
});
|
||||
@@ -123,6 +117,7 @@ const layout = {
|
||||
|
||||
const forgotPasswordType = ref();
|
||||
const userStore = useUserStore();
|
||||
const settingStore = useSettingStore();
|
||||
const formRef = ref();
|
||||
const imageCodeRef = ref();
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<a-form-item>
|
||||
<a-button type="primary" size="large" html-type="submit" :loading="loading" class="login-button">
|
||||
<a-button type="primary" size="large" html-type="button" :loading="loading" class="login-button" @click="handleFinish">
|
||||
{{ t("authentication.loginButton") }}
|
||||
</a-button>
|
||||
|
||||
@@ -217,7 +217,6 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
.login-page.main {
|
||||
//margin: 20px !important;
|
||||
margin-bottom: 100px;
|
||||
|
||||
@@ -97,6 +97,7 @@ export type CertApplyPluginSysInput = {
|
||||
export type PluginSysSetting<T> = {
|
||||
sysSetting: {
|
||||
input?: T;
|
||||
metadata?: Record<string, any>;
|
||||
};
|
||||
};
|
||||
export type CommPluginConfig = {
|
||||
@@ -118,6 +119,14 @@ export async function SaveCommPluginConfigs(data: CommPluginConfig): Promise<voi
|
||||
});
|
||||
}
|
||||
|
||||
export async function savePluginSetting(req: { name: string; sysSetting: any }): Promise<void> {
|
||||
return await request({
|
||||
url: apiPrefix + "/saveSetting",
|
||||
method: "post",
|
||||
data: req,
|
||||
});
|
||||
}
|
||||
|
||||
export async function DoTest(req: { id: number; input: any }): Promise<void> {
|
||||
return await request({
|
||||
url: apiPrefix + "/doTest",
|
||||
|
||||
184
packages/ui/certd-client/src/views/sys/plugin/config-editor.vue
Normal file
184
packages/ui/certd-client/src/views/sys/plugin/config-editor.vue
Normal file
@@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<div class="plugin-config">
|
||||
<div class="origin-metadata w-100%">
|
||||
<div class="block-title">
|
||||
自定义插件参数配置
|
||||
<div class="helper">可以设置插件选项的配置,设置配置默认值、修改帮助说明、设置是否显示该字段等</div>
|
||||
</div>
|
||||
<div class="p-10">
|
||||
<div ref="formRef" class="config-form w-full" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<table class="table-fixed w-full">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left p-5" width="200px">插件参数</th>
|
||||
<th class="text-left p-5" width="100px">参数配置</th>
|
||||
<th class="text-left flex-1 p-5">自定义</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<template v-for="item in originInputs" :key="item.key">
|
||||
<template v-for="prop in editableKeys" :key="prop.key">
|
||||
<tr>
|
||||
<td v-if="prop.key === 'value'" class="border-t-2 p-5" rowspan="3" :class="{ 'border-t-2': prop.key === 'value' }">{{ item.title }}</td>
|
||||
<td class="border-t p-5" :class="{ 'border-t-2': prop.key === 'value' }">{{ prop.label }}</td>
|
||||
<td class="border-t p-5" :class="{ 'border-t-2': prop.key === 'value' }">
|
||||
<rollbackable :value="configForm[item.key][prop.key]" @set="prop.onSet(item)" @clear="delete configForm[item.key][prop.key]">
|
||||
<template #default>
|
||||
<fs-render :render-func="prop.defaultRender(item)"></fs-render>
|
||||
</template>
|
||||
<template #edit>
|
||||
<fs-render :render-func="prop.editRender(item)"></fs-render>
|
||||
</template>
|
||||
</rollbackable>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import { computed, nextTick, onMounted, reactive, ref, Ref, unref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import * as api from "./api";
|
||||
import { usePluginStore } from "/@/store/plugin";
|
||||
import { cloneDeep, get, merge, set, unset } from "lodash-es";
|
||||
import Rollbackable from "./rollbackable.vue";
|
||||
import { FsRender } from "@fast-crud/fast-crud";
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const pluginStore = usePluginStore();
|
||||
const props = defineProps<{
|
||||
plugin: any;
|
||||
}>();
|
||||
|
||||
const pluginMetadata = ref<any>("");
|
||||
const currentPlugin = ref();
|
||||
const labelCol = ref({
|
||||
span: null,
|
||||
style: {
|
||||
width: "145px",
|
||||
},
|
||||
});
|
||||
const wrapperCol = ref({ span: 16 });
|
||||
const configForm: any = reactive({});
|
||||
|
||||
function getScope() {
|
||||
return {
|
||||
form: configForm,
|
||||
};
|
||||
}
|
||||
|
||||
function getForm() {
|
||||
return configForm;
|
||||
}
|
||||
|
||||
const editableKeys = ref([
|
||||
{
|
||||
key: "value",
|
||||
label: "默认值",
|
||||
onSet(item: any) {
|
||||
configForm[item.key]["value"] = item.value ?? null;
|
||||
},
|
||||
defaultRender(item: any) {
|
||||
return () => {
|
||||
return item["value"] ?? "";
|
||||
};
|
||||
},
|
||||
editRender(item: any) {
|
||||
return () => {
|
||||
return <fs-component-render {...item.component} vModel:modelValue={configForm[item.key]["value"]} scope={getScope()} />;
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "show",
|
||||
label: "是否显示",
|
||||
onSet(item: any) {
|
||||
configForm[item.key]["show"] = item.show ?? true;
|
||||
},
|
||||
defaultRender(item: any) {
|
||||
return () => {
|
||||
const value = item["show"];
|
||||
return value === false ? "不显示" : "显示";
|
||||
};
|
||||
},
|
||||
editRender(item: any) {
|
||||
return () => {
|
||||
return <a-switch vModel:checked={configForm[item.key]["show"]} />;
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "helper",
|
||||
label: "帮助说明",
|
||||
onSet(item: any) {
|
||||
configForm[item.key]["helper"] = item.helper ?? "";
|
||||
},
|
||||
defaultRender(item: any) {
|
||||
return () => {
|
||||
return <pre class={"helper"}>{item["helper"]}</pre>;
|
||||
};
|
||||
},
|
||||
editRender(item: any) {
|
||||
return () => {
|
||||
return <a-textarea rows={5} vModel:value={configForm[item.key]["helper"]} />;
|
||||
};
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const originInputs = computed(() => {
|
||||
if (!currentPlugin.value) {
|
||||
return;
|
||||
}
|
||||
const input = cloneDeep(currentPlugin.value.input);
|
||||
const newInputs: any = {};
|
||||
|
||||
for (const key in input) {
|
||||
const value = input[key];
|
||||
value.key = key;
|
||||
const newInput: any = cloneDeep(value);
|
||||
newInputs[key] = newInput;
|
||||
}
|
||||
return newInputs;
|
||||
});
|
||||
|
||||
function clearFormValue(key: string) {
|
||||
unset(configForm, key);
|
||||
console.log(key, configForm);
|
||||
}
|
||||
|
||||
async function loadPluginSetting() {
|
||||
currentPlugin.value = await pluginStore.getPluginDefineFromOrigin(props.plugin.name);
|
||||
for (const key in currentPlugin.value.input) {
|
||||
configForm[key] = {};
|
||||
}
|
||||
const setting = props.plugin.sysSetting;
|
||||
if (setting) {
|
||||
const settingJson = JSON.parse(setting);
|
||||
merge(configForm, settingJson.metadata?.input || {});
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadPluginSetting();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
getForm,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.plugin-config {
|
||||
pre {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,11 +1,14 @@
|
||||
import * as api from "./api";
|
||||
import { useI18n } from "/src/locales";
|
||||
import { Ref, ref } from "vue";
|
||||
import { Ref, ref, computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, useFormWrapper, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
import { Modal, notification } from "ant-design-vue";
|
||||
import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
import { Modal } from "ant-design-vue";
|
||||
//@ts-ignore
|
||||
import yaml from "js-yaml";
|
||||
import { usePluginImport } from "./use-import";
|
||||
import { usePluginConfig } from "./use-config";
|
||||
import { useSettingStore } from "/src/store/settings/index";
|
||||
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const router = useRouter();
|
||||
@@ -35,75 +38,11 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
|
||||
const selectedRowKeys: Ref<any[]> = ref([]);
|
||||
context.selectedRowKeys = selectedRowKeys;
|
||||
const { openCrudFormDialog } = useFormWrapper();
|
||||
|
||||
async function openImportDialog() {
|
||||
function createCrudOptions() {
|
||||
return {
|
||||
crudOptions: {
|
||||
columns: {
|
||||
content: {
|
||||
title: t("certd.pluginFile"),
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
name: "pem-input",
|
||||
vModel: "modelValue",
|
||||
textarea: {
|
||||
rows: 8,
|
||||
},
|
||||
},
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
helper: t("certd.selectPluginFile"),
|
||||
},
|
||||
},
|
||||
override: {
|
||||
title: t("certd.overrideSameName"),
|
||||
type: "dict-switch",
|
||||
dict: dict({
|
||||
data: [
|
||||
{
|
||||
value: true,
|
||||
label: t("certd.override"),
|
||||
},
|
||||
{
|
||||
value: false,
|
||||
label: t("certd.noOverride"),
|
||||
},
|
||||
],
|
||||
}),
|
||||
form: {
|
||||
value: false,
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
helper: t("certd.overrideHelper"),
|
||||
},
|
||||
},
|
||||
},
|
||||
form: {
|
||||
wrapper: {
|
||||
title: t("certd.importPlugin"),
|
||||
saveRemind: false,
|
||||
},
|
||||
afterSubmit() {
|
||||
notification.success({ message: t("certd.operationSuccess") });
|
||||
crudExpose.doRefresh();
|
||||
},
|
||||
async doSubmit({ form }: any) {
|
||||
return await api.ImportPlugin({
|
||||
...form,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
const { crudOptions } = createCrudOptions();
|
||||
await openCrudFormDialog({ crudOptions });
|
||||
}
|
||||
const { openImportDialog } = usePluginImport();
|
||||
const { openConfigDialog } = usePluginConfig();
|
||||
|
||||
const settingStore = useSettingStore();
|
||||
return {
|
||||
crudOptions: {
|
||||
settings: {
|
||||
@@ -139,7 +78,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
text: t("certd.import"),
|
||||
type: "primary",
|
||||
async click() {
|
||||
await openImportDialog();
|
||||
await openImportDialog({ crudExpose });
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -186,6 +125,21 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
}
|
||||
},
|
||||
},
|
||||
config: {
|
||||
show: computed(() => {
|
||||
return settingStore.isComm;
|
||||
}),
|
||||
text: null,
|
||||
icon: "ion:settings-outline",
|
||||
title: t("certd.config"),
|
||||
type: "link",
|
||||
async click({ row }) {
|
||||
await openConfigDialog({
|
||||
row,
|
||||
crudExpose,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
table: {
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import { defineProps } from "vue";
|
||||
|
||||
const props = defineProps<{ value: any }>();
|
||||
|
||||
const emits = defineEmits(["set", "clear"]);
|
||||
function setValue() {
|
||||
emits("set");
|
||||
}
|
||||
function clearValue() {
|
||||
emits("clear");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="rollbackable">
|
||||
<div class="flex">
|
||||
<div style="width: 100px">
|
||||
<a-tag v-if="value === undefined" color="green" size="small" class="pointer flex-inline items-center" @click.stop="setValue">
|
||||
<fs-icon icon="material-symbols:edit" class="mr-5"></fs-icon>
|
||||
自定义
|
||||
</a-tag>
|
||||
<a-tag v-else color="red" size="small" class="pointer flex-inline items-center" @click.stop="clearValue">
|
||||
<fs-icon icon="material-symbols:undo" class="mr-5"></fs-icon>
|
||||
还原
|
||||
</a-tag>
|
||||
</div>
|
||||
<div class="flex-1 overflow-hidden value-render">
|
||||
<slot v-if="value === undefined" name="default"></slot>
|
||||
<slot v-else name="edit"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
.rollbackable {
|
||||
.value-render {
|
||||
.ant-select,
|
||||
.ant-input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
79
packages/ui/certd-client/src/views/sys/plugin/use-config.tsx
Normal file
79
packages/ui/certd-client/src/views/sys/plugin/use-config.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import * as api from "/@/views/sys/plugin/api";
|
||||
import { useFormWrapper } from "@fast-crud/fast-crud";
|
||||
import { useI18n } from "/@/locales";
|
||||
import { notification } from "ant-design-vue";
|
||||
import ConfigEditor from "./config-editor.vue";
|
||||
import { ref } from "vue";
|
||||
import { usePluginStore } from "/@/store/plugin";
|
||||
|
||||
export function usePluginConfig() {
|
||||
const { openCrudFormDialog } = useFormWrapper();
|
||||
const { t } = useI18n();
|
||||
|
||||
const pluginStore = usePluginStore();
|
||||
// @ts-ignore
|
||||
async function openConfigDialog({ row, crudExpose }) {
|
||||
const configEditorRef = ref();
|
||||
function createCrudOptions() {
|
||||
return {
|
||||
crudOptions: {
|
||||
columns: {},
|
||||
form: {
|
||||
wrapper: {
|
||||
width: "80%",
|
||||
title: "插件参数自定义",
|
||||
saveRemind: false,
|
||||
slots: {
|
||||
"form-body-top": () => {
|
||||
return (
|
||||
<div>
|
||||
<ConfigEditor ref={configEditorRef} plugin={row}></ConfigEditor>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
afterSubmit() {
|
||||
notification.success({ message: t("certd.operationSuccess") });
|
||||
crudExpose.doRefresh();
|
||||
},
|
||||
async doSubmit({}: any) {
|
||||
const form = configEditorRef.value.getForm();
|
||||
const newForm: any = {};
|
||||
for (const key in form) {
|
||||
const value = form[key];
|
||||
if (value && Object.keys(value).length > 0) {
|
||||
newForm[key] = value;
|
||||
}
|
||||
}
|
||||
const res = await api.savePluginSetting({
|
||||
name: row.name,
|
||||
sysSetting: {
|
||||
metadata: {
|
||||
input: newForm,
|
||||
},
|
||||
},
|
||||
});
|
||||
await pluginStore.clear();
|
||||
return res;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
const { crudOptions } = createCrudOptions();
|
||||
await openCrudFormDialog({ crudOptions });
|
||||
|
||||
// modal.confirm({
|
||||
// title: "插件元数据配置",
|
||||
// width: "80%",
|
||||
// content: () => {
|
||||
// return <ConfigEditor plugin={row}></ConfigEditor>;
|
||||
// },
|
||||
// });
|
||||
}
|
||||
|
||||
return {
|
||||
openConfigDialog,
|
||||
};
|
||||
}
|
||||
80
packages/ui/certd-client/src/views/sys/plugin/use-import.ts
Normal file
80
packages/ui/certd-client/src/views/sys/plugin/use-import.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import * as api from "/@/views/sys/plugin/api";
|
||||
import { useFormWrapper } from "@fast-crud/fast-crud";
|
||||
import { useI18n } from "/@/locales";
|
||||
import { Modal, notification } from "ant-design-vue";
|
||||
export function usePluginImport() {
|
||||
const { openCrudFormDialog } = useFormWrapper();
|
||||
const { t } = useI18n();
|
||||
|
||||
async function openImportDialog({ crudExpose }) {
|
||||
function createCrudOptions() {
|
||||
return {
|
||||
crudOptions: {
|
||||
columns: {
|
||||
content: {
|
||||
title: t("certd.pluginFile"),
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
name: "pem-input",
|
||||
vModel: "modelValue",
|
||||
textarea: {
|
||||
rows: 8,
|
||||
},
|
||||
},
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
helper: t("certd.selectPluginFile"),
|
||||
},
|
||||
},
|
||||
override: {
|
||||
title: t("certd.overrideSameName"),
|
||||
type: "dict-switch",
|
||||
dict: dict({
|
||||
data: [
|
||||
{
|
||||
value: true,
|
||||
label: t("certd.override"),
|
||||
},
|
||||
{
|
||||
value: false,
|
||||
label: t("certd.noOverride"),
|
||||
},
|
||||
],
|
||||
}),
|
||||
form: {
|
||||
value: false,
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
helper: t("certd.overrideHelper"),
|
||||
},
|
||||
},
|
||||
},
|
||||
form: {
|
||||
wrapper: {
|
||||
title: t("certd.importPlugin"),
|
||||
saveRemind: false,
|
||||
},
|
||||
afterSubmit() {
|
||||
notification.success({ message: t("certd.operationSuccess") });
|
||||
crudExpose.doRefresh();
|
||||
},
|
||||
async doSubmit({ form }: any) {
|
||||
return await api.ImportPlugin({
|
||||
...form,
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
const { crudOptions } = createCrudOptions();
|
||||
await openCrudFormDialog({ crudOptions });
|
||||
}
|
||||
|
||||
return {
|
||||
openImportDialog,
|
||||
};
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
<a-form-item v-if="formState.yizhifu.enabled" label="易支付配置" :name="['yizhifu', 'accessId']" :required="true">
|
||||
<access-selector v-model="formState.yizhifu.accessId" type="yizhifu" from="sys" />
|
||||
<div class="helper">
|
||||
<a href="https://certd.docmirror.cn/comm/payments/yizhifu.html">彩虹易支付配置帮助文档</a>
|
||||
<a href="https://certd.docmirror.cn/guide/use/comm/payments/yizhifu.html">彩虹易支付配置帮助文档</a>
|
||||
</div>
|
||||
</a-form-item>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</a-form-item>
|
||||
<a-form-item v-if="formState.alipay.enabled" label="支付宝配置" :name="['alipay', 'accessId']" :required="true">
|
||||
<access-selector v-model="formState.alipay.accessId" type="alipay" from="sys" />
|
||||
<div class="helper">需要开通电脑网站支付, <a href="https://certd.docmirror.cn/comm/payments/alipay.html">支付宝配置帮助文档</a></div>
|
||||
<div class="helper">需要开通电脑网站支付, <a href="https://certd.docmirror.cn/guide/use/comm/payments/alipay.html">支付宝配置帮助文档</a></div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="微信支付" :name="['wxpay', 'enabled']" :required="true">
|
||||
@@ -25,7 +25,7 @@
|
||||
</a-form-item>
|
||||
<a-form-item v-if="formState.wxpay.enabled" label="微信支付配置" :name="['wxpay', 'accessId']" :required="true">
|
||||
<access-selector v-model="formState.wxpay.accessId" type="wxpay" from="sys" />
|
||||
<div class="helper">需要开通Native支付, <a href="https://certd.docmirror.cn/comm/payments/wxpay.html">微信配置帮助文档</a></div>
|
||||
<div class="helper">需要开通Native支付, <a href="https://certd.docmirror.cn/guide/use/comm/payments/wxpay.html">微信配置帮助文档</a></div>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label=" " :colon="false" :wrapper-col="{ span: 16 }">
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
<a-form-item :label="t('certd.smsProvider')" :name="['private', 'sms', 'type']">
|
||||
<a-select v-model:value="formState.private.sms.type" @change="smsTypeChange">
|
||||
<a-select-option value="aliyun">{{ t("certd.aliyunSms") }}</a-select-option>
|
||||
<a-select-option value="tencent">{{ t("certd.tencentSms") }}</a-select-option>
|
||||
<a-select-option value="yfysms">{{ t("certd.yfySms") }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
15
packages/ui/certd-server/.env.dev-mysql-comm.yaml
Normal file
15
packages/ui/certd-server/.env.dev-mysql-comm.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
flyway:
|
||||
scriptDir: './db/migration-mysql'
|
||||
|
||||
typeorm:
|
||||
dataSource:
|
||||
default:
|
||||
type: mysql # mariadb
|
||||
host: localhost
|
||||
port: 3309
|
||||
logging: true
|
||||
username: root
|
||||
password: root
|
||||
database: certd_comm
|
||||
|
||||
|
||||
@@ -3,6 +3,53 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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))
|
||||
* 修复mysql下购买套餐加量包无效的bug ([c26ad4c](https://github.com/certd/certd/commit/c26ad4c8075f0606d45b8da13915737968d6191a))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 增加健康检查探针 /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))
|
||||
|
||||
## [1.36.18](https://github.com/certd/certd/compare/v1.36.17...v1.36.18) (2025-08-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 更新我爱云CDN域名地址,和部分目录结构 [@tyjsjxh](https://github.com/tyjsjxh) ([#514](https://github.com/certd/certd/issues/514)) ([78e7a81](https://github.com/certd/certd/commit/78e7a81638c2ee779f0ab6c3ba7e5c6f6e064151))
|
||||
* 修复proxmox某些情况下执行卡住的bug ([ebd6917](https://github.com/certd/certd/commit/ebd6917a1d40ae4d94555c32b7e3c093d0599b94))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 部署到k8s支持自动创建secret ([c09c962](https://github.com/certd/certd/commit/c09c962cb676ca261610aa9f3e5105c9dae43f43))
|
||||
* 短信验证码支持腾讯云 ([9108459](https://github.com/certd/certd/commit/9108459ae42bcd95a59acba164a64e82e5f2cfe6))
|
||||
* 商业版支持自定义插件的参数配置 ([17f23f3](https://github.com/certd/certd/commit/17f23f37516af925d5049291d67d41e4271f81f8))
|
||||
* 腾讯云插件支持国际版 ([58e82d5](https://github.com/certd/certd/commit/58e82d5dbd4ebf089ef239578ef9b68454d17b30))
|
||||
* 腾讯云EO插件支持自动获取zoneid和域名列表 ([70fcdc9](https://github.com/certd/certd/commit/70fcdc9ebbfb7c883c0c8a2138f61a0776a9491b))
|
||||
* 支持部署到阿里云云原生API网关、AI网关 ([2ca20be](https://github.com/certd/certd/commit/2ca20be197720201fceabcce9d927f4dbc1cc872))
|
||||
* 支持部署到华为云obs ([9feb9d0](https://github.com/certd/certd/commit/9feb9d04b3c56ec95c06fcf4fd071eb0e88ffc6f))
|
||||
* 支持部署到dokploy ([7dbdeae](https://github.com/certd/certd/commit/7dbdeaebe0bfee7521a863fe5e6b4a712aec5876))
|
||||
* 支持删除宝塔证书夹中的过期证书 ([3575113](https://github.com/certd/certd/commit/3575113655be751d19f88c64491e98a89042d6a2))
|
||||
* 支持p7b证书格式 ([d9f4a57](https://github.com/certd/certd/commit/d9f4a5793d68a017a5d80ad5385cbda603c4e165))
|
||||
* lecdnv2支持api token ([e448934](https://github.com/certd/certd/commit/e4489343fee7754be07bcfc3323969dc3a30e90c))
|
||||
* openapi返回证书时挑选匹配范围最小的那一个;增加format参数,增加返回值p7b格式,增加detail返回 ([2085bcc](https://github.com/certd/certd/commit/2085bcceb61c3723c9bdfec4c4cc0917631ff5e5))
|
||||
|
||||
## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复新部署的无法保存公共eab配置的bug ([d5dee75](https://github.com/certd/certd/commit/d5dee75df3bd635a597436e448b2de1407531f3a))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 阿里云 FC3.0 不在要求证书加密方式为旧版, 修复支持的协议类型可以正常选择 ([a34db74](https://github.com/certd/certd/commit/a34db7449eff6ad1dda01de673bf85579fa3865a))
|
||||
* 部署到腾讯云cdn,每个域名增加3每秒延迟 ([f7d43ad](https://github.com/certd/certd/commit/f7d43ad5af4663d4be369820a80d1fd9817ca4ab))
|
||||
|
||||
## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
```
|
||||
|
||||
|
||||
```shell
|
||||
npm run heap
|
||||
```
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
update cd_user_suite set is_empty = false where is_empty is null;
|
||||
|
||||
ALTER TABLE cd_user_suite MODIFY COLUMN `is_empty` boolean default false ;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user