Merge branch 'v2-dev' into v2

This commit is contained in:
xiaojunnuo
2025-05-11 20:29:59 +08:00
87 changed files with 1157 additions and 301 deletions

View File

@@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
### Bug Fixes
* 修复部署到又拍云强制https无效的bug ([2397097](https://github.com/certd/certd/commit/2397097e4ddcb6f593210598e8779ffd44ac3f8f))
* 修复刷新流水线页面后日志不自动更新的bug ([0b2e28b](https://github.com/certd/certd/commit/0b2e28b62dd5eb6804c602083e65c87a9d1d72d2))
### Performance Improvements
* 集成智能问答机器人 ([9dd4905](https://github.com/certd/certd/commit/9dd49054d18ec436a5029444ca55a38adc682933))
* 支持设置网安备案号 ([d18e431](https://github.com/certd/certd/commit/d18e431e2f08e6b37704032c4ea6fbdd8e971442))
* http方式支持校验443端口 ([d75fcb7](https://github.com/certd/certd/commit/d75fcb7fec421a9a638eaa27fe9378c84b5e0f19))
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
### Bug Fixes

View File

@@ -5,28 +5,35 @@ Certd 是一个免费全自动申请和自动部署更新SSL证书的管理系
关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具
> 关于证书续期:
>* 实际上没有办法不改变证书文件本身情况下直接续期或者续签。
>* 我们所说的续期,其实就是按照全套流程重新申请一份新证书,然后重新部署上去。
>* 免费证书过期时间90天以后可能还会缩短所以自动化部署必不可少
> 流水线数量现已调整为无限制,欢迎大家使用
## 一、特性
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
* 全自动申请证书(支持所有注册商注册的域名)
* 全自动申请证书(支持所有注册商注册的域名支持DNS-01、HTTP-01、CNAME代理等多种域名验证方式
* 全自动部署更新证书目前支持部署到主机、阿里云、腾讯云等70+部署插件)
* 支持DNS-01、HTTP-01、CNAME代理等多种域名验证方式
* 支持通配符域名/泛域名支持多个域名打到一个证书上支持pem、pfx、der、jks等多种证书格式
* 邮件通知、webhook通知
* 私有化部署,数据保存本地,授权信息加密存储镜像由Github Actions构建过程公开透明
* 支持SQLitePostgreSQL、MySQL数据库
* 邮件通知、webhook通知、企微、钉钉、飞书、anpush等多种通知方式
* 私有化部署,数据保存本地,安装升级非常简单快捷
* 镜像由Github Actions构建过程公开透明
* 授权加密站点隐藏2FA密码防爆破等多重安全保障
* 支持SQLitePostgreSQL、MySQL多种数据库
* 开放接口支持
* 站点证书监控
* 多用户管理
![](./docs/images/intro/intro.svg)
>
> 流水线数量现已调整为无限制,欢迎大家使用
>
> 关于证书续期:
>* 实际上没有办法不改变证书文件本身情况下直接续期或者续签。
>* 我们所说的续期,其实就是按照全套流程重新申请一份新证书,然后重新部署上去。
>* 免费证书过期时间90天以后可能还会缩短所以自动化部署必不可少
## 二、在线体验

View File

@@ -1 +1 @@
00:32
20:28

View File

@@ -3,6 +3,8 @@ services:
certd:
# 镜像 # ↓↓↓↓↓ ---- 镜像版本号,建议改成固定版本号,例如certd:1.29.0
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
# image: ghcr.io/certd/certd:latest # --------- 如果 报镜像not found可以尝试其他镜像源
# image: greper/certd:latest
container_name: certd # 容器名
restart: unless-stopped # 自动重启
volumes:
@@ -35,6 +37,8 @@ services:
# networks:
# - ip6net
environment:
# ↓↓↓↓ ----------------------------------------------------- 使用上海东八时区
# - TZ=Asia/Shanghai
# 设置环境变量即可自定义certd配置
# 配置项见: packages/ui/certd-server/src/config/config.default.ts
# 配置规则: certd_ + 配置项, 点号用_代替

View File

@@ -88,22 +88,25 @@ export default defineConfig({
text: "特性",
items: [
{text: "CNAME代理校验", link: "/guide/feature/cname/index.md"},
{text: "插件列表", link: "/guide/plugins.md"},
{text: "多数据库支持", link: "/guide/install/database.md"},
{text: "开放接口", link: "/guide/open/index.md"},
{
text: "站点安全", items: [
{text: "安全特性", link: "/guide/feature/safe"},
{text: "站点隐藏", link: "/guide/feature/safe/hidden"},
{text: "安全生产建议", link: "/guide/feature/safe/suggest"},
text: "站点安全", link: "/guide/feature/safe"
},
{
text: "插件列表", items: [
{text: "授权提供商", link: "/guide/plugins/access"},
{text: "DNS提供商", link: "/guide/plugins/dns-provider"},
{text: "任务插件", link: "/guide/plugins/deploy"},
{text: "通知插件", link: "/guide/plugins/notification"},
]
},
]
},
{
text: "常见问题",
items: [
{text: "常见报错处理", link: "/guide/qa/"},
{text: "群晖证书部署", link: "/guide/use/synology/"},
{text: "腾讯云密钥获取", link: "/guide/use/tencent/"},
{text: "连接windows主机", link: "/guide/use/host/windows.md"},
@@ -115,8 +118,14 @@ export default defineConfig({
{text: "js脚本插件使用", link: "/guide/use/custom-script/index.md"},
{text: "邮箱配置", link: "/guide/use/email/index.md"},
{text: "IPv6支持", link: "/guide/use/setting/ipv6.md"},
{text: "其他插件使用", link: "/deploy/"},
{text: "商业版说明", link: "/comm/"},
{text: "ESXi", link: "/guide/use/ESXi/index.md"},
]
},
{
text: "商业版配置", link: "/guide/use/comm/", items: [
{text: "支付宝配置", link: "/guide/use/comm/payments/alipay.md"},
{text: "微信支付配置", link: "/guide/use/comm/payments/wxpay.md"},
{text: "彩虹易支付配置", link: "/guide/use/comm/payments/yizhifu.md"},
]
},
{
@@ -133,26 +142,6 @@ export default defineConfig({
]
}
],
"/deploy/": [
{
text: "部署证书插件",
items: [
{text: "插件说明", link: "/deploy/index.md"},
{text: "部署到ESXi", link: "/deploy/ESXi/index.md"},
]
}
],
"/comm/": [
{
text: "商业版",
items: [
{text: "支付宝配置", link: "/comm/payments/alipay.md"},
{text: "微信支付配置", link: "/comm/payments/wxpay.md"},
{text: "彩虹易支付配置", link: "/comm/payments/yizhifu.md"},
]
}
]
,
},
socialLinks: [

View File

@@ -1,4 +0,0 @@
# 部署插件说明
## 待完善

View File

@@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
### Bug Fixes
* 修复部署到又拍云强制https无效的bug ([2397097](https://github.com/certd/certd/commit/2397097e4ddcb6f593210598e8779ffd44ac3f8f))
* 修复刷新流水线页面后日志不自动更新的bug ([0b2e28b](https://github.com/certd/certd/commit/0b2e28b62dd5eb6804c602083e65c87a9d1d72d2))
### Performance Improvements
* 集成智能问答机器人 ([9dd4905](https://github.com/certd/certd/commit/9dd49054d18ec436a5029444ca55a38adc682933))
* 支持设置网安备案号 ([d18e431](https://github.com/certd/certd/commit/d18e431e2f08e6b37704032c4ea6fbdd8e971442))
* http方式支持校验443端口 ([d75fcb7](https://github.com/certd/certd/commit/d75fcb7fec421a9a638eaa27fe9378c84b5e0f19))
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
### Bug Fixes

View File

@@ -22,4 +22,6 @@
![](./images/hidden2.png)
## 3、忘记解除地址和解除密码怎么办
登录服务器,在数据库平级的目录下创建`.unhidden`文件即可`临时解除`站点隐藏
登录服务器,在数据库平级的目录下创建`.unhidden`命名的空白文件即可临时解除站点隐藏
临时解除后会自动删除`.unhidden`文件,请尽快设置好新的`解除地址``解除密码`,并记住

View File

@@ -1,36 +1,48 @@
# 站点安全特性
# 安全特性
Certd 存储了证书以及授权等敏感数据,所以需要严格保障安全。
我们非常重视您的数据安全,提供了以下安全特性
Certd 存储了证书以及授权等敏感数据,所以需要严格保障安全。
我们提供了以下安全特性,以及安全生产建议(请遵照建议进行生产部署以保障数据安全)
## 1、 授权数据加密存储【默认开启】
## 一、站点安全特性
### 1、 授权数据加密存储【默认开启】
* 所有的授权敏感字段会加密后存储
* 每个用户独立维护授权数据,连管理员都无权查看
![星号部分为加密数据](./images/access.png)
星号部分为加密数据
## 2、 密码防爆破【默认开启】
### 2、 密码防爆破【默认开启】
* 登录失败次数过多账号将被锁定最高24小时(重启服务可解除锁定)
* 用户登录密码加密hash后存储无法计算出密码明文
![](./images/login.png)
## 3、站点隐藏【建议开启】
### 3、站点隐藏【建议开启】
* 一般来说Certd设置好之后后续很少需要访问修改。
* 所以我们平时可以把站点访问关闭,需要的时候再打开,减少站点被攻击的风险
* 请前往 `系统管理->系统设置->安全设置->开启站点隐藏`
* [站点隐藏设置说明](./hidden/)
![](./images/hidden.png)
## 4、登录双重验证
点击查看 [站点隐藏功能详细使用说明](./hidden/)
### 4、登录双重验证
支持2FA双重认证
![](./images/2fa.png)
## 5、数据库自动备份【建议开启】
### 5、数据库自动备份【建议开启】
* [自动备份设置说明](../../use/backup/)
## 更多安全生产建议
[安全生产建议](./suggest.md)
## 二、安全生产建议
尽管`Cert`本身实现了很多安全特性,但`外部环境的安全`仍需要您来确保。
`务必`遵循如下建议做好安全防护
*`务必`使用`HTTPS协议`访问本应用,避免被中间人攻击
*`务必`使用`web应用防火墙`防护本应用防止XSS、SQL注入等攻击
*`务必`做好`服务器本身`的安全防护,防止数据库泄露
*`务必`做好[`数据备份`](../../use/backup/),避免数据丢失
* 建议开启[`站点隐藏`](./hidden/)功能

View File

@@ -1,10 +0,0 @@
# 安全生产建议
尽管`Cert`本身实现了很多安全特性,但`外部环境的安全`仍需要您来确保。
`务必`遵循如下建议做好安全防护
*`务必`使用`HTTPS协议`访问本应用,避免被中间人攻击
*`务必`使用`web应用防火墙`防护本应用防止XSS、SQL注入等攻击
*`务必`做好`服务器本身`的安全防护,防止数据库泄露
*`务必`做好[`数据备份`](../../use/backup/),避免数据丢失
* 建议开启[`站点隐藏`](./hidden/)功能

View File

@@ -5,32 +5,28 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具
## 1、关于证书续期
>* 实际上没有办法不改变证书文件本身情况下直接续期或者续签。
>* 我们所说的续期,其实就是按照全套流程重新申请一份新证书,然后重新部署上去。
>* 免费证书过期时间90天以后可能还会缩短所以自动化部署必不可少
## 一、特性
## 2、项目特性
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
* 全自动申请证书(支持所有注册商注册的域名)
* 全自动部署更新证书(目前支持部署到主机、部署到阿里云、腾讯云等目前已支持60+部署插件)
* 支持通配符域名/泛域名,支持多个域名打到一个证书上
* 邮件通知
* 私有化部署,保障数据安全
* 支持SQLite、Postgresql、MySQL数据库
* 全自动申请证书(支持所有注册商注册的域名支持DNS-01、HTTP-01、CNAME代理等多种域名验证方式
* 全自动部署更新证书(目前支持部署到主机、阿里云、腾讯云等70+部署插件)
* 支持通配符域名/泛域名,支持多个域名打到一个证书上支持pem、pfx、der、jks等多种证书格式
* 邮件通知、webhook通知、企微、钉钉、飞书、anpush等多种通知方式
* 私有化部署,数据保存本地,安装升级非常简单快捷
* 镜像由Github Actions构建过程公开透明
* 授权加密站点隐藏2FA密码防爆破等多重安全保障
* 支持SQLitePostgreSQL、MySQL多种数据库
* 开放接口支持
* 站点证书监控
* 多用户管理
![](../images/intro/intro.svg)
## 二、一些说明
* 本项目申请证书过程遵循acme协议
* 需要验证域名所有权,一般有两种方式
* http-01 在网站根目录下放置一份txt文件
* dns-01 需要给域名添加txt解析记录通配符域名只能用这种方式本项目仅支持dns-01
* 证书续期:
* 实际上没有办法不改变证书文件本身情况下直接续期或者续签。
* 我们所说的续期,其实就是按照全套流程重新申请一份新证书,然后重新部署上去。
* 免费证书过期时间90天以后可能还会缩短所以自动化部署必不可少
* 设置每天自动运行当证书过期前35天会自动重新申请证书并部署
## 三、证书颁发机构对比
* Let's Encrypt申请最简单。
* Google: 大厂光环兼容性好首次需要翻墙获取EAB。
* ZeroSSL 需要EAB获取EAB无需翻墙。

View File

@@ -1,5 +0,0 @@
# 插件列表
![img_1.png](../images/plugins/list.png)

View File

@@ -0,0 +1,58 @@
# 授权列表
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **阿里云授权** | |
| 2.| **EAB授权** | ZeroSSL证书申请需要EAB授权 |
| 3.| **google cloud** | 谷歌云授权 |
| 4.| **主机登录授权** | |
| 5.| **SFTP授权** | |
| 6.| **阿里云OSS授权** | 包含地域和Bucket |
| 7.| **FTP授权** | |
| 8.| **腾讯云** | |
| 9.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 |
| 10.| **七牛云授权** | |
| 11.| **七牛OSS授权** | |
| 12.| **天翼云授权** | |
| 13.| **s3/minio授权** | S3/minio oss授权 |
| 14.| **baota授权** | |
| 15.| **易盾DCDN授权** | https://user.yiduncdn.com |
| 16.| **易盾rcdn授权** | 易盾CDN每月免费30G[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
| 17.| **易发云短信** | sms.yfyidc.cn/ |
| 18.| **cdnfly授权** | |
| 19.| **群晖登录授权** | |
| 20.| **k8s授权** | |
| 21.| **1panel授权** | 账号和密码 |
| 22.| **百度云授权** | |
| 23.| **LeCDN授权** | |
| 24.| **白山云授权** | |
| 25.| **plesk授权** | |
| 26.| **易支付** | |
| 27.| **支付宝** | |
| 28.| **微信支付** | |
| 29.| **长亭雷池授权** | |
| 30.| **lucky** | |
| 31.| **括彩云cdn授权** | 括彩云CDN每月免费30G[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
| 32.| **uniCloud** | unicloud授权 |
| 33.| **华为云授权** | |
| 34.| **西部数码授权** | |
| 35.| **多吉云** | |
| 36.| **我爱云授权** | 我爱云CDN |
| 37.| **CacheFly** | CacheFly |
| 38.| **Gcore** | Gcore |
| 39.| **亚马逊云aws授权** | |
| 40.| **dns.la授权** | |
| 41.| **又拍云** | |
| 42.| **火山引擎** | |
| 43.| **京东云** | |
| 44.| **51dns授权** | |
<style module>
table th:first-of-type {
width: 65px;
}
table th:nth-of-type(2) {
width: 240px;
}
</style>

View File

@@ -0,0 +1,130 @@
# 任务插件
`70` 款任务插件
## 1. 证书申请
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **证书申请JS版** | 免费通配符域名证书申请,支持多个域名打到同一个证书上 |
| 2.| **证书申请Lego** | 支持海量DNS解析提供商推荐使用一样的免费通配符域名证书申请支持多个域名打到同一个证书上 |
| 3.| **商用证书托管** | 手动上传自定义证书后,自动部署(每次证书有更新,都需要手动上传一次) |
## 2. 主机
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **FTP-上传证书到FTP** | 将证书上传到FTP服务器 |
| 2.| **IIS-部署到IIS站点** | |
| 3.| **主机-执行远程主机脚本命令** | 可以执行重启nginx等操作让证书生效 |
| 4.| **主机-部署证书到SSH主机** | SFTP上传证书到主机然后SSH执行部署脚本命令 |
## 3. CDN
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **易盾-部署到易盾DCDN** | 主要是防御http://user.yiduncdn.com/ |
| 2.| **易盾-部署到易盾RCDN** | 易盾CDN每月免费30G[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
| 3.| **cdnfly-部署证书到cdnfly** | cdnfly |
| 4.| **百度云-部署证书到CDN** | 部署到百度云CDN |
| 5.| **LeCDN-更新证书** | |
| 6.| **LeCDN-更新证书V2** | 支持新版本LeCDN |
| 7.| **白山云-更新证书** | |
| 8.| **天翼云-部署证书到CDN** | 部署证书到天翼云CDN和全站加速 |
| 9.| **括彩云-部署到括彩云CDN** | 括彩云CDN每月免费30G[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
| 10.| **多吉云-部署到多吉云CDN** | |
| 11.| **我爱云-部署证书到我爱云CDN** | 部署证书到我爱云CDN |
| 12.| **CacheFly-部署证书到CacheFly** | 部署证书到 CacheFly |
| 13.| **Gcore-部署证书到Gcore** | 仅上传 并不会部署到cdn |
| 14.| **Gcore-刷新Gcore证书** | 刷新现有的证书 |
| 15.| **又拍云-部署证书到CDN/USS** | 支持又拍云CDN又拍云云存储USS |
## 4. 面板
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **宝塔-面板证书部署** | 部署宝塔面板本身的ssl证书 |
| 2.| **宝塔-网站证书部署** | 部署宝塔管理的站点的ssl证书目前支持网站站点、docker站点等 |
| 3.| **群晖-部署证书到群晖面板** | Synology支持6.x以上版本 |
| 4.| **K8S-部署证书到Secret** | 部署证书到k8s的secret |
| 5.| **K8S-Ingress 证书部署** | 部署证书到k8s的Ingress |
| 6.| **1Panel-部署证书到1Panel** | 更新1Panel的证书 |
| 7.| **Plesk-部署Plesk网站证书** | |
| 8.| **雷池-更新证书** | 更新长亭雷池WAF的证书 |
| 9.| **lucky-更新Lucky证书** | |
| 10.| **uniCloud-部署到服务空间** | 部署到服务空间 |
| 11.| **威联通-部署证书到威联通** | 部署证书到qnap |
## 5. 阿里云
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **阿里云-部署到Ack** | 部署到阿里云Ack集群Ingress等通过Secret管理证书的应用 |
| 2.| **阿里云-部署至任意云资源** | 【不建议使用】需要消耗阿里云自动部署次数支持SLB、LIVE、webHosting、VOD、CR、DCDN、DDoS、CDN、ALB、APIGateway、FC、GA、MSE、NLB、OSS、SAE、WAF等云产品 |
| 3.| **阿里云-部署证书至CDN** | 自动部署域名证书至阿里云CDN |
| 4.| **阿里云-部署证书至DCDN** | 依赖证书申请前置任务自动部署域名证书至阿里云DCDN |
| 5.| **阿里云-部署证书至OSS** | 自动部署域名证书至阿里云OSS |
| 6.| **阿里云-上传证书到阿里云** | 如果不想在阿里云上同一份证书上传多次,可以把此任务作为前置任务,其他阿里云任务证书那一项选择此任务的输出 |
| 7.| **阿里云-部署至阿里云WAF** | 部署证书到阿里云WAF |
| 8.| **阿里云-部署至ALB应用负载均衡** | ALB,更新监听器的默认证书 |
| 9.| **阿里云-部署至NLB网络负载均衡** | NLB,网络负载均衡,更新监听器的默认证书 |
| 10.| **阿里云-部署至SLB(传统负载均衡)** | 部署证书到阿里云SLB(传统负载均衡) |
| 11.| **阿里云-部署至阿里云FC(3.0)** | 部署证书到阿里云函数计算FC3.0,【注意】证书的加密算法必须选择【pkcs1旧版】 |
## 6. 华为云
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **华为云-部署证书至CDN** | |
## 7. 腾讯云
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **腾讯云-部署证书到任意云资源** | 支持负载均衡、CDN、DDoS、直播、点播、Web应用防火墙、API网关、TEO、容器服务、对象存储、轻应用服务器、云原生微服务、云开发 |
| 2.| **腾讯云-部署到CLB** | 暂时只支持单向认证证书,暂时只支持通用负载均衡 |
| 3.| **腾讯云-部署到CDN废弃** | 已废弃请使用v2版 |
| 4.| **腾讯云-部署到CDN-v2** | 推荐使用 |
| 5.| **腾讯云-上传证书到腾讯云** | 上传成功后输出tencentCertId |
| 6.| **腾讯云-部署证书到COS** | 部署到腾讯云COS源站域名证书【注意很不稳定需要重试很多次偶尔才能成功一次】 |
| 7.| **腾讯云-部署到腾讯云EO** | 腾讯云边缘安全加速平台EO必须配置上传证书到腾讯云任务 |
| 8.| **腾讯云-删除即将过期证书** | 仅删除未使用的证书 |
| 9.| **腾讯云-部署到TKE-ingress** | serverless集群请使用K8S部署插件Qcloud类型需要【上传到腾讯云】作为前置任务ApiServer未开启外网访问则需要做域名的内网IP映射 |
## 8. 火山引擎
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **火山引擎-部署证书至CDN** | 支持网页,文件下载,音视频点播 |
| 2.| **火山引擎-部署证书至CLB** | 部署至火山引擎负载均衡 |
| 3.| **火山引擎-上传证书至证书中心** | 上传证书至火山引擎证书中心 |
| 4.| **火山引擎-部署证书至ALB** | 部署至火山引擎应用负载均衡 |
| 5.| **火山引擎-部署证书至Live** | 部署至火山引擎视频直播 |
## 9. 京东云
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **京东云-部署证书至CDN** | 京东云内容分发网络 |
| 2.| **京东云-更新已有证书** | 更新SSL数字证书中的证书 |
| 3.| **京东云-上传新证书** | 上传证书到SSL数字证书中心 |
## 10. 七牛云
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **七牛云-部署证书至OSS** | 自动部署域名证书至七牛云KODO注意是自定义源站域名不是CDN域名 |
| 2.| **七牛云-部署证书至CDN** | 自动部署域名证书至七牛云CDN |
## 11. 亚马逊云
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **AWS-部署证书到CloudFront** | 部署证书到 AWS CloudFront |
## 12. 其他
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **Demo-测试插件** | |
| 2.| **重启 Certd** | 【仅管理员可用】 重启 certd的https服务用于更新 Certd 的 ssl 证书 |
| 3.| **自定义js脚本** | 【仅管理员】运行自定义js脚本执行 |
| 4.| **等待** | 等待一段时间 |
| 5.| **数据库备份** | 仅支持备份SQLite数据库 |
<style module>
table th:first-of-type {
width: 65px;
}
table th:nth-of-type(2) {
width: 240px;
}
</style>

View File

@@ -0,0 +1,22 @@
# DNS提供商
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **阿里云** | 阿里云DNS解析提供商 |
| 2.| **腾讯云** | 腾讯云域名DNS解析提供者 |
| 3.| **华为云** | 华为云DNS解析提供商 |
| 4.| **西部数码** | west dns provider |
| 5.| **dns.la** | dns.la |
| 6.| **火山引擎** | 火山引擎DNS解析提供商 |
| 7.| **京东云** | 京东云DNS解析提供商 |
| 8.| **51dns** | 51DNS |
<style module>
table th:first-of-type {
width: 65px;
}
table th:nth-of-type(2) {
width: 240px;
}
</style>

View File

@@ -0,0 +1,26 @@
# 通知插件
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **企业微信通知** | 企业微信群聊机器人通知 |
| 2.| **电子邮件** | 电子邮件通知 |
| 3.| **爱语飞飞微信通知(iyuu)** | https://iyuu.cn/ |
| 4.| **自定义webhook** | 根据模版自定义http请求 |
| 5.| **Server酱ᵀ** | https://sct.ftqq.com/ |
| 6.| **Server酱³** | https://doc.sc3.ft07.com/serverchan3 |
| 7.| **AnPush** | https://anpush.com |
| 8.| **Telegram通知** | Telegram Bot推送通知 |
| 9.| **Discord 通知** | Discord 机器人通知 |
| 10.| **Slack通知** | Slack消息推送通知 |
| 11.| **Bark 通知** | Bark 推送通知插件 |
| 12.| **飞书通知** | 飞书群聊webhook通知 |
<style module>
table th:first-of-type {
width: 65px;
}
table th:nth-of-type(2) {
width: 240px;
}
</style>

19
docs/guide/qa/index.md Normal file
View File

@@ -0,0 +1,19 @@
# 常见报错解决
## 1. getaddrinfo ENOTFOUND错误
如果出现`getaddrinfo ENOTFOUND`错误,可以尝试在`docker-compose.yaml`中设置dns
```yaml
version: '3.3' # 兼容旧版docker-compose
services:
certd:
#↓↓↓↓ ------------ # 如果出现getaddrinfo ENOTFOUND错误可以尝试设置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
```

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,13 @@
# 带输出的前置任务
前置任务输出可以在后续任务中使用
比如上传证书到阿里云会返回阿里云的CertId之后其他阿里云的部署任务可以选择复用这个证书
## 复用证书
![img.png](images/pretask1.png)
在后续任务中可以选择前置任务的输出
![img.png](images/pretask2.png)

View File

@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.34.1"
"version": "1.34.2"
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/publishlab/node-acme-client/compare/v1.34.1...v1.34.2) (2025-05-11)
### Performance Improvements
* http方式支持校验443端口 ([d75fcb7](https://github.com/publishlab/node-acme-client/commit/d75fcb7fec421a9a638eaa27fe9378c84b5e0f19))
## [1.34.1](https://github.com/publishlab/node-acme-client/compare/v1.34.0...v1.34.1) (2025-05-05)
### Bug Fixes

View File

@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.34.1",
"version": "1.34.2",
"type": "module",
"module": "scr/index.js",
"main": "src/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.34.1",
"@certd/basic": "^1.34.2",
"@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": "6c74148c277432f91014bf1eebd824e7423c6f4b"
"gitHead": "a1e504c1387e9b0554c8d030cb53c5058e7d683a"
}

View File

@@ -234,6 +234,7 @@ export default async (client, userOpts) => {
throw new CancelError("用户取消");
}
const waitDnsDiffuseTime = opts.waitDnsDiffuseTime || 30;
try {
// eslint-disable-next-line no-await-in-loop
await runPromisePa(challengePromises);
@@ -242,8 +243,8 @@ export default async (client, userOpts) => {
await wait(60 * 1000);
} else {
await runPromisePa(localVerifyTasks, 1000);
log("本地校验完成,等待30s")
await wait(30 * 1000)
log(`本地校验完成,等待${waitDnsDiffuseTime}s`)
await wait(waitDnsDiffuseTime * 1000)
}
log("开始向提供商请求挑战验证");

View File

@@ -24,22 +24,46 @@ const dns = dnsSdk.promises
*/
async function verifyHttpChallenge(authz, challenge, keyAuthorization, suffix = `/.well-known/acme-challenge/${challenge.token}`) {
async function doQuery(challengeUrl){
log(`正在测试请求 ${challengeUrl} `)
// const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443;
// const challengeUrl = `https://${authz.identifier.value}:${httpsPort}${suffix}`;
/* May redirect to HTTPS with invalid/self-signed cert - https://letsencrypt.org/docs/challenge-types/#http-01-challenge */
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
log(`Sending HTTP query to ${authz.identifier.value}, suffix: ${suffix}, port: ${httpPort}`);
let data = ""
try{
const resp = await axios.get(challengeUrl, { httpsAgent });
data = (resp.data || '').replace(/\s+$/, '');
}catch (e) {
log(`[error] HTTP request error from ${authz.identifier.value}`,e.message);
return false
}
if (!data || (data !== keyAuthorization)) {
log(`[error] Authorization not found in HTTP response from ${authz.identifier.value}`);
return false
}
return true
}
const httpPort = axios.defaults.acmeSettings.httpChallengePort || 80;
const challengeUrl = `http://${authz.identifier.value}:${httpPort}${suffix}`;
/* May redirect to HTTPS with invalid/self-signed cert - https://letsencrypt.org/docs/challenge-types/#http-01-challenge */
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
log(`Sending HTTP query to ${authz.identifier.value}, suffix: ${suffix}, port: ${httpPort}`);
const resp = await axios.get(challengeUrl, { httpsAgent });
const data = (resp.data || '').replace(/\s+$/, '');
log(`Query successful, HTTP status code: ${resp.status}`);
if (!data || (data !== keyAuthorization)) {
throw new Error(`Authorization not found in HTTP response from ${authz.identifier.value}`);
if (!await doQuery(challengeUrl)) {
const httpsPort = axios.defaults.acmeSettings.httpsChallengePort || 443;
const httpsChallengeUrl = `https://${authz.identifier.value}:${httpsPort}${suffix}`;
const res = await doQuery(httpsChallengeUrl)
if (!res) {
throw new Error(`[error] 验证失败请检查以上测试url是否可以正常访问`);
}
}
log(`Key authorization match for ${challenge.type}/${authz.identifier.value}, ACME challenge verified`);
return true;
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
**Note:** Version bump only for package @certd/basic
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
### Performance Improvements

View File

@@ -1 +1 @@
00:16
20:23

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.34.1",
"version": "1.34.2",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -45,5 +45,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
"gitHead": "a1e504c1387e9b0554c8d030cb53c5058e7d683a"
}

View File

@@ -145,7 +145,8 @@ export function createAxiosService({ logger }: { logger: Logger }) {
} else {
logger.info("http response status:", response?.status);
}
if (response?.config?.returnResponse) {
if (response?.config?.returnOriginRes) {
return response;
}
return response.data;
@@ -215,7 +216,7 @@ export type HttpRequestConfig<D = any> = {
logParams?: boolean;
logRes?: boolean;
httpProxy?: string;
returnResponse?: boolean;
returnOriginRes?: boolean;
} & AxiosRequestConfig<D>;
export type HttpClient = {
request<D = any, R = any>(config: HttpRequestConfig<D>): Promise<HttpClientResponse<R>>;

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
**Note:** Version bump only for package @certd/pipeline
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
**Note:** Version bump only for package @certd/pipeline

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.34.1",
"version": "1.34.2",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -17,8 +17,8 @@
"pub": "npm publish"
},
"dependencies": {
"@certd/basic": "^1.34.1",
"@certd/plus-core": "^1.34.1",
"@certd/basic": "^1.34.2",
"@certd/plus-core": "^1.34.2",
"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": "6c74148c277432f91014bf1eebd824e7423c6f4b"
"gitHead": "a1e504c1387e9b0554c8d030cb53c5058e7d683a"
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
**Note:** Version bump only for package @certd/lib-huawei
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
**Note:** Version bump only for package @certd/lib-huawei

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.34.1",
"version": "1.34.2",
"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": "6c74148c277432f91014bf1eebd824e7423c6f4b"
"gitHead": "a1e504c1387e9b0554c8d030cb53c5058e7d683a"
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
**Note:** Version bump only for package @certd/lib-iframe
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
**Note:** Version bump only for package @certd/lib-iframe

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.34.1",
"version": "1.34.2",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -31,5 +31,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
"gitHead": "a1e504c1387e9b0554c8d030cb53c5058e7d683a"
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
**Note:** Version bump only for package @certd/jdcloud
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
**Note:** Version bump only for package @certd/jdcloud

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/jdcloud",
"version": "1.34.1",
"version": "1.34.2",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
@@ -61,5 +61,5 @@
"fetch"
]
},
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
"gitHead": "a1e504c1387e9b0554c8d030cb53c5058e7d683a"
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
**Note:** Version bump only for package @certd/lib-k8s
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
**Note:** Version bump only for package @certd/lib-k8s

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-k8s",
"private": false,
"version": "1.34.1",
"version": "1.34.2",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -17,7 +17,7 @@
"pub": "npm publish"
},
"dependencies": {
"@certd/basic": "^1.34.1",
"@certd/basic": "^1.34.2",
"@kubernetes/client-node": "0.21.0"
},
"devDependencies": {
@@ -32,5 +32,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
"gitHead": "a1e504c1387e9b0554c8d030cb53c5058e7d683a"
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
### Performance Improvements
* 支持设置网安备案号 ([d18e431](https://github.com/certd/certd/commit/d18e431e2f08e6b37704032c4ea6fbdd8e971442))
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
**Note:** Version bump only for package @certd/lib-server

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.34.1",
"version": "1.34.2",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -27,10 +27,10 @@
],
"license": "AGPL",
"dependencies": {
"@certd/acme-client": "^1.34.1",
"@certd/basic": "^1.34.1",
"@certd/pipeline": "^1.34.1",
"@certd/plus-core": "^1.34.1",
"@certd/acme-client": "^1.34.2",
"@certd/basic": "^1.34.2",
"@certd/pipeline": "^1.34.2",
"@certd/plus-core": "^1.34.2",
"@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": "6c74148c277432f91014bf1eebd824e7423c6f4b"
"gitHead": "a1e504c1387e9b0554c8d030cb53c5058e7d683a"
}

View File

@@ -25,6 +25,7 @@ export class SysPublicSettings extends BaseSettings {
limitUserPipelineCount = 0;
managerOtherUserPipeline = false;
icpNo?: string;
mpsNo?: string;
robots?: boolean = true;
}

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
**Note:** Version bump only for package @certd/midway-flyway-js

View File

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

View File

@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
**Note:** Version bump only for package @certd/plugin-cert
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
### Bug Fixes

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.34.1",
"version": "1.34.2",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -16,10 +16,10 @@
"pub": "npm publish"
},
"dependencies": {
"@certd/acme-client": "^1.34.1",
"@certd/basic": "^1.34.1",
"@certd/pipeline": "^1.34.1",
"@certd/plugin-lib": "^1.34.1",
"@certd/acme-client": "^1.34.2",
"@certd/basic": "^1.34.2",
"@certd/pipeline": "^1.34.2",
"@certd/plugin-lib": "^1.34.2",
"@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": "6c74148c277432f91014bf1eebd824e7423c6f4b"
"gitHead": "a1e504c1387e9b0554c8d030cb53c5058e7d683a"
}

View File

@@ -8,6 +8,8 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
logger!: ILogger;
usePunyCode(): boolean {
//是否使用punycode来添加解析记录
//默认都使用原始中文域名来添加
return false;
}

View File

@@ -63,6 +63,7 @@ type AcmeServiceOptions = {
maxCheckRetryCount?: number;
userId: number;
domainParser: IDomainParser;
waitDnsDiffuseTime?: number;
};
export class AcmeService {

View File

@@ -68,9 +68,9 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
],
},
required: true,
helper: `DNS直接验证:域名是在阿里云/腾讯云/华为云/Cloudflare/NameSilo/西数/火山/dns.la/京东云注册的,选它
CNAME代理验证支持任何注册商注册的域名,第一次需要手动添加CNAME记录
HTTP文件验证不支持泛域名需要配置网站文件上传`,
helper: `1. <b>DNS直接验证</b>域名dns解析是在阿里云/腾讯云/华为云/CF/NameSilo/西数/火山/dns.la/京东云/51dns的,选它
2. <b>CNAME代理验证</b>支持任何注册商的域名第一次需要手动添加CNAME记录建议将DNS服务器修改为阿里云/腾讯云的然后使用DNS直接验证
3. <b>HTTP文件验证</b>:不支持泛域名,需要配置网站文件上传`,
})
challengeType!: string;
@@ -290,6 +290,17 @@ HTTP文件验证不支持泛域名需要配置网站文件上传`,
})
maxCheckRetryCount = 20;
@TaskInput({
title: "等待解析生效时长",
value: 30,
component: {
name: "a-input-number",
vModel: "value",
},
helper: "等待解析生效时长(秒)",
})
waitDnsDiffuseTime = 30;
acme!: AcmeService;
eab!: EabAccess;
@@ -341,6 +352,7 @@ HTTP文件验证不支持泛域名需要配置网站文件上传`,
signal: this.ctx.signal,
maxCheckRetryCount: this.maxCheckRetryCount,
domainParser,
waitDnsDiffuseTime: this.waitDnsDiffuseTime,
});
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
### Performance Improvements
* http方式支持校验443端口 ([d75fcb7](https://github.com/certd/certd/commit/d75fcb7fec421a9a638eaa27fe9378c84b5e0f19))
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
### Performance Improvements

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
"version": "1.34.1",
"version": "1.34.2",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -18,8 +18,8 @@
"dependencies": {
"@alicloud/pop-core": "^1.7.10",
"@aws-sdk/client-s3": "^3.787.0",
"@certd/basic": "^1.34.1",
"@certd/pipeline": "^1.34.1",
"@certd/basic": "^1.34.2",
"@certd/pipeline": "^1.34.2",
"@kubernetes/client-node": "0.21.0",
"ali-oss": "^6.22.0",
"basic-ftp": "^5.0.5",
@@ -50,5 +50,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
"gitHead": "a1e504c1387e9b0554c8d030cb53c5058e7d683a"
}

View File

@@ -52,9 +52,11 @@ export class AliossClient {
}
}
async uploadFile(filePath: string, content: Buffer | string) {
async uploadFile(filePath: string, content: Buffer | string, timeout = 1000 * 60 * 60) {
await this.init();
return await this.client.put(filePath, content);
return await this.client.put(filePath, content, {
timeout,
});
}
async removeFile(filePath: string) {
@@ -62,9 +64,11 @@ export class AliossClient {
return await this.client.delete(filePath);
}
async downloadFile(key: string, savePath: string) {
async downloadFile(key: string, savePath: string, timeout = 1000 * 60 * 60) {
await this.init();
return await this.client.get(key, savePath);
return await this.client.get(key, savePath, {
timeout,
});
}
async listDir(dirKey: string) {

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
### Bug Fixes
* 修复刷新流水线页面后日志不自动更新的bug ([0b2e28b](https://github.com/certd/certd/commit/0b2e28b62dd5eb6804c602083e65c87a9d1d72d2))
### Performance Improvements
* 集成智能问答机器人 ([9dd4905](https://github.com/certd/certd/commit/9dd49054d18ec436a5029444ca55a38adc682933))
* 支持设置网安备案号 ([d18e431](https://github.com/certd/certd/commit/d18e431e2f08e6b37704032c4ea6fbdd8e971442))
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
### Bug Fixes

View File

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

View File

@@ -3,6 +3,7 @@
<FsFormProvider>
<contextHolder />
<router-view />
<MaxKBChat ref="chatBox" />
</FsFormProvider>
</AConfigProvider>
</template>
@@ -10,7 +11,7 @@
<script lang="ts" setup>
import zhCN from "ant-design-vue/es/locale/zh_CN";
import enUS from "ant-design-vue/es/locale/en_US";
import { computed, provide, ref } from "vue";
import { computed, onMounted, provide, ref } from "vue";
import "dayjs/locale/zh-cn";
import "dayjs/locale/en";
import dayjs from "dayjs";
@@ -19,6 +20,8 @@ import { useAntdDesignTokens } from "/@/vben/hooks";
import { theme } from "ant-design-vue";
import AConfigProvider from "ant-design-vue/es/config-provider";
import { Modal } from "ant-design-vue";
import MaxKBChat from "/@/components/ai/index.vue";
import { util } from "/@/utils";
defineOptions({
name: "App",
@@ -69,4 +72,10 @@ const tokenTheme = computed(() => {
// pageStore.init();
// const settingStore = useSettingStore();
// settingStore.init();
const chatBox = ref();
onMounted(async () => {
// await util.sleep(5000);
// await chatBox.value.openChat({ q: "hello" });
});
</script>

View File

@@ -36,7 +36,7 @@ function createService() {
return response;
}
//@ts-ignore
if (response.config.returnResponse) {
if (response.config.returnOriginRes) {
return response;
}
// dataAxios 是 axios 返回数据中的 data

View File

@@ -0,0 +1,307 @@
<template>
<div id="maxkb">
<!-- 引导层 -->
<div v-if="showGuide" class="maxkb-mask">
<div class="maxkb-content" :style="contentStyle"></div>
</div>
<div v-if="showGuide" class="maxkb-tips" :style="tipsStyle">
<div class="" @click="closeGuide">
<!-- 关闭图标 -->
<svg style="vertical-align: middle; overflow: hidden" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M9.95317 8.73169L15.5511 3.13376C15.7138 2.97104 15.9776 2.97104 16.1403 3.13376L16.7296 3.72301C16.8923 3.88573 16.8923 4.14955 16.7296 4.31227L11.1317 9.9102L16.7296 15.5081C16.8923 15.6708 16.8923 15.9347 16.7296 16.0974L16.1403 16.6866C15.9776 16.8494 15.7138 16.8494 15.5511 16.6866L9.95317 11.0887L4.35524 16.6866C4.19252 16.8494 3.9287 16.8494 3.76598 16.6866L3.17673 16.0974C3.01401 15.9347 3.01401 15.6708 3.17673 15.5081L8.77465 9.9102L3.17673 4.31227C3.01401 4.14955 3.01401 3.88573 3.17673 3.72301L3.76598 3.13376C3.9287 2.97104 4.19252 2.97104 4.35524 3.13376L9.95317 8.73169Z"
fill="#ffffff"
></path>
</svg>
</div>
<div class="maxkb-title">🌟 遇见问题不再有障碍</div>
<p>你好我是你的智能小助手<br />点我开启高效解答模式让问题变成过去式</p>
<div class="maxkb-button">
<button @click="closeGuide">我知道了</button>
</div>
<span class="maxkb-arrow"></span>
</div>
<!-- 聊天按钮 -->
<div v-show="!chatVisible" class="maxkb-chat-button" :style="buttonPosition" @click="toggleChat">
<img src="https://maxkb.handfree.work/ui/MaxKB.gif" />
</div>
<!-- 聊天窗口 -->
<div id="maxkb-chat-container" :class="{ 'maxkb-enlarge': isFullscreen }" :style="containerStyle">
<iframe id="maxkb-chat" allow="microphone" :src="chatUrl"></iframe>
<div class="maxkb-operate">
<div class="viewport" :class="{ 'maxkb-viewportnone': !isFullscreen }" @click="toggleFullscreen">
<!-- 缩小图标 -->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M7.507 11.6645C7.73712 11.6645 7.94545 11.7578 8.09625 11.9086C8.24706 12.0594 8.34033 12.2677 8.34033 12.4978V16.7976C8.34033 17.0277 8.15378 17.2143 7.92366 17.2143H7.09033C6.86021 17.2143 6.67366 17.0277 6.67366 16.7976V14.5812L3.41075 17.843C3.24803 18.0057 2.98421 18.0057 2.82149 17.843L2.23224 17.2537C2.06952 17.091 2.06952 16.8272 2.23224 16.6645L5.56668 13.3311H3.19634C2.96622 13.3311 2.77967 13.1446 2.77967 12.9145V12.0811C2.77967 11.851 2.96622 11.6645 3.19634 11.6645H7.507ZM16.5991 2.1572C16.7619 1.99448 17.0257 1.99448 17.1884 2.1572L17.7777 2.74645C17.9404 2.90917 17.9404 3.17299 17.7777 3.33571L14.4432 6.66904H16.8136C17.0437 6.66904 17.2302 6.85559 17.2302 7.08571V7.91904C17.2302 8.14916 17.0437 8.33571 16.8136 8.33571H12.5029C12.2728 8.33571 12.0644 8.24243 11.9136 8.09163C11.7628 7.94082 11.6696 7.73249 11.6696 7.50237V3.20257C11.6696 2.97245 11.8561 2.7859 12.0862 2.7859H12.9196C13.1497 2.7859 13.3362 2.97245 13.3362 3.20257V5.419L16.5991 2.1572Z"
fill="rgb(100, 106, 115)"
/>
</svg>
</div>
<div class="maxkb-openviewport" :class="{ 'maxkb-viewportnone': isFullscreen }" @click="toggleFullscreen">
<!-- 放大图标 -->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M7.15209 11.5968C7.31481 11.4341 7.57862 11.4341 7.74134 11.5968L8.3306 12.186C8.49332 12.3487 8.49332 12.6126 8.3306 12.7753L4.99615 16.1086H7.3665C7.59662 16.1086 7.78316 16.2952 7.78316 16.5253V17.3586C7.78316 17.5887 7.59662 17.7753 7.3665 17.7753H3.05584C2.82572 17.7753 2.61738 17.682 2.46658 17.5312C2.31578 17.3804 2.2225 17.1721 2.2225 16.9419V12.6421C2.2225 12.412 2.40905 12.2255 2.63917 12.2255H3.4725C3.70262 12.2255 3.88917 12.412 3.88917 12.6421V14.8586L7.15209 11.5968ZM16.937 2.22217C17.1671 2.22217 17.3754 2.31544 17.5262 2.46625C17.677 2.61705 17.7703 2.82538 17.7703 3.0555V7.35531C17.7703 7.58543 17.5837 7.77198 17.3536 7.77198H16.5203C16.2902 7.77198 16.1036 7.58543 16.1036 7.35531V5.13888L12.8407 8.40068C12.678 8.5634 12.4142 8.5634 12.2515 8.40068L11.6622 7.81142C11.4995 7.64871 11.4995 7.38489 11.6622 7.22217L14.9966 3.88883H12.6263C12.3962 3.88883 12.2096 3.70229 12.2096 3.47217V2.63883C12.2096 2.40872 12.3962 2.22217 12.6263 2.22217H16.937Z"
fill="rgb(100, 106, 115)"
/>
</svg>
</div>
<div class="maxkb-chat-close" @click="toggleChat">
<!-- 关闭图标 -->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path
d="M9.95317 8.73169L15.5511 3.13376C15.7138 2.97104 15.9776 2.97104 16.1403 3.13376L16.7296 3.72301C16.8923 3.88573 16.8923 4.14955 16.7296 4.31227L11.1317 9.9102L16.7296 15.5081C16.8923 15.6708 16.8923 15.9347 16.7296 16.0974L16.1403 16.6866C15.9776 16.8494 15.7138 16.8494 15.5511 16.6866L9.95317 11.0887L4.35524 16.6866C4.19252 16.8494 3.9287 16.8494 3.76598 16.6866L3.17673 16.0974C3.01401 15.9347 3.01401 15.6708 3.17673 15.5081L8.77465 9.9102L3.17673 4.31227C3.01401 4.14955 3.01401 3.88573 3.17673 3.72301L3.76598 3.13376C3.9287 2.97104 4.19252 2.97104 4.35524 3.13376L9.95317 8.73169Z"
fill="rgb(100, 106, 115)"
/>
</svg>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, onMounted, ref } from "vue";
defineOptions({
name: "MaxKBChat",
});
const props = defineProps<{
url?: string;
showGuideOnInit?: boolean;
}>();
// 响应式状态
const showGuide = ref(false);
const chatVisible = ref(false);
const isFullscreen = ref(false);
const buttonPos = ref({ x: 16, y: 30 });
const buttonSize = ref({ width: 64, height: 64 });
// 计算属性
const chatUrl = computed(() => {
return props.url || "https://maxkb.handfree.work/ui/chat/326a8651825e8a02?mode=embed";
});
const contentStyle = computed(() => ({
width: `${buttonSize.value.width}px`,
height: `${buttonSize.value.height}px`,
}));
const tipsStyle = computed(() => ({
marginRight: `${Math.min(buttonSize.value.width, 500) - 64}px`,
}));
const containerStyle = computed(() => ({
display: chatVisible.value ? "block" : "none",
right: `${buttonPos.value.x}px`,
bottom: `${buttonPos.value.y}px`,
}));
// 方法
const closeGuide = () => {
showGuide.value = false;
localStorage.setItem("maxkbMaskTip", "true");
};
const toggleChat = () => {
chatVisible.value = !chatVisible.value;
};
const toggleFullscreen = () => {
isFullscreen.value = !isFullscreen.value;
};
// 初始化
onMounted(() => {
if (props.showGuideOnInit && !localStorage.getItem("maxkbMaskTip")) {
showGuide.value = true;
}
});
async function openChat(req: { q: string }) {
showGuide.value = true;
const iframeId = "maxkb-chat";
const iframe = document.getElementById(iframeId) as HTMLIFrameElement | null;
if (!iframe) {
throw new Error("iframe not found");
return;
}
iframe.contentWindow?.postMessage(req, "*");
}
defineExpose({
openChat,
});
</script>
<style scoped>
/* 这里放置所有样式(保持原有样式不变) */
/* 注意将原样式中的 #maxkb 选择器改为 #maxkb-container */
/* 示例: */
/* 其他样式保持原样... */
/* 放大 */
#maxkb .maxkb-enlarge {
width: 50% !important;
height: 100% !important;
bottom: 0 !important;
right: 0 !important;
}
@media only screen and (max-width: 768px) {
#maxkb .maxkb-enlarge {
width: 100% !important;
height: 100% !important;
right: 0 !important;
bottom: 0 !important;
}
}
/* 引导 */
#maxkb .maxkb-mask {
position: fixed;
z-index: 10001;
background-color: transparent;
height: 100%;
width: 100%;
top: 0;
left: 0;
}
#maxkb .maxkb-mask .maxkb-content {
width: 64px;
height: 64px;
box-shadow: 1px 1px 1px 9999px rgba(0, 0, 0, 0.6);
position: absolute;
right: 0px;
bottom: 30px;
z-index: 10001;
}
#maxkb .maxkb-tips {
position: fixed;
right: calc(0px + 75px);
bottom: calc(30px + 0px);
padding: 22px 24px 24px;
border-radius: 6px;
color: #ffffff;
font-size: 14px;
background: #3370ff;
z-index: 10001;
}
#maxkb .maxkb-tips .maxkb-arrow {
position: absolute;
background: #3370ff;
width: 10px;
height: 10px;
pointer-events: none;
transform: rotate(45deg);
box-sizing: border-box;
/* left */
right: -5px;
bottom: 33px;
border-left-color: transparent;
border-bottom-color: transparent;
}
#maxkb .maxkb-tips .maxkb-title {
font-size: 20px;
font-weight: 500;
margin-bottom: 8px;
}
#maxkb .maxkb-tips .maxkb-button {
text-align: right;
margin-top: 24px;
}
#maxkb .maxkb-tips .maxkb-button button {
border-radius: 4px;
background: #fff;
padding: 3px 12px;
color: #3370ff;
cursor: pointer;
outline: none;
border: none;
}
#maxkb .maxkb-tips .maxkb-button button::after {
border: none;
}
#maxkb .maxkb-tips . {
position: absolute;
right: 20px;
top: 20px;
cursor: pointer;
}
#maxkb-chat-container {
width: 450px;
height: 600px;
display: none;
}
@media only screen and (max-width: 768px) {
#maxkb-chat-container {
width: 100%;
height: 70%;
right: 0 !important;
}
}
#maxkb .maxkb-chat-button {
position: fixed;
right: 0px;
bottom: 30px;
cursor: pointer;
z-index: 10000;
}
#maxkb #maxkb-chat-container {
z-index: 10000;
position: relative;
border-radius: 8px;
border: 1px solid #ffffff;
background: linear-gradient(188deg, rgba(235, 241, 255, 0.2) 39.6%, rgba(231, 249, 255, 0.2) 94.3%), #eff0f1;
box-shadow: 0px 4px 8px 0px rgba(31, 35, 41, 0.1);
position: fixed;
bottom: 16px;
right: 16px;
overflow: hidden;
}
#maxkb #maxkb-chat-container .maxkb-operate {
top: 18px;
right: 15px;
position: absolute;
display: flex;
align-items: center;
line-height: 18px;
}
#maxkb #maxkb-chat-container .maxkb-operate .maxkb-chat-close {
margin-left: 15px;
cursor: pointer;
}
#maxkb #maxkb-chat-container .maxkb-operate .maxkb-openviewport {
cursor: pointer;
}
#maxkb #maxkb-chat-container .maxkb-operate .maxkb-closeviewport {
cursor: pointer;
}
#maxkb #maxkb-chat-container .maxkb-viewportnone {
display: none;
}
#maxkb #maxkb-chat-container #maxkb-chat {
height: 100%;
width: 100%;
border: none;
}
#maxkb #maxkb-chat-container {
animation: appear 0.4s ease-in-out;
}
@keyframes appear {
from {
height: 0;
}
to {
height: 600px;
}
}
</style>

View File

@@ -19,6 +19,7 @@ export default {
"CodeEditor",
defineAsyncComponent(() => import("./code-editor/index.vue"))
);
app.component("PiContainer", PiContainer);
app.component("TextEditable", TextEditable);
app.component("FileInput", FileInput);

View File

@@ -15,8 +15,13 @@
<template v-if="sysPublic.icpNo">
<span>
<a href="https://beian.miit.gov.cn/" target="_blank">{{ sysPublic.icpNo }}</a>
<a-divider type="vertical" />
</span>
</template>
<span v-if="sysPublic.mpsNo">
<a href="http://www.beian.gov.cn/portal/registerSystemInfo" target="_blank">{{ sysPublic.mpsNo }}</a>
</span>
</div>
<div class="ml-5">v{{ version }}</div>
</div>
@@ -26,7 +31,7 @@ import { computed, onMounted, ref } from "vue";
import { useSettingStore } from "/@/store/settings";
defineOptions({
name: "PageFooter"
name: "PageFooter",
});
const version = ref(import.meta.env.VITE_APP_VERSION);

View File

@@ -20,8 +20,8 @@ const menus = computed(() => [
router.push("/certd/mine/user-profile");
},
icon: "fa-solid:book",
text: "账号信息"
}
text: "账号信息",
},
]);
const avatar = computed(() => {
@@ -42,7 +42,7 @@ const siteInfo = computed(() => {
return settingStore.siteInfo;
});
onErrorCaptured((e) => {
onErrorCaptured(e => {
console.error("ErrorCaptured:", e);
// notification.error({ message: e.message });
//阻止错误向上传递
@@ -69,6 +69,9 @@ onMounted(async () => {
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full">
<vip-button class="flex-center header-btn" mode="nav" />
</div>
<div class="hover:bg-accent ml-1 mr-2 cursor-pointer rounded-full">
<fs-icon icon="ion:logo-github" />
</div>
</template>
<template #footer>
<PageFooter></PageFooter>

View File

@@ -133,7 +133,7 @@ const asideCollapsed = ref(false);
function asideCollapsedToggle() {
asideCollapsed.value = !asideCollapsed.value;
}
onErrorCaptured((e) => {
onErrorCaptured(e => {
console.error("ErrorCaptured:", e);
// notification.error({ message: e.message });
//阻止错误向上传递

View File

@@ -31,6 +31,10 @@
<a-divider type="vertical" />
<a href="https://beian.miit.gov.cn/" target="_blank">{{ sysPublic.icpNo }}</a>
</span>
<span v-if="sysPublic.mpsNo">
<a-divider type="vertical" />
<a href="http://www.beian.gov.cn/portal/registerSystemInfo" target="_blank">{{ sysPublic.mpsNo }}</a>
</span>
</div>
</div>
</div>

View File

@@ -39,6 +39,7 @@ export type SysPublicSetting = {
limitUserPipelineCount?: number;
managerOtherUserPipeline?: boolean;
icpNo?: string;
mpsNo?: string;
robots?: boolean;
};
export type SuiteSetting = {

View File

@@ -18,7 +18,7 @@ import { useLayout } from "./hooks/use-layout";
interface Props extends VbenLayoutProps {}
defineOptions({
name: "VbenLayout"
name: "VbenLayout",
});
const props = withDefaults(defineProps<Props>(), {
@@ -48,7 +48,7 @@ const props = withDefaults(defineProps<Props>(), {
sideCollapseWidth: 60,
tabbarEnable: true,
tabbarHeight: 40,
zIndex: 200
zIndex: 200,
});
const emit = defineEmits<{ sideMouseLeave: []; toggleSidebar: [] }>();
@@ -193,7 +193,7 @@ const mainStyle = computed(() => {
}
return {
sidebarAndExtraWidth,
width
width,
};
});
@@ -221,7 +221,7 @@ const tabbarStyle = computed((): CSSProperties => {
return {
marginLeft: `${marginLeft}px`,
width
width,
};
});
@@ -231,7 +231,7 @@ const contentStyle = computed((): CSSProperties => {
const { footerEnable, footerFixed, footerHeight } = props;
return {
marginTop: fixed && !isFullContent.value && !headerIsHidden.value && (!isHeaderAutoMode.value || scrollY.value < headerWrapperHeight.value) ? `${headerWrapperHeight.value}px` : 0,
paddingBottom: `${footerEnable && footerFixed ? footerHeight : 0}px`
paddingBottom: `${footerEnable && footerFixed ? footerHeight : 0}px`,
};
});
@@ -249,7 +249,7 @@ const headerWrapperStyle = computed((): CSSProperties => {
position: fixed ? "fixed" : "static",
top: headerIsHidden.value || isFullContent.value ? `-${headerWrapperHeight.value}px` : 0,
width: mainStyle.value.width,
"z-index": headerZIndex.value
"z-index": headerZIndex.value,
};
});
@@ -289,13 +289,13 @@ const showHeaderLogo = computed(() => {
watch(
() => props.isMobile,
(val) => {
val => {
if (val) {
sidebarCollapse.value = true;
}
},
{
immediate: true
immediate: true,
}
);
@@ -305,7 +305,7 @@ watch(
setLayoutHeaderHeight(isFullContent.value ? 0 : height);
},
{
immediate: true
immediate: true,
}
);
@@ -315,7 +315,7 @@ watch(
setLayoutFooterHeight(height);
},
{
immediate: true
immediate: true,
}
);
@@ -336,7 +336,7 @@ watch(
mouseMove();
},
{
immediate: true
immediate: true,
}
);
}
@@ -433,9 +433,9 @@ const idMainContent = ELEMENT_ID_MAIN_CONTENT;
<div
:class="[
{
'shadow-[0_16px_24px_hsl(var(--background))]': scrollY > 20
'shadow-[0_16px_24px_hsl(var(--background))]': scrollY > 20,
},
SCROLL_FIXED_CLASS
SCROLL_FIXED_CLASS,
]"
:style="headerWrapperStyle"
class="overflow-hidden transition-all duration-200"

View File

@@ -35,7 +35,7 @@ interface Props {
/**
* 菜单数组
*/
menus?: Array<{ handler: AnyFunction; icon?: Component; text: string }>;
menus?: Array<{ handler: AnyFunction; icon?: Component | string; text: string }>;
/**
* 标签文本
@@ -52,7 +52,7 @@ interface Props {
}
defineOptions({
name: "UserDropdown"
name: "UserDropdown",
});
const props = withDefaults(defineProps<Props>(), {
@@ -64,7 +64,7 @@ const props = withDefaults(defineProps<Props>(), {
tagText: "",
text: "",
trigger: "click",
hoverDelay: 500
hoverDelay: 500,
});
const emit = defineEmits<{ logout: [] }>();
@@ -72,12 +72,12 @@ const emit = defineEmits<{ logout: [] }>();
const { globalLockScreenShortcutKey, globalLogoutShortcutKey } = usePreferences();
const lockStore = useLockStore();
const [LockModal, lockModalApi] = useVbenModal({
connectedComponent: LockScreenModal
connectedComponent: LockScreenModal,
});
const [LogoutModal, logoutModalApi] = useVbenModal({
onConfirm() {
handleSubmitLogout();
}
},
});
const refTrigger = useTemplateRef("refTrigger");
@@ -86,7 +86,7 @@ const [openPopover, hoverWatcher] = useHoverToggle([refTrigger, refContent], ()
watch(
() => props.trigger === "hover" || props.trigger === "both",
(val) => {
val => {
if (val) {
hoverWatcher.enable();
} else {
@@ -94,7 +94,7 @@ watch(
}
},
{
immediate: true
immediate: true,
}
);

View File

@@ -255,7 +255,7 @@
</template>
<script lang="ts">
import { defineComponent, onMounted, provide, Ref, ref, watch } from "vue";
import { defineComponent, onMounted, onUnmounted, provide, Ref, ref, watch } from "vue";
import { useRouter } from "vue-router";
import PiTaskForm from "./component/task-form/index.vue";
import PiTriggerForm from "./component/trigger-form/index.vue";
@@ -274,6 +274,7 @@ import { useSettingStore } from "/@/store/settings";
import { useUserStore } from "/@/store/user";
import TaskShortcuts from "./component/shortcut/task-shortcuts.vue";
import { eachSteps, findStep } from "../utils";
import { PluginGroups } from "/@/store/plugin";
export default defineComponent({
name: "PipelineEdit",
@@ -365,9 +366,14 @@ export default defineComponent({
return true;
}
const intervalLoadHistoryRef = ref();
const isLoadingHistory = ref(false);
function watchNewHistoryList() {
intervalLoadHistoryRef.value = setTimeout(async () => {
intervalLoadHistoryRef.value = setInterval(async () => {
if (isLoadingHistory.value) {
return;
}
try {
isLoadingHistory.value = true;
if (currentHistory.value == null) {
await loadHistoryList();
}
@@ -375,16 +381,21 @@ export default defineComponent({
if (currentHistory.value != null) {
if (currentHistory.value.pipeline?.status?.status === "start") {
await loadCurrentHistoryDetail();
} else {
return;
}
}
} catch (e) {
console.error(e);
} finally {
isLoadingHistory.value = false;
}
watchNewHistoryList();
}, 3000);
}
onMounted(() => {
watchNewHistoryList();
});
onUnmounted(() => {
clearInterval(intervalLoadHistoryRef.value);
});
watch(
() => {
@@ -632,7 +643,6 @@ export default defineComponent({
async onOk() {
//@ts-ignore
await changeCurrentHistory(null);
watchNewHistoryList();
await props.options.doTrigger({ pipelineId: pipeline.value.id, stepId: stepId });
notification.success({ message: "管道已经开始运行" });
},
@@ -1081,7 +1091,7 @@ export default defineComponent({
}
.layout-right {
position: "relative";
position: relative;
&.collapsed {
margin-right: -350px;
}

View File

@@ -4,6 +4,9 @@
<a-form-item label="ICP备案号" :name="['public', 'icpNo']">
<a-input v-model:value="formState.public.icpNo" placeholder="粤ICP备xxxxxxx号" />
</a-form-item>
<a-form-item label="网安备案号" :name="['public', 'mpsNo']">
<a-input v-model:value="formState.public.mpsNo" placeholder="京公网安备xxxxxxx号" />
</a-form-item>
<a-form-item label="允许爬虫" :name="['public', 'robots']">
<a-switch v-model:checked="formState.public.robots" />
@@ -53,19 +56,20 @@ import { notification } from "ant-design-vue";
import { util } from "/@/utils";
defineOptions({
name: "SettingBase"
name: "SettingBase",
});
const formState = reactive<Partial<SysSettings>>({
public: {
icpNo: ""
icpNo: "",
mpsNo: "",
},
private: {}
private: {},
});
const urlRules = ref({
type: "url",
message: "请输入正确的URL"
message: "请输入正确的URL",
});
async function loadSysSettings() {
@@ -82,7 +86,7 @@ const onFinish = async (form: any) => {
await api.SysSettingsSave(form);
await settingsStore.loadSysSettings();
notification.success({
message: "保存成功"
message: "保存成功",
});
} finally {
saveLoading.value = false;
@@ -96,7 +100,7 @@ const onFinishFailed = (errorInfo: any) => {
async function stopOtherUserTimer() {
await api.stopOtherUserTimer();
notification.success({
message: "停止成功"
message: "停止成功",
});
}
@@ -120,13 +124,13 @@ async function testProxy() {
if (!success) {
notification.error({
message: "测试失败",
description: content
description: content,
});
return;
}
notification.success({
message: "测试完成",
description: content
description: content,
});
} finally {
testProxyLoading.value = false;

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.2](https://github.com/certd/certd/compare/v1.34.1...v1.34.2) (2025-05-11)
### Bug Fixes
* 修复部署到又拍云强制https无效的bug ([2397097](https://github.com/certd/certd/commit/2397097e4ddcb6f593210598e8779ffd44ac3f8f))
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
### Bug Fixes

View File

@@ -0,0 +1,107 @@
import "./dist/plugins/index.js";
import { accessRegistry, notificationRegistry, pluginGroups, pluginRegistry } from "@certd/pipeline";
import { dnsProviderRegistry } from "@certd/plugin-cert";
import fs from "fs";
function genPluginMd() {
const plugins = {
access: [],
deploy: [],
dnsProvider: [],
notification: []
};
plugins.access = accessRegistry.getDefineList();
plugins.deploy = pluginRegistry.getDefineList();
plugins.dnsProvider = dnsProviderRegistry.getDefineList();
plugins.notification = notificationRegistry.getDefineList();
// function genMd(list) {
// let mdContent = `<table style='width:100%'>
// <thead style='width:100%'>
// <tr >
// <th width='70'>序号</th><th width='265'>名称</th><th>说明</th>
// </tr>
// </thead>
// <tbody>
// `;
// let i = 0;
// for (const x of list) {
// i++
// mdContent += `<tr> <td>${i}.</td> <td style='font-weight: bold'>${x.title}</td> <td>${x.desc||''}</td> </tr>`;
// }
// mdContent += `</tbody></table>`;
// return mdContent;
// }
// function genMd(list) {
// let mdContent = ``;
// let i = 0;
// for (const x of list) {
// i++
// mdContent += `${i}. **${x.title}** \n${x.desc||''} \n\n\n`;
// }
// return mdContent;
// }
function genMd(list) {
let mdContent = `
| 序号 | 名称 | 说明 |
|-----|-----|-----|
`;
let i = 0;
for (const x of list) {
i++
mdContent += `| ${i}.| **${x.title}** | ${x.desc||''} | \n`;
}
return mdContent;
}
function addTableStyle(){
return `
<style module>
table th:first-of-type {
width: 65px;
}
table th:nth-of-type(2) {
width: 240px;
}
</style>
`
}
let mdContent = "";
mdContent = "# 授权列表\n";
mdContent += genMd(plugins.access);
mdContent += addTableStyle()
fs.writeFileSync("../../../docs/guide/plugins/access.md", mdContent);
mdContent = "# DNS提供商\n";
mdContent += genMd(plugins.dnsProvider);
mdContent += addTableStyle()
fs.writeFileSync("../../../docs/guide/plugins/dns-provider.md", mdContent);
mdContent = "# 通知插件\n";
mdContent += genMd(plugins.notification);
mdContent += addTableStyle()
fs.writeFileSync("../../../docs/guide/plugins/notification.md", mdContent);
mdContent = "# 任务插件\n";
mdContent += `\`${plugins.deploy.length}\` 款任务插件 \n`
let index =0
for (const key in pluginGroups) {
index++
const group = pluginGroups[key];
mdContent += `## ${index}. ${group.title}\n`;
mdContent += genMd(group.plugins);
}
mdContent += addTableStyle()
fs.writeFileSync("../../../docs/guide/plugins/deploy.md", mdContent);
process.exit()
}
genPluginMd()

View File

@@ -50,49 +50,52 @@ export default async function loadModules(dir) {
function isPrototypeOf(value,cls){
return cls.prototype.isPrototypeOf(value.prototype)
}
async function genMetadata(){
const modules = await loadModules('./dist/plugins');
const modules = await loadModules('./dist/plugins');
fs.rmSync("./metadata", { recursive: true });
fs.mkdirSync("./metadata", { recursive: true });
for (const key in modules) {
console.log(key)
const module = modules[key]
const entry = Object.entries(module)
for (const [name, value] of entry) {
//如果有define属性
if(value.define){
//那么就是插件
let location = key.substring(4)
location = location.substring(0, location.length - 3)
location = location.replaceAll("\\","/")
location += ".js"
location = `../../..${location}` // 从modules/plugin/plugin-service 加载 ../../plugins目录下的文件
fs.rmSync("./metadata", { recursive: true });
fs.mkdirSync("./metadata", { recursive: true });
for (const key in modules) {
console.log(key)
const module = modules[key]
const entry = Object.entries(module)
for (const [name, value] of entry) {
//如果有define属性
if(value.define){
//那么就是插件
let location = key.substring(4)
location = location.substring(0, location.length - 3)
location = location.replaceAll("\\","/")
location += ".js"
location = `../../..${location}` // 从modules/plugin/plugin-service 加载 ../../plugins目录下的文件
const pluginDefine = {
...value.define
}
pluginDefine.type = "builtIn"
if(pluginDefine.accessType){
pluginDefine.pluginType = "dnsProvider"
}else if(isPrototypeOf(value,AbstractTaskPlugin)){
pluginDefine.pluginType = "deploy"
}else if(isPrototypeOf(value,BaseNotification)){
pluginDefine.pluginType = "notification"
}else if(isPrototypeOf(value,BaseAccess)){
pluginDefine.pluginType = "access"
}else{
console.log(`[warning] 未知的插件类型:${pluginDefine.name}`)
}
const pluginDefine = {
...value.define
const filePath = path.join(`./metadata/${pluginDefine.pluginType}_${pluginDefine.name}.yaml`)
pluginDefine.scriptFilePath = location
const data = yaml.dump(pluginDefine)
fs.writeFileSync(filePath,data ,'utf8')
}
pluginDefine.type = "builtIn"
if(pluginDefine.accessType){
pluginDefine.pluginType = "dnsProvider"
}else if(isPrototypeOf(value,AbstractTaskPlugin)){
pluginDefine.pluginType = "deploy"
}else if(isPrototypeOf(value,BaseNotification)){
pluginDefine.pluginType = "notification"
}else if(isPrototypeOf(value,BaseAccess)){
pluginDefine.pluginType = "access"
}else{
console.log(`[warning] 未知的插件类型:${pluginDefine.name}`)
}
const filePath = path.join(`./metadata/${pluginDefine.pluginType}_${pluginDefine.name}.yaml`)
pluginDefine.scriptFilePath = location
const data = yaml.dump(pluginDefine)
fs.writeFileSync(filePath,data ,'utf8')
}
}
process.exit()
}
// import why from 'why-is-node-running'
// setTimeout(() => why(), 100); // 延迟打印原因
genMetadata()
process.exit()

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/ui-server",
"version": "1.34.1",
"version": "1.34.2",
"description": "fast-server base midway",
"private": true,
"type": "module",
@@ -22,6 +22,7 @@
"ci": "npm run cov",
"build": "mwtsc --cleanOutDir --skipLibCheck",
"export-metadata": "node export-plugin-yaml.js",
"export-md": "node export-plugin-md.js",
"dev-build": "echo 1",
"build-on-docker": "node ./before-build.js && npm run build",
"up-mw-deps": "npx midway-version -u -w",
@@ -40,19 +41,19 @@
"@aws-sdk/client-acm": "^3.699.0",
"@aws-sdk/client-cloudfront": "^3.699.0",
"@aws-sdk/client-s3": "^3.705.0",
"@certd/acme-client": "^1.34.1",
"@certd/basic": "^1.34.1",
"@certd/commercial-core": "^1.34.1",
"@certd/jdcloud": "^1.34.1",
"@certd/lib-huawei": "^1.34.1",
"@certd/lib-k8s": "^1.34.1",
"@certd/lib-server": "^1.34.1",
"@certd/midway-flyway-js": "^1.34.1",
"@certd/pipeline": "^1.34.1",
"@certd/plugin-cert": "^1.34.1",
"@certd/plugin-lib": "^1.34.1",
"@certd/plugin-plus": "^1.34.1",
"@certd/plus-core": "^1.34.1",
"@certd/acme-client": "^1.34.2",
"@certd/basic": "^1.34.2",
"@certd/commercial-core": "^1.34.2",
"@certd/jdcloud": "^1.34.2",
"@certd/lib-huawei": "^1.34.2",
"@certd/lib-k8s": "^1.34.2",
"@certd/lib-server": "^1.34.2",
"@certd/midway-flyway-js": "^1.34.2",
"@certd/pipeline": "^1.34.2",
"@certd/plugin-cert": "^1.34.2",
"@certd/plugin-lib": "^1.34.2",
"@certd/plugin-plus": "^1.34.2",
"@certd/plus-core": "^1.34.2",
"@corsinvest/cv4pve-api-javascript": "^8.3.0",
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",

View File

@@ -58,7 +58,7 @@ export class Dns51Client {
method: "get",
withCredentials: true,
logRes: false,
returnResponse: true,
returnOriginRes: true,
headers: {
// 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36',
'Origin': 'https://www.51dns.com',
@@ -90,7 +90,7 @@ export class Dns51Client {
},
withCredentials: true,
logRes: false,
returnResponse: true,
returnOriginRes: true,
headers: {
'Origin': 'https://www.51dns.com',
'Referer': 'https://www.51dns.com',
@@ -117,7 +117,7 @@ export class Dns51Client {
method: 'get',
withCredentials: true,
logRes: false,
returnResponse: true,
returnOriginRes: true,
headers: {
// 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.6261.95 Safari/537.36',
'Origin': 'https://www.51dns.com',
@@ -144,7 +144,7 @@ export class Dns51Client {
method: "get",
withCredentials: true,
logRes: false,
returnResponse: true,
returnOriginRes: true,
headers: this.getRequestHeaders()
});

View File

@@ -11,6 +11,7 @@ import { AliyunAccess, AliyunClient } from '@certd/plugin-lib';
order:0,
})
export class AliyunDnsProvider extends AbstractDnsProvider {
client: any;
async onInstance() {
const access: AliyunAccess = this.ctx.access as AliyunAccess

View File

@@ -1,22 +1,21 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { AliyunAccess, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
import { CertApplyPluginNames} from '@certd/plugin-cert';
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { AliyunAccess, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
@IsTaskPlugin({
name: 'AliyunDeployCertToFC',
title: '阿里云-部署至阿里云FC(3.0)',
icon: 'svg:icon-aliyun',
group: pluginGroups.aliyun.key,
desc: '部署证书到阿里云函数计算FC3.0,【注意】证书的加密算法必须选择【pkcs1旧版】',
needPlus: true,
needPlus: false,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class AliyunDeployCertToFC extends AbstractPlusTaskPlugin {
export class AliyunDeployCertToFC extends AbstractTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择证书申请任务输出的域名证书',

View File

@@ -1,22 +1,27 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { AliyunAccess, AliyunClient, AliyunSslClient, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
import { CertApplyPluginNames} from '@certd/plugin-cert';
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import {
AliyunAccess,
AliyunClient,
AliyunSslClient,
createCertDomainGetterInputDefine,
createRemoteSelectInputDefine
} from "@certd/plugin-lib";
@IsTaskPlugin({
name: 'AliyunDeployCertToWaf',
title: '阿里云-部署至阿里云WAF',
icon: 'svg:icon-aliyun',
group: pluginGroups.aliyun.key,
desc: '部署证书到阿里云WAF',
needPlus: true,
needPlus: false,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class AliyunDeployCertToWaf extends AbstractPlusTaskPlugin {
export class AliyunDeployCertToWaf extends AbstractTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择证书申请任务输出的域名证书\n或者选择前置任务“上传证书到阿里云”任务的证书ID可以减少上传到阿里云的证书数量',

View File

@@ -1,25 +1,24 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { AwsAccess, AwsRegions } from '../access.js';
import { AwsAcmClient } from '../libs/aws-acm-client.js';
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
import { optionsUtils } from '@certd/basic/dist/utils/util.options.js';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
import { CertApplyPluginNames} from '@certd/plugin-cert';
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { AwsAccess, AwsRegions } from "../access.js";
import { AwsAcmClient } from "../libs/aws-acm-client.js";
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
import { optionsUtils } from "@certd/basic/dist/utils/util.options.js";
@IsTaskPlugin({
name: 'AwsDeployToCloudFront',
title: 'AWS-部署证书到CloudFront',
desc: '部署证书到 AWS CloudFront',
icon: 'svg:icon-aws',
group: pluginGroups.aws.key,
needPlus: true,
needPlus: false,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class AwsDeployToCloudFront extends AbstractPlusTaskPlugin {
export class AwsDeployToCloudFront extends AbstractTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',

View File

@@ -1,22 +1,21 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { CertInfo } from '@certd/plugin-cert';
import { createRemoteSelectInputDefine, TencentAccess, TencentSslClient } from '@certd/plugin-lib';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
import { CertApplyPluginNames} from '@certd/plugin-cert';
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { createRemoteSelectInputDefine, TencentAccess, TencentSslClient } from "@certd/plugin-lib";
@IsTaskPlugin({
name: 'TencentDeployCertToLive',
title: '腾讯云-部署到腾讯云直播',
icon: 'svg:icon-tencentcloud',
desc: 'https://console.cloud.tencent.com/live/',
group: pluginGroups.tencent.key,
needPlus: true,
needPlus: false,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class TencentDeployCertToLive extends AbstractPlusTaskPlugin {
export class TencentDeployCertToLive extends AbstractTaskPlugin {
@TaskInput({
title: 'Access提供者',
helper: 'access 授权',

View File

@@ -1,13 +1,13 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
import { utils } from '@certd/basic';
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { utils } from "@certd/basic";
import dayjs from "dayjs";
import { CertApplyPluginNames } from "@certd/plugin-cert";
import dayjs from 'dayjs';
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
import { CertApplyPluginNames} from '@certd/plugin-cert';
@IsTaskPlugin({
name: 'DeployCertToTencentTKEIngress',
title: '腾讯云-部署到TKE-ingress',
needPlus: true,
needPlus: false,
icon: 'svg:icon-tencentcloud',
group: pluginGroups.tencent.key,
desc: 'serverless集群请使用K8S部署插件Qcloud类型需要【上传到腾讯云】作为前置任务ApiServer未开启外网访问则需要做域名的内网IP映射',
@@ -17,7 +17,7 @@ import { CertApplyPluginNames} from '@certd/plugin-cert';
},
},
})
export class DeployCertToTencentTKEIngressPlugin extends AbstractPlusTaskPlugin {
export class DeployCertToTencentTKEIngressPlugin extends AbstractTaskPlugin {
@TaskInput({ title: '大区', value: 'ap-guangzhou', required: true })
region!: string;

View File

@@ -41,7 +41,7 @@ export class UpyunClient {
password: access.password
},
logRes: false,
returnResponse: true
returnOriginRes: true
});
if (res.data?.errors?.length > 0) {
throw new Error(JSON.stringify(res.data.msg));

View File

@@ -1,9 +1,7 @@
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { CertInfo } from "@certd/plugin-cert";
import { AbstractPlusTaskPlugin } from "@certd/plugin-plus";
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { UpyunAccess } from "../access.js";
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
import { CertApplyPluginNames } from "@certd/plugin-cert";
import { optionsUtils } from "@certd/basic/dist/utils/util.options.js";
import { UpyunClient } from "../client.js";
@@ -15,7 +13,7 @@ import { UpyunClient } from "../client.js";
desc:"支持又拍云CDN又拍云云存储USS",
//插件分组
group: pluginGroups.cdn.key,
needPlus: true,
needPlus: false,
default: {
//默认值配置照抄即可
strategy: {
@@ -24,7 +22,7 @@ import { UpyunClient } from "../client.js";
}
})
//类名规范跟上面插件名称name一致
export class UpyunDeployToCdn extends AbstractPlusTaskPlugin {
export class UpyunDeployToCdn extends AbstractTaskPlugin {
//证书选择,此项必须要有
@TaskInput({
title: "域名证书",
@@ -115,24 +113,26 @@ export class UpyunDeployToCdn extends AbstractPlusTaskPlugin {
for (const item of this.cdnList) {
const data :any= {
crt_id: certId,
domain_name: item,
certificate_id: certId,
domain: item,
}
if (this.forceHttps !== 'keep') {
data.force_https = Boolean(this.forceHttps);
}
if (this.https !=='keep') {
data.https = Boolean(this.https);
}
this.logger.info(`开始部署证书:${item}`);
const res = await upyunClient.doRequest({
cookie: cookie,
url: "https://console.upyun.com/api/https/migrate/domain",
url: "https://console.upyun.com/api/https/certificate/manager",
method: "POST",
data: data
});
this.logger.info(`部署成功:${JSON.stringify(res)}`);
}
this.logger.info("部署成功");