Compare commits

...

64 Commits

Author SHA1 Message Date
xiaojunnuo
a1e504c138 v1.34.2 2025-05-11 20:27:04 +08:00
xiaojunnuo
4cc413047c build: prepare to build 2025-05-11 20:23:52 +08:00
xiaojunnuo
2397097e4d fix: 修复部署到又拍云强制https无效的bug 2025-05-11 12:29:04 +08:00
xiaojunnuo
c88f959ec9 chore: 2025-05-11 10:55:45 +08:00
xiaojunnuo
0b2e28b62d fix: 修复刷新流水线页面后,日志不自动更新的bug 2025-05-11 10:55:01 +08:00
xiaojunnuo
c7f2ead696 chore: doc 2025-05-11 10:22:10 +08:00
xiaojunnuo
b454e02d01 chore: doc 2025-05-11 10:04:54 +08:00
xiaojunnuo
47df2ffc3e chore: doc 2025-05-10 22:05:21 +08:00
xiaojunnuo
d18e431e2f perf: 支持设置网安备案号 2025-05-10 21:31:32 +08:00
xiaojunnuo
0a147d2db7 chore: 2025-05-10 20:52:23 +08:00
xiaojunnuo
ccdc933064 chore: 2025-05-10 17:29:10 +08:00
xiaojunnuo
023f2d4569 Merge remote-tracking branch 'origin/v2-dev' into v2-dev 2025-05-10 15:05:28 +08:00
xiaojunnuo
06a7371d2b chore: 2025-05-10 15:04:57 +08:00
xiaojunnuo
626f5d3487 chore: 2025-05-10 13:58:08 +08:00
xiaojunnuo
8cd3b9fe2e Merge remote-tracking branch 'origin/v2-dev' into v2-dev 2025-05-09 18:51:39 +08:00
xiaojunnuo
716c35d52a chore: doc 2025-05-09 18:51:08 +08:00
xiaojunnuo
8cc0f3918b chore: 文档增加插件列表 2025-05-08 23:47:50 +08:00
xiaojunnuo
98b51f0799 chore: 文档增加插件列表 2025-05-08 23:27:46 +08:00
xiaojunnuo
81d6dad548 chore: 2025-05-08 10:29:42 +08:00
xiaojunnuo
41bc11cf96 chore: 2025-05-08 10:29:25 +08:00
xiaojunnuo
721dbe415a chore: 2025-05-08 10:28:44 +08:00
xiaojunnuo
f5c0b51428 chore: 2025-05-08 10:27:49 +08:00
xiaojunnuo
892c6ad80c chore: 2025-05-08 10:23:47 +08:00
xiaojunnuo
a47805e494 chore: 2025-05-08 10:22:31 +08:00
xiaojunnuo
9dd49054d1 perf: 集成智能问答机器人 2025-05-07 14:15:32 +08:00
xiaojunnuo
f5d1d1a0b7 chore: 1 2025-05-06 17:52:39 +08:00
xiaojunnuo
d75fcb7fec perf: http方式支持校验443端口 2025-05-06 17:01:20 +08:00
xiaojunnuo
826be45b6a chore: 等待解析生效时长可自定义 2025-05-06 11:04:02 +08:00
xiaojunnuo
d35d9c17c5 chore: doc 2025-05-06 10:57:07 +08:00
xiaojunnuo
638c9720cf chore: 1.34.1 2025-05-06 00:32:38 +08:00
xiaojunnuo
08a190882f build: trigger build image 2025-05-06 00:32:25 +08:00
xiaojunnuo
bfa7530a39 chore: 1.34.1 2025-05-06 00:32:11 +08:00
xiaojunnuo
6c74148c27 v1.34.1 2025-05-06 00:21:50 +08:00
xiaojunnuo
480cad0fed build: prepare to build 2025-05-06 00:16:10 +08:00
xiaojunnuo
32be489136 chore: parseDomain优化 2025-05-06 00:14:17 +08:00
xiaojunnuo
11801d8e2e chore: 2025-05-05 22:20:42 +08:00
xiaojunnuo
1b280a2940 fix: 根据SOA记录判断子域名托管有缺陷,改回手动配置子域名托管记录的方式 2025-05-05 21:43:39 +08:00
xiaojunnuo
424890a1e1 chore: safePromise 优化 2025-04-30 09:38:44 +08:00
xiaojunnuo
5f85219495 perf: 支持部署证书到火山dcdn 2025-04-29 18:40:13 +08:00
xiaojunnuo
a63d687f1c perf: 支持部署证书到unicloud 2025-04-29 13:36:24 +08:00
xiaojunnuo
f2d6c3ad83 chore: 移除 autowire 2025-04-28 23:34:08 +08:00
xiaojunnuo
0b6941d5ce chore: 移除autowire特性 2025-04-28 21:55:23 +08:00
xiaojunnuo
048696ee93 fix: 修复宝塔授权测试按钮显示错误的bug 2025-04-28 21:33:07 +08:00
xiaojunnuo
27a405fb1d build: publish 2025-04-28 17:29:04 +08:00
xiaojunnuo
e2cf65b591 build: trigger build image 2025-04-28 17:28:45 +08:00
xiaojunnuo
9749fc817d v1.34.0 2025-04-28 17:27:05 +08:00
xiaojunnuo
e6600f2c43 build: prepare to build 2025-04-28 17:24:37 +08:00
xiaojunnuo
a664931e7a build: prepare to build 2025-04-28 17:14:39 +08:00
xiaojunnuo
a2ba965600 chore: 2025-04-28 17:12:25 +08:00
xiaojunnuo
65255dbb50 chore: 恢复加载插件方式为注解方式 2025-04-28 17:05:14 +08:00
xiaojunnuo
a5cb8761a5 build: prepare to build 2025-04-28 16:58:59 +08:00
xiaojunnuo
e3930e0717 fix: 修复二次认证登录进入错误账号的bug 2025-04-28 16:57:30 +08:00
xiaojunnuo
afd59e9933 perf: 优化cdnfly插件,支持自动匹配域名部署 2025-04-28 00:55:39 +08:00
xiaojunnuo
8087524bef chore: product info 2025-04-27 22:51:47 +08:00
xiaojunnuo
605440812f Merge remote-tracking branch 'refs/remotes/origin/v2-dev-plugin-yaml' into v2-dev
# Conflicts:
#	packages/ui/certd-server/export-plugin-yaml.js
2025-04-27 22:50:19 +08:00
xiaojunnuo
b10c6eb615 Merge branch 'v2-dev-yaml' into v2-dev 2025-04-27 21:35:36 +08:00
xiaojunnuo
a96264ff6a chore: 2025-04-27 15:50:38 +08:00
xiaojunnuo
deb3893820 feat: 从yaml文件注册插件 2025-04-27 15:11:50 +08:00
xiaojunnuo
9b1d822b5b chore: 2025-04-27 11:05:01 +08:00
xiaojunnuo
5cd61c4c02 build: publish 2025-04-27 01:58:54 +08:00
xiaojunnuo
586fa70eac build: trigger build image 2025-04-27 01:58:37 +08:00
xiaojunnuo
e5a7ada3cf Merge branch 'v2' into v2-dev-yaml 2025-04-15 21:19:39 +08:00
xiaojunnuo
b76fdd7fe4 Merge branch 'v2-dev' into v2-dev-yaml 2025-04-15 21:19:36 +08:00
xiaojunnuo
91ffb0820a chore: plugin元数据换成yaml格式 2025-04-15 00:16:57 +08:00
236 changed files with 7340 additions and 715 deletions

View File

@@ -3,6 +3,45 @@
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
* 根据SOA记录判断子域名托管有缺陷改回手动配置子域名托管记录的方式 ([1b280a2](https://github.com/certd/certd/commit/1b280a2940f9e2d919b0bf23b89cc185be1fa498))
* 修复宝塔授权测试按钮显示错误的bug ([048696e](https://github.com/certd/certd/commit/048696ee9386491bb68592fb3a47d1c900bb68bf))
### Performance Improvements
* 支持部署证书到火山dcdn ([5f85219](https://github.com/certd/certd/commit/5f852194953dc1b4e6336770f417507b8f5a33ad))
* 支持部署证书到unicloud ([a63d687](https://github.com/certd/certd/commit/a63d687f1c573159f0857693f37602b0e1e44072))
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
### Bug Fixes
* 修复二次认证登录进入错误账号的bug ([e3930e0](https://github.com/certd/certd/commit/e3930e07172dd7903cb0f6ff26e0e3e828ba3e77))
### Features
* 从yaml文件注册插件 ([deb3893](https://github.com/certd/certd/commit/deb38938204b29543f36d3266249958faaaa6b66))
### Performance Improvements
* 优化cdnfly插件支持自动匹配域名部署 ([afd59e9](https://github.com/certd/certd/commit/afd59e9933b2650f41c5d47684c171b93b962065))
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
### 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 @@
22:32
00:32

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,50 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.34.1](https://github.com/certd/certd/compare/v1.34.0...v1.34.1) (2025-05-05)
### Bug Fixes
* 根据SOA记录判断子域名托管有缺陷改回手动配置子域名托管记录的方式 ([1b280a2](https://github.com/certd/certd/commit/1b280a2940f9e2d919b0bf23b89cc185be1fa498))
* 修复宝塔授权测试按钮显示错误的bug ([048696e](https://github.com/certd/certd/commit/048696ee9386491bb68592fb3a47d1c900bb68bf))
### Performance Improvements
* 支持部署证书到火山dcdn ([5f85219](https://github.com/certd/certd/commit/5f852194953dc1b4e6336770f417507b8f5a33ad))
* 支持部署证书到unicloud ([a63d687](https://github.com/certd/certd/commit/a63d687f1c573159f0857693f37602b0e1e44072))
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
### Bug Fixes
* 修复二次认证登录进入错误账号的bug ([e3930e0](https://github.com/certd/certd/commit/e3930e07172dd7903cb0f6ff26e0e3e828ba3e77))
### Features
* 从yaml文件注册插件 ([deb3893](https://github.com/certd/certd/commit/deb38938204b29543f36d3266249958faaaa6b66))
### Performance Improvements
* 优化cdnfly插件支持自动匹配域名部署 ([afd59e9](https://github.com/certd/certd/commit/afd59e9933b2650f41c5d47684c171b93b962065))
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
### Bug Fixes
* 服务器时间获取不准确的bug ([5d10cbf](https://github.com/certd/certd/commit/5d10cbf18daf94a90a7551641a3b13e3c5fec611))
* 修复复制流水线无效的bug ([3df20a9](https://github.com/certd/certd/commit/3df20a924f32970b052e2588ea20de095f0ea693))
* 修复http上传方式无法清除记录文件的bug ([72a7b51](https://github.com/certd/certd/commit/72a7b51d479602b2c54c6c3ac8d8a0dcb9664e73))
* 修复token过期后疯狂打印token过期信息的bug ([50a5fa1](https://github.com/certd/certd/commit/50a5fa15bb240a125bbc91d2ce1ff3c835888a77))
### Performance Improvements
* 从域名的soa获取主域名子域名托管无需额外配置 ([a586a92](https://github.com/certd/certd/commit/a586a92d5e32ea846ac37be52a7ad8c328d89966))
* 七牛oss支持删除过期备份 ([b7113bd](https://github.com/certd/certd/commit/b7113bda2378116d6c116dc583f563cce7cf9f00))
* 数据库备份支持oss ([308d460](https://github.com/certd/certd/commit/308d4600efe2002f199c33b4594d3071784e58ea))
* 支持阿里云中文域名申请 ([b3468cf](https://github.com/certd/certd/commit/b3468cf7f28228d7c9cf68de6b5a9bbeb67f2c6d))
* 支持反向代理增加contextPath路径 ([0088929](https://github.com/certd/certd/commit/0088929622160cc922995de9a563e8061686ff34))
* 支持中文域名 ([162ebfd](https://github.com/certd/certd/commit/162ebfd4e0c25727efb33952d3bbf7420a02e2c3))
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
### Performance Improvements

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.33.8"
"version": "1.34.2"
}

View File

@@ -30,7 +30,8 @@
"init": "lerna run build",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
"docs:preview": "vitepress preview docs",
"pub": "echo 1"
},
"license": "AGPL-3.0",
"dependencies": {

View File

@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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
* 根据SOA记录判断子域名托管有缺陷改回手动配置子域名托管记录的方式 ([1b280a2](https://github.com/publishlab/node-acme-client/commit/1b280a2940f9e2d919b0bf23b89cc185be1fa498))
# [1.34.0](https://github.com/publishlab/node-acme-client/compare/v1.33.8...v1.34.0) (2025-04-28)
**Note:** Version bump only for package @certd/acme-client
## [1.33.8](https://github.com/publishlab/node-acme-client/compare/v1.33.7...v1.33.8) (2025-04-26)
### Bug Fixes

View File

@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.33.8",
"version": "1.34.2",
"type": "module",
"module": "scr/index.js",
"main": "src/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.33.8",
"@certd/basic": "^1.34.2",
"@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5",
"axios": "^1.7.2",
@@ -51,7 +51,8 @@
"lint": "eslint .",
"lint-types": "tsd",
"prepublishOnly": "npm run build-docs",
"test": "mocha -t 60000 \"test/setup.js\" \"test/**/*.spec.js\""
"test": "mocha -t 60000 \"test/setup.js\" \"test/**/*.spec.js\"",
"pub": "npm publish"
},
"repository": {
"type": "git",
@@ -68,5 +69,5 @@
"bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues"
},
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
}

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

@@ -0,0 +1,11 @@
import {assert} from 'chai'
import {resolveDomainBySoaRecord} from "../src/util.js"
describe('dns', () => {
it('resolveDomainBySoaRecord', async () => {
const resp = await resolveDomainBySoaRecord("a.corp.smartdeer.com")
assert.equal(resp, "smartdeer.com")
});
})

View File

@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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
* 支持部署证书到火山dcdn ([5f85219](https://github.com/certd/certd/commit/5f852194953dc1b4e6336770f417507b8f5a33ad))
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
**Note:** Version bump only for package @certd/basic
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
**Note:** Version bump only for package @certd/basic

View File

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

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.33.8",
"version": "1.34.2",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -12,7 +12,8 @@
"build": "npm run before-build && tsc --skipLibCheck",
"dev-build": "npm run build",
"preview": "vite preview",
"test": "mocha --loader=ts-node/esm"
"test": "mocha --loader=ts-node/esm",
"pub": "npm publish"
},
"dependencies": {
"axios": "^1.7.2",
@@ -44,5 +45,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
}

View File

@@ -1,4 +1,4 @@
import { domainUtils } from './util.domain.js';
import { domainUtils } from "./util.domain.js";
function groupByDomain(options: any[], inDomains: string[]) {
const matched = [];
@@ -19,16 +19,16 @@ function groupByDomain(options: any[], inDomains: string[]) {
function buildGroupOptions(options: any[], inDomains: string[]) {
const grouped = groupByDomain(options, inDomains);
const groupOptions = [];
groupOptions.push({ value: 'matched', disabled: true, label: '----已匹配----' });
groupOptions.push({ value: "matched", disabled: true, label: "----已匹配----" });
if (grouped.matched.length === 0) {
options.push({ value: '', disabled: true, label: '没有可以匹配的域名' });
options.push({ value: "", disabled: true, label: "没有可以匹配的域名" });
} else {
for (const matched of grouped.matched) {
groupOptions.push(matched);
}
}
if (grouped.notMatched.length > 0) {
groupOptions.push({ value: 'unmatched', disabled: true, label: '----未匹配----' });
groupOptions.push({ value: "unmatched", disabled: true, label: "----未匹配----" });
for (const notMatched of grouped.notMatched) {
groupOptions.push(notMatched);
}

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,18 @@
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
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
**Note:** Version bump only for package @certd/pipeline
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
**Note:** Version bump only for package @certd/pipeline

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.33.8",
"version": "1.34.2",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -13,11 +13,12 @@
"dev-build": "npm run build",
"build3": "rollup -c",
"preview": "vite preview",
"test": "mocha --loader=ts-node/esm"
"test": "mocha --loader=ts-node/esm",
"pub": "npm publish"
},
"dependencies": {
"@certd/basic": "^1.33.8",
"@certd/plus-core": "^1.33.8",
"@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"
@@ -43,5 +44,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
}

View File

@@ -1,17 +0,0 @@
import { Decorator } from "./index.js";
export type AutowireProp = {
name?: string;
type?: any;
};
export const AUTOWIRE_KEY = "pipeline:autowire";
export function Autowire(props?: AutowireProp): PropertyDecorator {
return (target, propertyKey) => {
const _type = Reflect.getMetadata("design:type", target, propertyKey);
target = Decorator.target(target, propertyKey);
props = props || {};
props.type = _type;
Reflect.defineMetadata(AUTOWIRE_KEY, props || {}, target, propertyKey);
};
}

View File

@@ -1,2 +1 @@
export * from "./utils.js";
export * from "./common.js";

View File

@@ -51,10 +51,6 @@ export type PluginDefine = Registrable & {
[key: string]: TaskOutputDefine;
};
autowire?: {
[key: string]: any;
};
shortcut?: {
[key: string]: {
title: string;

View File

@@ -1,7 +1,6 @@
import { pluginRegistry } from "./registry.js";
import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api.js";
import { Decorator } from "../decorator/index.js";
import { AUTOWIRE_KEY } from "../decorator/index.js";
import "reflect-metadata";
import { merge, sortBy } from "lodash-es";
// 提供一个唯一 key
@@ -12,7 +11,6 @@ export function IsTaskPlugin(define: PluginDefine): ClassDecorator {
target = Decorator.target(target);
const inputs: any = {};
const autowires: any = {};
const outputs: any = {};
const properties = Decorator.getClassProperties(target);
for (const property in properties) {
@@ -21,11 +19,6 @@ export function IsTaskPlugin(define: PluginDefine): ClassDecorator {
inputs[property] = input;
}
const autowire = Reflect.getMetadata(AUTOWIRE_KEY, target, property);
if (autowire) {
autowires[property] = autowire;
}
const output = Reflect.getMetadata(PLUGIN_OUTPUT_KEY, target, property);
if (output) {
outputs[property] = output;
@@ -57,7 +50,7 @@ export function IsTaskPlugin(define: PluginDefine): ClassDecorator {
},
};
define = merge(defaultConfig, define, { input: inputMap, autowire: autowires, output: outputs });
define = merge(defaultConfig, define, { input: inputMap, output: outputs });
Reflect.defineMetadata(PLUGIN_CLASS_KEY, define, target);

View File

@@ -65,7 +65,7 @@ export class Registry<T = any> {
}
getDefineList() {
const list = [];
let list = [];
for (const key in this.storage) {
const define = this.getDefine(key);
if (define) {
@@ -78,6 +78,10 @@ export class Registry<T = any> {
list.push({ ...define, key });
}
}
list = list.sort((a, b) => {
return (a.order ?? 10) - (b?.order ?? 10);
});
return list;
}

View File

@@ -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.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
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
**Note:** Version bump only for package @certd/lib-huawei
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
**Note:** Version bump only for package @certd/lib-huawei

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.33.8",
"version": "1.34.2",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -10,7 +10,8 @@
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
"build": "npm run before-build && rollup -c ",
"dev-build": "npm run build",
"preview": "vite preview"
"preview": "vite preview",
"pub": "npm publish"
},
"dependencies": {
"axios": "^1.7.2",
@@ -23,5 +24,5 @@
"prettier": "^2.8.8",
"tslib": "^2.8.1"
},
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
}

View File

@@ -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.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
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
**Note:** Version bump only for package @certd/lib-iframe
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
**Note:** Version bump only for package @certd/lib-iframe

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.33.8",
"version": "1.34.2",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -13,7 +13,8 @@
"dev-build": "npm run build",
"build3": "rollup -c",
"build2": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
"preview": "vite preview",
"pub": "npm publish"
},
"dependencies": {
"nanoid": "^4.0.0"
@@ -30,5 +31,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
}

View File

@@ -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.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
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
**Note:** Version bump only for package @certd/jdcloud
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
**Note:** Version bump only for package @certd/jdcloud

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/jdcloud",
"version": "1.33.8",
"version": "1.34.2",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
@@ -9,7 +9,8 @@
"test": "cross-env NODE_CONFIG_DIR=./test/config mocha --recursive --require babel-register",
"dev": "babel src --out-dir babel -w",
"build": "rollup -c ",
"dev-build": "npm run build"
"dev-build": "npm run build",
"pub": "npm publish"
},
"author": "",
"license": "Apache",
@@ -60,5 +61,5 @@
"fetch"
]
},
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
}

View File

@@ -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.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
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
**Note:** Version bump only for package @certd/lib-k8s
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
**Note:** Version bump only for package @certd/lib-k8s

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-k8s",
"private": false,
"version": "1.33.8",
"version": "1.34.2",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -13,10 +13,11 @@
"dev-build": "npm run build",
"build3": "rollup -c",
"build2": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
"preview": "vite preview",
"pub": "npm publish"
},
"dependencies": {
"@certd/basic": "^1.33.8",
"@certd/basic": "^1.34.2",
"@kubernetes/client-node": "0.21.0"
},
"devDependencies": {
@@ -31,5 +32,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
}

View File

@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
**Note:** Version bump only for package @certd/lib-server
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
**Note:** Version bump only for package @certd/lib-server

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.33.8",
"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.33.8",
"@certd/basic": "^1.33.8",
"@certd/pipeline": "^1.33.8",
"@certd/plus-core": "^1.33.8",
"@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": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
}

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,18 @@
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
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
**Note:** Version bump only for package @certd/midway-flyway-js

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
"version": "1.33.8",
"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": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
}

View File

@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.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
* 根据SOA记录判断子域名托管有缺陷改回手动配置子域名托管记录的方式 ([1b280a2](https://github.com/certd/certd/commit/1b280a2940f9e2d919b0bf23b89cc185be1fa498))
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
**Note:** Version bump only for package @certd/plugin-cert
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
### Bug Fixes

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.33.8",
"version": "1.34.2",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -12,13 +12,14 @@
"dev-build": "npm run build",
"build3": "rollup -c",
"build2": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
"preview": "vite preview",
"pub": "npm publish"
},
"dependencies": {
"@certd/acme-client": "^1.33.8",
"@certd/basic": "^1.33.8",
"@certd/pipeline": "^1.33.8",
"@certd/plugin-lib": "^1.33.8",
"@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",
@@ -42,5 +43,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
}

View File

@@ -4,9 +4,6 @@ import { IAccess, Registrable } from "@certd/pipeline";
export type DnsProviderDefine = Registrable & {
accessType: string;
icon?: string;
autowire?: {
[key: string]: any;
};
};
export type CreateRecordOptions = {

View File

@@ -1,6 +1,5 @@
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, IDnsProvider, RemoveRecordOptions } from "./api.js";
import { dnsProviderRegistry } from "./registry.js";
import { Decorator } from "@certd/pipeline";
import { HttpClient, ILogger } from "@certd/basic";
export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
@@ -9,6 +8,8 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
logger!: ILogger;
usePunyCode(): boolean {
//是否使用punycode来添加解析记录
//默认都使用原始中文域名来添加
return false;
}
@@ -32,15 +33,13 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
export async function createDnsProvider(opts: { dnsProviderType: string; context: DnsProviderContext }): Promise<IDnsProvider> {
const { dnsProviderType, context } = opts;
const dnsProviderPlugin = dnsProviderRegistry.get(dnsProviderType);
const DnsProviderClass = dnsProviderPlugin.target;
const DnsProviderClass = await dnsProviderPlugin.target();
const dnsProviderDefine = dnsProviderPlugin.define as DnsProviderDefine;
if (dnsProviderDefine.deprecated) {
context.logger.warn(dnsProviderDefine.deprecated);
}
// @ts-ignore
const dnsProvider: IDnsProvider = new DnsProviderClass();
Decorator.inject(dnsProviderDefine.autowire, dnsProvider, context);
dnsProvider.setCtx(context);
await dnsProvider.onInstance();
return dnsProvider;

View File

@@ -1,6 +1,6 @@
import { dnsProviderRegistry } from "./registry.js";
import { DnsProviderDefine } from "./api.js";
import { Decorator, AUTOWIRE_KEY } from "@certd/pipeline";
import { Decorator } from "@certd/pipeline";
import * as _ from "lodash-es";
// 提供一个唯一 key
@@ -9,22 +9,15 @@ export const DNS_PROVIDER_CLASS_KEY = "pipeline:dns-provider";
export function IsDnsProvider(define: DnsProviderDefine): ClassDecorator {
return (target: any) => {
target = Decorator.target(target);
const autowires: any = {};
const properties = Decorator.getClassProperties(target);
for (const property in properties) {
const autowire = Reflect.getMetadata(AUTOWIRE_KEY, target, property);
if (autowire) {
autowires[property] = autowire;
}
}
_.merge(define, { autowire: autowires });
Reflect.defineMetadata(DNS_PROVIDER_CLASS_KEY, define, target);
target.define = define;
dnsProviderRegistry.register(define.name, {
define,
target,
target: async () => {
return target;
},
});
};
}

View File

@@ -1,16 +1,18 @@
import { IDomainParser, ISubDomainsGetter } from "./api";
//@ts-ignore
import psl from "psl";
import { ILogger, utils, logger as globalLogger } from "@certd/basic";
import { resolveDomainBySoaRecord } from "@certd/acme-client";
import { logger, utils } from "@certd/basic";
export class DomainParser implements IDomainParser {
subDomainsGetter: ISubDomainsGetter;
constructor(subDomainsGetter: ISubDomainsGetter) {
logger: ILogger;
constructor(subDomainsGetter: ISubDomainsGetter, logger?: ILogger) {
this.subDomainsGetter = subDomainsGetter;
this.logger = logger || globalLogger;
}
parseDomain(fullDomain: string) {
parseDomainByPsl(fullDomain: string) {
const parsed = psl.parse(fullDomain) as psl.ParsedDomain;
if (parsed.error) {
throw new Error(`解析${fullDomain}域名失败:` + JSON.stringify(parsed.error));
@@ -19,38 +21,46 @@ export class DomainParser implements IDomainParser {
}
async parse(fullDomain: string) {
logger.info(`查找主域名:${fullDomain}`);
this.logger.info(`查找主域名:${fullDomain}`);
const cacheKey = `domain_parse:${fullDomain}`;
const value = utils.cache.get(cacheKey);
if (value) {
logger.info(`从缓存获取到主域名:${fullDomain}->${value}`);
this.logger.info(`从缓存获取到主域名:${fullDomain}->${value}`);
return value;
}
const subDomains = await this.subDomainsGetter.getSubDomains();
if (subDomains && subDomains.length > 0) {
const fullDomainDot = "." + fullDomain;
for (const subDomain of subDomains) {
if (fullDomainDot.endsWith("." + subDomain)) {
//找到子域名托管
utils.cache.set(cacheKey, subDomain, {
ttl: 60 * 1000,
});
this.logger.info(`获取到子域名托管域名:${fullDomain}->${subDomain}`);
return subDomain;
}
}
}
const res = this.parseDomainByPsl(fullDomain);
this.logger.info(`从psl获取主域名:${fullDomain}->${res}`);
let soaManDomain = null;
try {
const mainDomain = await resolveDomainBySoaRecord(fullDomain);
if (mainDomain) {
utils.cache.set(cacheKey, mainDomain, {
ttl: 2 * 60 * 1000,
});
logger.info(`获取到主域名:${fullDomain}->${mainDomain}`);
return mainDomain;
this.logger.info(`从SOA获取到主域名:${fullDomain}->${mainDomain}`);
soaManDomain = mainDomain;
}
} catch (e) {
logger.error("从SOA获取主域名失败", e.message);
this.logger.error("从SOA获取主域名失败", e.message);
}
if (soaManDomain && soaManDomain !== res) {
this.logger.warn(`SOA获取的主域名${soaManDomain}和psl获取的主域名(${res})不一致,请确认是否有设置子域名托管`);
}
// const subDomains = await this.subDomainsGetter.getSubDomains();
// if (subDomains && subDomains.length > 0) {
// for (const subDomain of subDomains) {
// if (fullDomain.endsWith(subDomain)) {
// //找到子域名托管
// return subDomain;
// }
// }
// }
const res = this.parseDomain(fullDomain);
logger.info(`从psl获取主域名:${fullDomain}->${res}`);
return res;
}
}

View File

@@ -63,6 +63,7 @@ type AcmeServiceOptions = {
maxCheckRetryCount?: number;
userId: number;
domainParser: IDomainParser;
waitDnsDiffuseTime?: number;
};
export class AcmeService {
@@ -248,7 +249,10 @@ export class AcmeService {
fullRecord = cname.fullRecord;
}
} else {
this.logger.error("未找到域名Cname校验计划使用默认的dnsProvider");
this.logger.error(`未找到域名${fullDomain}的CNAME校验计划请修改证书申请配置`);
}
if (dnsProvider == null) {
throw new Error(`未找到域名${fullDomain}CNAME校验计划的DnsProvider请修改证书申请配置`);
}
} else if (domainVerifyPlan.type === "http") {
const httpVerifyPlan = domainVerifyPlan.httpVerifyPlan;

View File

@@ -27,7 +27,8 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
"1、支持多个域名打到一个证书上例如 foo.com*.foo.com*.bar.com\n" +
"2、子域名被通配符包含的不要填写例如www.foo.com已经被*.foo.com包含不要填写www.foo.com\n" +
"3、泛域名只能通配*号那一级(*.foo.com的证书不能用于xxx.yyy.foo.com、不能用于foo.com\n" +
"4、输入一个空格之后再输入下一个",
"4、输入一个空格之后再输入下一个 \n" +
"5、如果您配置了子域托管解析请先[设置托管子域名](#/certd/pipeline/subDomain)",
})
domains!: string[];

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;
@@ -327,7 +338,7 @@ HTTP文件验证不支持泛域名需要配置网站文件上传`,
}
this.eab = eab;
const subDomainsGetter = await this.ctx.serviceGetter.get<ISubDomainsGetter>("subDomainsGetter");
const domainParser = new DomainParser(subDomainsGetter);
const domainParser = new DomainParser(subDomainsGetter, this.logger);
this.acme = new AcmeService({
userId: this.ctx.user.id,
userContext: this.userContext,
@@ -341,6 +352,7 @@ HTTP文件验证不支持泛域名需要配置网站文件上传`,
signal: this.ctx.signal,
maxCheckRetryCount: this.maxCheckRetryCount,
domainParser,
waitDnsDiffuseTime: this.waitDnsDiffuseTime,
});
}

View File

@@ -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.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
* 支持部署证书到火山dcdn ([5f85219](https://github.com/certd/certd/commit/5f852194953dc1b4e6336770f417507b8f5a33ad))
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
### Performance Improvements
* 优化cdnfly插件支持自动匹配域名部署 ([afd59e9](https://github.com/certd/certd/commit/afd59e9933b2650f41c5d47684c171b93b962065))
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
### Bug Fixes

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
"version": "1.33.8",
"version": "1.34.2",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -12,13 +12,14 @@
"dev-build": "npm run build",
"build3": "rollup -c",
"build2": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
"preview": "vite preview",
"pub": "npm publish"
},
"dependencies": {
"@alicloud/pop-core": "^1.7.10",
"@aws-sdk/client-s3": "^3.787.0",
"@certd/basic": "^1.33.8",
"@certd/pipeline": "^1.33.8",
"@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",
@@ -49,5 +50,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "a188385817e8dbd270dbdbf7f86a5b812884cf59"
"gitHead": "6c74148c277432f91014bf1eebd824e7423c6f4b"
}

View File

@@ -5,6 +5,7 @@ import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline";
title: "阿里云授权",
desc: "",
icon: "ant-design:aliyun-outlined",
order: 0,
})
export class AliyunAccess extends BaseAccess {
@AccessInput({

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

@@ -37,6 +37,7 @@ export function createRemoteSelectInputDefine(opts?: {
multi?: boolean;
required?: boolean;
rules?: any;
mergeScript?: string;
}) {
const title = opts?.title || "请选择";
const certDomainsInputKey = opts?.certDomainsInputKey || "certDomains";
@@ -65,8 +66,10 @@ export function createRemoteSelectInputDefine(opts?: {
watches: [certDomainsInputKey, accessIdInputKey, ...watches],
},
rules: opts?.rules,
required: true,
mergeScript: `
required: opts.required ?? true,
mergeScript:
opts.mergeScript ??
`
return {
component:{
form: ctx.compute(({form})=>{
@@ -80,3 +83,5 @@ export function createRemoteSelectInputDefine(opts?: {
return merge(item, opts?.formItem);
}

View File

@@ -1,4 +1,4 @@
import { HttpClient, ILogger, utils } from "@certd/basic";
import { HttpClient, ILogger, safePromise, utils } from "@certd/basic";
import { QiniuAccess } from "../access.js";
import fs from "fs";
@@ -77,7 +77,7 @@ export class QiniuClient {
const http = new HttpClient({ timeout: 10000, middlewares: [auth] });
console.log("http", http);
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
try {
http.get({
url: opts.url,

View File

@@ -1,7 +1,7 @@
// @ts-ignore
import path from "path";
import { isArray } from "lodash-es";
import { ILogger } from "@certd/basic";
import { ILogger, safePromise } from "@certd/basic";
import { SshAccess } from "./ssh-access.js";
import fs from "fs";
@@ -70,7 +70,7 @@ export class AsyncSsh2Client {
const ssh2 = await import("ssh2");
const ssh2Constants = await import("ssh2/lib/protocol/constants.js");
const { SUPPORTED_KEX, SUPPORTED_SERVER_HOST_KEY, SUPPORTED_CIPHER, SUPPORTED_MAC } = ssh2Constants.default;
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
try {
const conn = new ssh2.default.Client();
conn
@@ -108,7 +108,7 @@ export class AsyncSsh2Client {
});
}
async getSftp() {
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
this.logger.info("获取sftp");
this.conn.sftp((err: any, sftp: any) => {
if (err) {
@@ -122,7 +122,7 @@ export class AsyncSsh2Client {
async fastPut(options: { sftp: any; localPath: string; remotePath: string; opts?: { mode?: string } }) {
const { sftp, localPath, remotePath, opts } = options;
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
this.logger.info(`开始上传:${localPath} => ${remotePath}`);
sftp.fastPut(localPath, remotePath, { ...(opts ?? {}) }, (err: Error) => {
if (err) {
@@ -138,7 +138,7 @@ export class AsyncSsh2Client {
async listDir(options: { sftp: any; remotePath: string }) {
const { sftp, remotePath } = options;
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
this.logger.info(`listDir${remotePath}`);
sftp.readdir(remotePath, (err: Error, list: any) => {
if (err) {
@@ -152,7 +152,7 @@ export class AsyncSsh2Client {
async unlink(options: { sftp: any; remotePath: string }) {
const { sftp, remotePath } = options;
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
this.logger.info(`开始删除远程文件:${remotePath}`);
sftp.unlink(remotePath, (err: Error) => {
if (err) {
@@ -182,7 +182,7 @@ export class AsyncSsh2Client {
// script += "\r\nexit\r\n";
// //保证windows下正常退出
// }
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
this.logger.info(`执行命令:[${this.connConf.host}][exec]: \n` + script);
// pty 伪终端window下的输出会带上conhost.exe之类的多余的字符串影响返回结果判断
// linux下 当使用keyboard-interactive 登录时需要pty
@@ -232,7 +232,7 @@ export class AsyncSsh2Client {
async shell(script: string | string[]): Promise<string> {
const stripAnsiModule = await import("strip-ansi");
const stripAnsi = stripAnsiModule.default;
return new Promise<any>((resolve, reject) => {
return safePromise<any>((resolve, reject) => {
this.logger.info(`执行shell脚本[${this.connConf.host}][shell]: ` + script);
this.conn.shell((err: Error, stream: any) => {
if (err) {
@@ -299,7 +299,7 @@ export class AsyncSsh2Client {
}
async download(param: { remotePath: string; savePath: string; sftp: any }) {
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
const { remotePath, savePath, sftp } = param;
sftp.fastGet(
remotePath,
@@ -385,44 +385,40 @@ export class SshClient {
async scpUpload(options: { conn: any; localPath: string; remotePath: string; opts?: { mode?: string } }) {
const { conn, localPath, remotePath } = options;
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
// 关键步骤:构造 SCP 命令
try {
this.logger.info(`开始上传:${localPath} => ${remotePath}`);
conn.conn.exec(
`scp -t ${remotePath}`, // -t 表示目标模式
(err, stream) => {
if (err) {
return reject(err);
}
try {
// 准备 SCP 协议头
const fileStats = fs.statSync(localPath);
const fileName = path.basename(localPath);
// SCP 协议格式C[权限] [文件大小] [文件名]\n
stream.write(`C0644 ${fileStats.size} ${fileName}\n`);
// 通过管道传输文件
fs.createReadStream(localPath)
.on("error", e => {
this.logger.info("read stream error", e);
reject(e);
})
.pipe(stream)
.on("finish", async () => {
this.logger.info(`上传完成:${localPath} => ${remotePath}`);
resolve(true);
})
.on("error", reject);
} catch (e) {
reject(e);
}
this.logger.info(`开始上传:${localPath} => ${remotePath}`);
conn.conn.exec(
`scp -t ${remotePath}`, // -t 表示目标模式
(err, stream) => {
if (err) {
return reject(err);
}
);
} catch (e) {
reject(e);
}
try {
// 准备 SCP 协议头
const fileStats = fs.statSync(localPath);
const fileName = path.basename(localPath);
// SCP 协议格式C[权限] [文件大小] [文件名]\n
stream.write(`C0644 ${fileStats.size} ${fileName}\n`);
// 通过管道传输文件
fs.createReadStream(localPath)
.on("error", e => {
this.logger.info("read stream error", e);
reject(e);
})
.pipe(stream)
.on("finish", async () => {
this.logger.info(`上传完成:${localPath} => ${remotePath}`);
resolve(true);
})
.on("error", reject);
} catch (e) {
reject(e);
}
}
);
});
}

View File

@@ -1,5 +1,5 @@
import { TencentAccess } from "../access.js";
import { ILogger } from "@certd/basic";
import { ILogger, safePromise } from "@certd/basic";
import fs from "fs";
export class TencentCosClient {
@@ -26,7 +26,7 @@ export class TencentCosClient {
async uploadFile(key: string, file: Buffer | string) {
const cos = await this.getCosClient();
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
let readableStream = file as any;
if (typeof file === "string") {
readableStream = fs.createReadStream(file);
@@ -54,7 +54,7 @@ export class TencentCosClient {
async removeFile(key: string) {
const cos = await this.getCosClient();
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
cos.deleteObject(
{
Bucket: this.bucket,
@@ -75,7 +75,7 @@ export class TencentCosClient {
async downloadFile(key: string, savePath: string) {
const cos = await this.getCosClient();
const writeStream = fs.createWriteStream(savePath);
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
cos.getObject(
{
Bucket: this.bucket,
@@ -96,7 +96,7 @@ export class TencentCosClient {
async listDir(dirKey: string) {
const cos = await this.getCosClient();
return new Promise((resolve, reject) => {
return safePromise((resolve, reject) => {
cos.getBucket(
{
Bucket: this.bucket,

View File

@@ -3,6 +3,34 @@
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
* 根据SOA记录判断子域名托管有缺陷改回手动配置子域名托管记录的方式 ([1b280a2](https://github.com/certd/certd/commit/1b280a2940f9e2d919b0bf23b89cc185be1fa498))
* 修复宝塔授权测试按钮显示错误的bug ([048696e](https://github.com/certd/certd/commit/048696ee9386491bb68592fb3a47d1c900bb68bf))
### Performance Improvements
* 支持部署证书到火山dcdn ([5f85219](https://github.com/certd/certd/commit/5f852194953dc1b4e6336770f417507b8f5a33ad))
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
### Bug Fixes
* 修复二次认证登录进入错误账号的bug ([e3930e0](https://github.com/certd/certd/commit/e3930e07172dd7903cb0f6ff26e0e3e828ba3e77))
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
### Bug Fixes

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/ui-client",
"version": "1.33.8",
"version": "1.34.2",
"private": true,
"scripts": {
"dev": "vite --open",
@@ -19,7 +19,8 @@
"upgrade": "yarn upgrade-interactive --latest",
"tsc": "vue-tsc --noEmit --skipLibCheck",
"circle:check": "pnpm dependency-cruise --validate --output-type err-html -f dependency-report.html src",
"afterPubPush": "git add . && git commit -m \"build: publish success\" && git push"
"afterPubPush": "git add . && git commit -m \"build: publish success\" && git push",
"pub": "echo 1"
},
"author": "greper",
"license": "AGPL-3.0",
@@ -101,8 +102,8 @@
"zod-defaults": "^0.1.3"
},
"devDependencies": {
"@certd/lib-iframe": "^1.33.8",
"@certd/pipeline": "^1.33.8",
"@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
@@ -69,12 +69,10 @@ function createService() {
const err = new CodeError(errorMessage, dataAxios.code, dataAxios.data);
// @ts-ignore
response.config.onError(err);
return;
}
//@ts-ignore
const showErrorNotify = response?.config?.showErrorNotify;
errorCreate(`${errorMessage}: ${response.config.url}`, showErrorNotify, dataAxios);
return dataAxios;
}
},
error => {

View File

@@ -67,7 +67,6 @@ export function errorLog(error: any, notify = true) {
/**
* @description 创建一个错误
* @param {String} msg 错误信息
*/
export function errorCreate(msg: string, notify = true, data?: any) {
const err = new CodeError(msg, data.code, data.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

@@ -8,7 +8,7 @@
<fs-copyable v-model="cnameRecord.hostRecord"></fs-copyable>
</td>
<td style="text-align: center">CNAME</td>
<td class="record-value">
<td class="record-value" :title="cnameRecord.recordValue">
<fs-copyable v-model="cnameRecord.recordValue"></fs-copyable>
</td>
<td class="status center flex-center">
@@ -38,12 +38,12 @@ const statusDict = dict({
{ label: "验证中", value: "validating", color: "blue" },
{ label: "验证成功", value: "valid", color: "green" },
{ label: "验证失败", value: "failed", color: "red" },
{ label: "验证超时", value: "timeout", color: "red" }
]
{ label: "验证超时", value: "timeout", color: "red" },
],
});
defineOptions({
name: "CnameRecordInfo"
name: "CnameRecordInfo",
});
const props = defineProps<{
@@ -55,7 +55,7 @@ const emit = defineEmits<{
{
id: number | null;
status: string | null;
}
},
];
}>();
@@ -64,7 +64,7 @@ const cnameRecord = ref<CnameRecord | null>(null);
function onRecordChange() {
emit("change", {
id: cnameRecord.value?.id,
status: cnameRecord.value?.status
status: cnameRecord.value?.status,
});
}
@@ -90,11 +90,11 @@ async function doRefresh() {
watch(
() => props.domain,
async (value) => {
async value => {
await doRefresh();
},
{
immediate: true
immediate: true,
}
);

View File

@@ -15,7 +15,7 @@ import { ref, inject } from "vue";
import { Form } from "ant-design-vue";
defineOptions({
name: "ApiTest"
name: "ApiTest",
});
const getScope: any = inject("get:scope");
@@ -49,14 +49,14 @@ const doTest = async () => {
type: pluginType,
typeName: form.type,
action: props.action,
input: pluginType === "plugin" ? form.input : form
input: pluginType === "plugin" ? form.input : form,
},
{
onError(err: any) {
hasError.value = true;
message.value = `错误:${err.message}`;
},
showErrorNotify: false
showErrorNotify: false,
}
);
message.value = "测试请求成功";

View File

@@ -53,11 +53,15 @@ const getOptions = async () => {
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 = props.form[key];
const value = input[key];
if (value == null || value === "") {
console.log("remote-select required", key);
return;
@@ -69,8 +73,6 @@ const getOptions = async () => {
hasError.value = false;
loading.value = true;
optionsRef.value = [];
const { form } = getScope();
const pluginType = getPluginType();
try {
const res = await doRequest(
@@ -78,7 +80,7 @@ const getOptions = async () => {
type: pluginType,
typeName: form.type,
action: props.action,
input: pluginType === "plugin" ? form.input : form,
input,
},
{
onError(err: any) {
@@ -115,11 +117,16 @@ async function refreshOptions() {
watch(
() => {
const values = [];
const pluginType = getPluginType();
const { form } = getScope();
const input = pluginType === "plugin" ? form.input : form;
for (const item of props.watches) {
values.push(props.form[item]);
values.push(input[item]);
}
return {
form: props.form,
form: input,
watched: values,
};
},

View File

@@ -27,7 +27,7 @@ const props = withDefaults(
mode?: "comm" | "button" | "nav" | "icon";
}>(),
{
mode: "button"
mode: "button",
}
);
type Text = {
@@ -40,57 +40,57 @@ const text = computed<Text>(() => {
isComm: {
comm: {
name: `${vipLabel}已开通`,
title: "到期时间:" + expireTime.value
title: "到期时间:" + expireTime.value,
},
button: {
name: `${vipLabel}已开通`,
title: "到期时间:" + expireTime.value
title: "到期时间:" + expireTime.value,
},
icon: {
name: "",
title: `${vipLabel}已开通`
title: `${vipLabel}已开通`,
},
nav: {
name: `${vipLabel}`,
title: "到期时间:" + expireTime.value
}
title: "到期时间:" + expireTime.value,
},
},
isPlus: {
comm: {
name: "商业版功能",
title: "升级商业版,获取商业授权"
title: "升级商业版,获取商业授权",
},
button: {
name: `${vipLabel}已开通`,
title: "到期时间:" + expireTime.value
title: "到期时间:" + expireTime.value,
},
icon: {
name: "",
title: `${vipLabel}已开通`
title: `${vipLabel}已开通`,
},
nav: {
name: `${vipLabel}`,
title: "到期时间:" + expireTime.value
}
title: "到期时间:" + expireTime.value,
},
},
free: {
comm: {
name: "商业版功能",
title: "升级商业版,获取商业授权"
title: "升级商业版,获取商业授权",
},
button: {
name: "专业版功能",
title: "升级专业版享受更多VIP特权"
title: "升级专业版享受更多VIP特权",
},
icon: {
name: "",
title: "专业版功能"
title: "专业版功能",
},
nav: {
name: "基础版",
title: "升级专业版享受更多VIP特权"
}
}
title: "升级专业版享受更多VIP特权",
},
},
};
if (settingStore.isComm) {
return map.isComm[props.mode];
@@ -119,7 +119,7 @@ const expiredDays = computed(() => {
const formState = reactive({
code: "",
inviteCode: ""
inviteCode: "",
});
const router = useRouter();
@@ -143,10 +143,10 @@ async function doActive() {
content: "绑定账号后可以避免License丢失强烈建议绑定",
onOk() {
router.push("/sys/account");
}
},
});
}
}
},
});
}
}
@@ -183,7 +183,7 @@ function openTrialModal() {
<div>点击确认即可获取7天专业版试用</div>
</div>
);
}
},
});
}
@@ -208,7 +208,7 @@ function openStarModal() {
<img class="ml-5" src="https://img.shields.io/github/stars/certd/certd?logo=github" />
</div>
);
}
},
});
}
@@ -226,13 +226,14 @@ function openUpgrade() {
title = "续期专业版/升级商业版";
}
const productInfo = settingStore.productInfo;
const vipTypeDefine = {
free: {
title: "基础版",
desc: "社区免费版",
type: "free",
icon: "lucide:package-open",
privilege: ["证书申请无限制", "域名数量无限制", "证书流水线数量无限制", "常用的主机、云平台、cdn等部署插件", "邮件、webhook通知方式"]
privilege: ["证书申请无限制", "域名数量无限制", "证书流水线数量无限制", "常用的主机、云平台、cdn等部署插件", "邮件、webhook通知方式"],
},
plus: {
title: "专业版",
@@ -243,10 +244,12 @@ function openUpgrade() {
title: "点击获取7天试用",
click: () => {
openStarModal();
}
},
},
icon: "stash:thumb-up",
price: 29.9,
price: productInfo.plus.price,
price3: `¥${productInfo.plus.price3}/3年`,
tooltip: productInfo.plus.tooltip,
get() {
return (
<a-tooltip title="爱发电赞助“VIP会员”后获取一年期专业版激活码开源需要您的支持">
@@ -255,7 +258,7 @@ function openUpgrade() {
</a-button>
</a-tooltip>
);
}
},
},
comm: {
title: "商业版",
@@ -263,11 +266,13 @@ function openUpgrade() {
type: "comm",
icon: "vaadin:handshake",
privilege: ["拥有专业版所有特权", "允许商用可修改logo、标题", "数据统计", "插件管理", "多用户无限制", "支持用户支付"],
price: 399,
price: productInfo.comm.price,
price3: `¥${productInfo.comm.price3}/3年`,
tooltip: productInfo.comm.tooltip,
get() {
return <a-button size="small">请联系作者获取</a-button>;
}
}
return <a-button size="small">请联系作者获取试用</a-button>;
},
},
};
const modalRef = modal.confirm({
@@ -321,9 +326,12 @@ function openUpgrade() {
<div class="footer flex-between flex-vc">
<div class="price-show">
{item.price && (
<span>
<span class="price-text">¥{item.price}</span>
/
<span class="flex">
<span class="-text">¥{item.price}</span>
<span>/</span>
<a-tooltip class="ml-5" title={item.price3}>
<fs-icon class="pointer color-red" icon="ic:outline-discount"></fs-icon>
</a-tooltip>
</span>
)}
{!item.price && (
@@ -340,6 +348,11 @@ function openUpgrade() {
}
return (
<div class="mt-10 mb-10 vip-active-modal">
{productInfo.notice && (
<div class="mb-10">
<a-alert type="error" message={productInfo.notice}></a-alert>
</div>
)}
<div class="vip-type-vs">
<a-row gutter={20}>{slots}</a-row>
</div>
@@ -365,7 +378,7 @@ function openUpgrade() {
</div>
</div>
);
}
},
});
}
onMounted(() => {

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

@@ -16,9 +16,9 @@ export const aboutResource = [
show: () => {
const settingStore = useSettingStore();
return !settingStore.isComm;
}
}
}
},
},
},
];
export default aboutResource;

View File

@@ -98,17 +98,17 @@ export const certdResources = [
keepAlive: true,
},
},
// {
// title: "子域名托管设置",
// name: "SubDomain",
// path: "/certd/pipeline/subDomain",
// component: "/certd/pipeline/sub-domain/index.vue",
// meta: {
// icon: "material-symbols:approval-delegation-outline",
// auth: true,
// keepAlive: true,
// },
// },
{
title: "子域名托管设置",
name: "SubDomain",
path: "/certd/pipeline/subDomain",
component: "/certd/pipeline/sub-domain/index.vue",
meta: {
icon: "material-symbols:approval-delegation-outline",
auth: true,
keepAlive: true,
},
},
{
title: "流水线分组管理",
name: "PipelineGroupManager",

View File

@@ -39,6 +39,7 @@ export type SysPublicSetting = {
limitUserPipelineCount?: number;
managerOtherUserPipeline?: boolean;
icpNo?: string;
mpsNo?: string;
robots?: boolean;
};
export type SuiteSetting = {
@@ -109,3 +110,11 @@ export async function sendEmailCode(data: any): Promise<any> {
data,
});
}
export async function getProductInfo(): Promise<any> {
return await request({
url: "/basic/settings/productInfo",
method: "get",
silent: true,
});
}

View File

@@ -9,7 +9,7 @@ import { env } from "/@/utils/util.env";
import { updatePreferences } from "/@/vben/preferences";
import { useTitle } from "@vueuse/core";
import { utils } from "/@/utils";
import { cloneDeep } from "lodash-es";
import { cloneDeep, merge } from "lodash-es";
export interface SettingState {
sysPublic?: SysPublicSetting;
@@ -32,6 +32,21 @@ export interface SettingState {
time?: number;
deltaTime?: number;
};
productInfo: {
notice?: string;
plus: {
name: string;
price: number;
price3: number;
tooltip?: string;
};
comm: {
name: string;
price: number;
price3: number;
tooltip?: string;
};
};
}
const defaultSiteInfo: SiteInfo = {
@@ -80,6 +95,19 @@ export const useSettingStore = defineStore({
time: 0,
deltaTime: 0,
},
productInfo: {
notice: "",
plus: {
name: "专业版",
price: 29.9,
price3: 89.9,
},
comm: {
name: "商业版",
price: 399,
price3: 899,
},
},
}),
getters: {
getSysPublic(): SysPublicSetting {
@@ -227,6 +255,14 @@ export const useSettingStore = defineStore({
}
}
},
async loadProductInfo() {
try {
const productInfo = await basicApi.getProductInfo();
merge(this.productInfo, productInfo);
} catch (e) {
console.error(e);
}
},
async init() {
await this.loadSysSettings();
},
@@ -235,6 +271,7 @@ export const useSettingStore = defineStore({
return;
}
await this.init();
this.loadProductInfo();
this.inited = true;
},
},

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

@@ -127,6 +127,7 @@ const doAuthenticatorSave = async (form: any) => {
message: "保存成功",
});
authenticatorForm.open = false;
formState.authenticator.verified = true;
};
function onAuthenticatorEnabledChanged(value: any) {

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;

Some files were not shown because too many files have changed in this diff Show More