Compare commits

...

88 Commits

Author SHA1 Message Date
xiaojunnuo
e1daaf07ce v1.37.2 2025-10-14 22:50:33 +08:00
xiaojunnuo
cd21f2d1d7 build: prepare to build 2025-10-14 22:48:36 +08:00
xiaojunnuo
836e41064f build: prepare to build 2025-10-14 22:43:55 +08:00
xiaojunnuo
4658e4c739 build: prepare to build 2025-10-14 22:41:32 +08:00
xiaojunnuo
7993a7cdb0 perf: 优化start.sh脚本,去掉删除非ui目录的操作及提示 2025-10-14 22:41:03 +08:00
xiaojunnuo
567cb7d737 perf: start.sh脚本支持根据当前系统判断是否使用sudo 2025-10-14 22:35:24 +08:00
xiaojunnuo
985128b537 chore: 1 2025-10-14 22:25:42 +08:00
xiaojunnuo
cd35568e04 perf: 证书监控支持设置证书即将过期天数 2025-10-14 22:25:04 +08:00
xiaojunnuo
f612509cac perf: 支持新网代理方式 2025-10-14 12:05:31 +08:00
xiaojunnuo
f415190483 perf: dns支持新网互联 2025-10-14 10:55:10 +08:00
xiaojunnuo
e00733a346 fix: aliyunoss 选择证书接入点选择新加坡无法上传的bug 2025-10-13 23:16:03 +08:00
xiaojunnuo
aafafa0e73 chore: 1 2025-10-12 23:57:17 +08:00
xiaojunnuo
c87c9af12e chore: 1 2025-10-12 23:56:14 +08:00
xiaojunnuo
622215715f chore: 删除无用依赖 2025-10-11 19:08:14 +08:00
xiaojunnuo
c87250c028 docs: ipv6地址提示 2025-10-11 18:52:57 +08:00
xiaojunnuo
d6b6d700a5 docs: ipv6地址提示 2025-10-11 18:21:17 +08:00
xiaojunnuo
9d4e2c98a3 Merge remote-tracking branch 'origin/v2-dev' into v2-dev 2025-10-11 17:00:00 +08:00
xiaojunnuo
08094c2660 docs: ipv6地址提示 2025-10-11 16:59:28 +08:00
xiaojunnuo
fda82c82b0 chore: 1 2025-10-07 21:54:50 +08:00
xiaojunnuo
f0eabd4ea0 build: vscode auto compile 2025-10-07 21:45:09 +08:00
xiaojunnuo
5a4d812146 perf: 增加飞牛证书id选择的提示 2025-10-07 21:05:28 +08:00
xiaojunnuo
bf156a13bd fix: 修复飞牛证书部署后无法生效的bug 2025-10-07 21:01:19 +08:00
xiaojunnuo
53d276a8fc chore: 官方email服务支持发送附件 2025-10-05 22:44:59 +08:00
xiaojunnuo
978fa54518 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2025-10-05 08:05:59 +00:00
xiaojunnuo
31f82e58b5 chore: test优化 2025-10-05 07:59:56 +00:00
xiaojunnuo
5967f66e6d chore: 1 2025-10-05 15:01:35 +08:00
xiaojunnuo
bea81b54ca chore: 1 2025-10-05 14:52:25 +08:00
xiaojunnuo
6fd403bdca Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2025-10-05 14:47:45 +08:00
xiaojunnuo
3d673d9d40 chroe: remote 2025-10-05 14:47:40 +08:00
xiaojunnuo
cac949de56 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2025-10-05 05:57:03 +00:00
xiaojunnuo
dffa152698 chore: nettest 2025-10-05 13:10:03 +08:00
xiaojunnuo
7796298fca 1 2025-10-01 02:11:03 +08:00
xiaojunnuo
5291bfe8d4 chore: 网络测试 2025-09-30 18:03:16 +00:00
xiaojunnuo
b364313297 chore: linux 网络测试命令验证 2025-09-30 18:01:49 +00:00
xiaojunnuo
2bef608e07 perf: 支持网络测试 2025-09-30 23:27:31 +08:00
xiaojunnuo
aee13ad909 docs 2025-09-29 21:11:43 +08:00
xiaojunnuo
9d82eba599 docs: 2025-09-29 21:00:28 +08:00
xiaojunnuo
4852beb390 1.37.1 2025-09-29 20:48:34 +08:00
xiaojunnuo
522c2f61c0 build: publish 2025-09-29 20:37:20 +08:00
xiaojunnuo
d331396afe build: trigger build image 2025-09-29 20:37:03 +08:00
xiaojunnuo
c725cee044 v1.37.1 2025-09-29 20:35:30 +08:00
xiaojunnuo
367ef4ecb2 build: prepare to build 2025-09-29 20:32:54 +08:00
xiaojunnuo
c3a64facd5 chore 2025-09-29 20:32:31 +08:00
xiaojunnuo
2671781e1b fix: 修复某些情况下cname申请证书报错主域名不一致的bug 2025-09-29 18:58:19 +08:00
xiaojunnuo
9291fa68aa perf: dns解析支持阿里esa 2025-09-28 23:29:56 +08:00
xiaojunnuo
6ebb3659f4 perf: cname主域名校验提示优化,显示不一致的两方便于排查问题 2025-09-28 16:18:39 +08:00
xiaojunnuo
109696e965 fix: 修复版本比较bug 2025-09-28 12:49:53 +08:00
xiaojunnuo
b86bbd370c chore: deploy 1.37.0 2025-09-28 12:44:02 +08:00
xiaojunnuo
1575a4fb1a chore: deploy 1.37.0 2025-09-28 12:42:54 +08:00
xiaojunnuo
e2f500be90 chore: deploy 1.37.0 2025-09-28 12:37:22 +08:00
xiaojunnuo
284b00a826 chore: 2025-09-28 12:21:42 +08:00
xiaojunnuo
66180e19b5 build: publish 2025-09-28 12:18:46 +08:00
xiaojunnuo
1531462d22 build: trigger build image 2025-09-28 12:18:30 +08:00
xiaojunnuo
e17cd1f298 v1.37.0 2025-09-28 12:17:05 +08:00
xiaojunnuo
13092e9f80 build: prepare to build 2025-09-28 12:14:35 +08:00
xiaojunnuo
8133b8b9dd build: prepare to build 2025-09-28 12:07:54 +08:00
xiaojunnuo
e25aafac6d chore: 2025-09-28 12:07:51 +08:00
xiaojunnuo
037c7beb1b build: prepare to build 2025-09-28 12:06:22 +08:00
xiaojunnuo
faac4dfc30 chore: 2025-09-28 12:06:13 +08:00
xiaojunnuo
469a088a4d build: prepare to build 2025-09-28 11:30:24 +08:00
xiaojunnuo
9c854f727f chore: 2025-09-28 11:29:57 +08:00
xiaojunnuo
8f6e5bd24b feat: dist打包前检查 2025-09-28 11:02:25 +08:00
xiaojunnuo
992f91cf4c Merge branch 'refs/heads/v2' into v2-dev 2025-09-28 10:42:17 +08:00
xiaojunnuo
0c61d4c978 feat: @certd/ui-server module import报错的问题 2025-09-28 09:58:22 +08:00
xiaojunnuo
72d32edf9a build: publish 2025-09-27 09:41:54 +08:00
xiaojunnuo
dde39def9e build: trigger build image 2025-09-27 09:41:37 +08:00
xiaojunnuo
6b43007c44 v1.36.25 2025-09-27 09:40:13 +08:00
xiaojunnuo
876558cf77 build: prepare to build 2025-09-27 09:38:14 +08:00
xiaojunnuo
b35a146edf chore: 2025-09-27 09:37:30 +08:00
xiaojunnuo
86cf6a9908 chore: 2025-09-27 09:30:22 +08:00
xiaojunnuo
b0f7288ac0 build: prepare to build 2025-09-27 09:28:26 +08:00
xiaojunnuo
32fcc1a8fb chore: 2025-09-27 09:27:21 +08:00
xiaojunnuo
eb4d125eaf fix: 固定midwayjs版本,修复ui-server import 错误的bug 2025-09-27 09:26:17 +08:00
xiaojunnuo
87e5cced3c chore: 2025-09-27 09:03:29 +08:00
xiaojunnuo
bcd9ee2d48 build: publish 2025-09-27 08:36:18 +08:00
xiaojunnuo
edf3d87458 build: trigger build image 2025-09-27 08:36:02 +08:00
xiaojunnuo
0c0c353ecc v1.36.24 2025-09-27 08:34:35 +08:00
xiaojunnuo
aaa4c8f899 build: prepare to build 2025-09-27 08:32:23 +08:00
xiaojunnuo
57e3565c11 chore: 2025-09-27 08:32:17 +08:00
xiaojunnuo
fbcf72d762 build: prepare to build 2025-09-27 08:31:29 +08:00
xiaojunnuo
ca8daa836e Merge remote-tracking branch 'origin/v2-dev' into v2-dev 2025-09-27 08:29:44 +08:00
xiaojunnuo
c2ccdbec9d fix: 修复 ui-server 加载失败问题 2025-09-27 08:29:22 +08:00
xiaojunnuo
69aee36e75 chore: 修复 ui-server 加载失败问题 2025-09-27 08:29:16 +08:00
xiaojunnuo
063f5c3b55 fix: 修复 ui-server 加载失败问题 2025-09-27 08:26:18 +08:00
xiaojunnuo
6d1b8ca65e chore: fix ui-server 加载失败问题 2025-09-27 08:24:39 +08:00
xiaojunnuo
03899d4d9c perf: 重置管理员密码同时会关闭验证码,防止验证码失效之后无法登录 2025-09-27 01:47:53 +08:00
xiaojunnuo
2b84af977d build: publish 2025-09-27 01:45:35 +08:00
xiaojunnuo
e15b180322 build: trigger build image 2025-09-27 01:45:19 +08:00
132 changed files with 3051 additions and 1014 deletions

View File

@@ -3,7 +3,7 @@ on:
push:
branches: ['v2-dev']
paths:
- "build.trigger"
- "trigger/build.trigger"
# schedule:
# - # 国际时间 19:17 执行北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
@@ -68,7 +68,7 @@ jobs:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
@@ -82,34 +82,4 @@ jobs:
push: true
context: ./packages/ui/
tags: |
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}
greper/certd:latest
greper/certd:${{steps.get_certd_version.outputs.result}}
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{steps.get_certd_version.outputs.result}}
- name: Build armv7
uses: docker/build-push-action@v6
with:
platforms: linux/arm/v7
push: true
context: ./packages/ui/
tags: |
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}-armv7
greper/certd:armv7
greper/certd:${{steps.get_certd_version.outputs.result}}-armv7
ghcr.io/${{ github.repository }}:armv7
ghcr.io/${{ github.repository }}:${{steps.get_certd_version.outputs.result}}-armv7
# - name: Build agent
# uses: docker/build-push-action@v6
# with:
# platforms: linux/amd64,linux/arm64
# push: true
# context: ./packages/ui/agent/
# tags: |
# registry.cn-shenzhen.aliyuncs.com/handsfree/certd-agent:latest
# registry.cn-shenzhen.aliyuncs.com/handsfree/certd-agent:${{steps.get_certd_version.outputs.result}}
# greper/certd-agent:latest
# greper/certd-agent:${{steps.get_certd_version.outputs.result}}

View File

@@ -3,12 +3,13 @@ on:
push:
branches: ['v2-dev']
paths:
- "deploy.trigger"
- "trigger/deploy.trigger"
workflow_run:
workflows: [ "build-image" ]
types:
- completed
# schedule:
# - # 国际时间 19:17 执行北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
# - cron: '17 19 * * *'
@@ -54,14 +55,3 @@ jobs:
retry-count: 3
retry-delay: 5000
- name: deploy-certd-doc
uses: tyrrrz/action-http-request@master
with:
url: http://flow-openapi.aliyun.com/pipeline/webhook/IiSxLDp9aOhgDUxJPytv
method: POST
body: |
{}
headers: |
Content-Type: application/json
retry-count: 3
retry-delay: 5000

View File

@@ -1,9 +1,13 @@
name: build-image-for-test
name: build-image-for-release
on:
push:
branches: ['v2-dev']
paths:
- "build-dev.trigger"
- "trigger/release.trigger"
# workflow_run:
# workflows: [ "deploy-demo" ]
# types:
# - completed
# schedule:
# - # 国际时间 19:17 执行北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
@@ -20,7 +24,7 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: v2-dev
lfs: true
- name: get_certd_version
id: get_certd_version
@@ -75,17 +79,19 @@ jobs:
username: ${{ secrets.dockerhub_username }}
password: ${{ secrets.dockerhub_password }}
# - name: Build default platforms
# uses: docker/build-push-action@v6
# with:
# platforms: linux/amd64,linux/arm64
# push: true
# context: ./packages/ui/
# tags: |
# registry.cn-shenzhen.aliyuncs.com/handsfree/certd-dev:latest
# greper/certd-dev:latest
# ghcr.io/${{ github.repository }}:dev-latest
- name: Build default platforms
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
push: true
context: ./packages/ui/
tags: |
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}
greper/certd:latest
greper/certd:${{steps.get_certd_version.outputs.result}}
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:${{steps.get_certd_version.outputs.result}}
- name: Build armv7
uses: docker/build-push-action@v6
with:
@@ -96,4 +102,30 @@ jobs:
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:armv7
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}-armv7
greper/certd:armv7
greper/certd:${{steps.get_certd_version.outputs.result}}-armv7
greper/certd:${{steps.get_certd_version.outputs.result}}-armv7
ghcr.io/${{ github.repository }}:armv7
ghcr.io/${{ github.repository }}:${{steps.get_certd_version.outputs.result}}-armv7
# - name: Build agent
# uses: docker/build-push-action@v6
# with:
# platforms: linux/amd64,linux/arm64
# push: true
# context: ./packages/ui/agent/
# tags: |
# registry.cn-shenzhen.aliyuncs.com/handsfree/certd-agent:latest
# registry.cn-shenzhen.aliyuncs.com/handsfree/certd-agent:${{steps.get_certd_version.outputs.result}}
# greper/certd-agent:latest
# greper/certd-agent:${{steps.get_certd_version.outputs.result}}
- name: deploy-certd-doc
uses: tyrrrz/action-http-request@master
with:
url: http://flow-openapi.aliyun.com/pipeline/webhook/IiSxLDp9aOhgDUxJPytv
method: POST
body: |
{}
headers: |
Content-Type: application/json
retry-count: 3
retry-delay: 5000

4
.npmrc
View File

@@ -1,2 +1,6 @@
link-workspace-packages=deep
prefer-workspace-packages=true
better_sqlite3_binary_host=https://registry.npmmirror.com/-/binary/better-sqlite3
better_sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/better-sqlite3
better-sqlite3_binary_host=https://registry.npmmirror.com/-/binary/better-sqlite3
better-sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/better-sqlite3

8
.vscode/launch.json vendored
View File

@@ -10,8 +10,8 @@
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}/packages/ui/certd-client",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"],
"runtimeExecutable": "pnpm",
"runtimeArgs": ["dev"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},
@@ -20,8 +20,8 @@
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}/packages/ui/certd-server",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "dev"],
"runtimeExecutable": "pnpm",
"runtimeArgs": ["dev"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen"
},

View File

@@ -1,4 +1,5 @@
{
"eslint.debug": false,
"eslint.format.enable": true
"eslint.format.enable": true,
"typescript.tsc.autoDetect": "watch"
}

View File

@@ -3,6 +3,59 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
### Bug Fixes
* 修复飞牛证书部署后无法生效的bug ([bf156a1](https://github.com/certd/certd/commit/bf156a13bd443cdadb73c9dff79bbef7231b4401))
* aliyunoss 选择证书接入点选择新加坡无法上传的bug ([e00733a](https://github.com/certd/certd/commit/e00733a34644c23ffe926486b15dc96bf2fa4b57))
### Performance Improvements
* 优化start.sh脚本去掉删除非ui目录的操作及提示 ([7993a7c](https://github.com/certd/certd/commit/7993a7cdb01885535950c63187e3f67d67ba2f75))
* 增加飞牛证书id选择的提示 ([5a4d812](https://github.com/certd/certd/commit/5a4d8121462b1afe921d028465687be8c9679814))
* 证书监控支持设置证书即将过期天数 ([cd35568](https://github.com/certd/certd/commit/cd35568e042e6ab928685efad51cdbed823d2d4f))
* 支持网络测试 ([2bef608](https://github.com/certd/certd/commit/2bef608e07ceb56d52007f290667e0afef401b22))
* 支持新网代理方式 ([f612509](https://github.com/certd/certd/commit/f612509cac87b859e81a7a52fe94b2eaccad22f9))
* dns支持新网互联 ([f415190](https://github.com/certd/certd/commit/f41519048326d971acd9e0a30462231f77a299a6))
* start.sh脚本支持根据当前系统判断是否使用sudo ([567cb7d](https://github.com/certd/certd/commit/567cb7d737023e26ec58403c6f28f109e212d379))
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
### Bug Fixes
* 修复版本比较bug ([109696e](https://github.com/certd/certd/commit/109696e965d68c50c8627ffd40203edd1d2daea5))
* 修复某些情况下cname申请证书报错主域名不一致的bug ([2671781](https://github.com/certd/certd/commit/2671781e1bb0838981728d85eacf0e1a25a0fa48))
### Performance Improvements
* cname主域名校验提示优化显示不一致的两方便于排查问题 ([6ebb365](https://github.com/certd/certd/commit/6ebb3659f42155e4e8da600c493fb5227cd08137))
* dns解析支持阿里esa ([9291fa6](https://github.com/certd/certd/commit/9291fa68aa7a88a05c2f888bf3048df36a8fbde3))
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
### Features
* @certd/ui-server module import报错的问题 ([0c61d4c](https://github.com/certd/certd/commit/0c61d4c9788677c83c567db5381b9e257ec90bba))
* dist打包前检查 ([8f6e5bd](https://github.com/certd/certd/commit/8f6e5bd24b3b65fbfcba36c08f532a3abad2d606))
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
### Bug Fixes
* 固定midwayjs版本修复ui-server import 错误的bug ([eb4d125](https://github.com/certd/certd/commit/eb4d125eaf4a41e88c752d0c68993829589f8f27))
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
### Bug Fixes
* 修复 ui-server 加载失败问题 ([c2ccdbe](https://github.com/certd/certd/commit/c2ccdbec9dd08bca4688eeb2f34d0105eec43ba1))
* 修复 ui-server 加载失败问题 ([063f5c3](https://github.com/certd/certd/commit/063f5c3b55e47df22543a64f02e039e84f92cd14))
### Performance Improvements
* 重置管理员密码同时会关闭验证码,防止验证码失效之后无法登录 ([03899d4](https://github.com/certd/certd/commit/03899d4d9c76fc2077dacc53ab88e2c9ca41af7c))
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
### Bug Fixes

View File

@@ -1 +0,0 @@
2

View File

@@ -1 +0,0 @@
01:50

View File

@@ -1 +0,0 @@
5

View File

@@ -47,6 +47,8 @@ services:
# 配置规则: certd_ + 配置项, 点号用_代替
# #↓↓↓↓ ----------------------------- 如果忘记管理员密码可以设置为truedocker compose up -d 重建容器之后管理员密码将改成123456然后请及时修改回false
- certd_system_resetAdminPasswd=false
# ↓↓↓ 要使用ipv6将此配置修改为::
- certd_koa_hostname=0.0.0.0
# 默认使用sqlite文件数据库如果需要使用其他数据库请设置以下环境变量
# 注意: 选定使用一种数据库之后,不支持更换数据库。

View File

@@ -3,6 +3,56 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
### Bug Fixes
* 修复版本比较bug ([109696e](https://github.com/certd/certd/commit/109696e965d68c50c8627ffd40203edd1d2daea5))
* 修复某些情况下cname申请证书报错主域名不一致的bug ([2671781](https://github.com/certd/certd/commit/2671781e1bb0838981728d85eacf0e1a25a0fa48))
### Performance Improvements
* cname主域名校验提示优化显示不一致的两方便于排查问题 ([6ebb365](https://github.com/certd/certd/commit/6ebb3659f42155e4e8da600c493fb5227cd08137))
* dns解析支持阿里esa ([9291fa6](https://github.com/certd/certd/commit/9291fa68aa7a88a05c2f888bf3048df36a8fbde3))
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
### Features
* @certd/ui-server module import报错的问题 ([0c61d4c](https://github.com/certd/certd/commit/0c61d4c9788677c83c567db5381b9e257ec90bba))
* dist打包前检查 ([8f6e5bd](https://github.com/certd/certd/commit/8f6e5bd24b3b65fbfcba36c08f532a3abad2d606))
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
### Bug Fixes
* 固定midwayjs版本修复ui-server import 错误的bug ([eb4d125](https://github.com/certd/certd/commit/eb4d125eaf4a41e88c752d0c68993829589f8f27))
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
### Bug Fixes
* 修复 ui-server 加载失败问题 ([c2ccdbe](https://github.com/certd/certd/commit/c2ccdbec9dd08bca4688eeb2f34d0105eec43ba1))
* 修复 ui-server 加载失败问题 ([063f5c3](https://github.com/certd/certd/commit/063f5c3b55e47df22543a64f02e039e84f92cd14))
### Performance Improvements
* 重置管理员密码同时会关闭验证码,防止验证码失效之后无法登录 ([03899d4](https://github.com/certd/certd/commit/03899d4d9c76fc2077dacc53ab88e2c9ca41af7c))
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
### Bug Fixes
* 授权页面id列位置不在第一列的bug ([3f1722d](https://github.com/certd/certd/commit/3f1722d54debcb4849dc14521a2da0d9b304b69f))
### Performance Improvements
* 动态加载验证码script ([dcc396a](https://github.com/certd/certd/commit/dcc396afb7a23aeb8af57c01014b09af5f033e61))
* 开启子域名托管之后cname记录支持重置 ([54c8d62](https://github.com/certd/certd/commit/54c8d622437761d350db0f17e07f7517f1911211))
* 手动上传证书优化,增加到期前报错提醒 ([3d42bfd](https://github.com/certd/certd/commit/3d42bfd479eaacc4a49c401224815a6e2a0204b0))
* 验证码支持测试,登录验证码需要测试通过后才能开启 ([83e6476](https://github.com/certd/certd/commit/83e6476408090b741fabb1b542fb458d9a8b4134))
* 支持腾讯云验证码 ([03f317f](https://github.com/certd/certd/commit/03f317ffdb6595ce70e8a2302b05f390c52110c8))
## [1.36.22](https://github.com/certd/certd/compare/v1.36.21...v1.36.22) (2025-09-23)
### Bug Fixes

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -5,6 +5,9 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具
![首页](../images/start/home.png)
## 1、关于证书续期
>* 实际上没有办法不改变证书文件本身情况下直接续期或者续签。
>* 我们所说的续期,其实就是按照全套流程重新申请一份新证书,然后重新部署上去。
@@ -15,7 +18,7 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
* 全自动申请证书支持所有注册商注册的域名支持DNS-01、HTTP-01、CNAME代理等多种域名验证方式
* 全自动部署更新证书(目前支持部署到主机、阿里云、腾讯云等70+部署插件)
* 全自动部署更新证书(目前支持部署到主机、阿里云、腾讯云等100+部署插件)
* 支持通配符域名/泛域名支持多个域名打到一个证书上支持pem、pfx、der、jks等多种证书格式
* 邮件通知、webhook通知、企微、钉钉、飞书、anpush等多种通知方式
* 私有化部署,数据保存本地,安装升级非常简单快捷

View File

@@ -11,9 +11,12 @@
git clone https://github.com/certd/certd --depth=1
# git checkout v1.x.x # 当v2主干分支代码无法正常启动时可以尝试此命令1.x.x换成最新版本号
cd certd
# 启动服务
./start.sh
```
>如果是windows请先安装`git for windows` ,然后右键,选择`open git bash here`打开终端,再执行`./start.sh`命令

View File

@@ -19,7 +19,7 @@
"detail": too many certificates (5) already issued for this exact set of idantifiers in the last 168hm0s
```
## ssl.com报错 CAA record does not include ssl.com which is required to issue the certificate
## 4. ssl.com报错 CAA record does not include ssl.com which is required to issue the certificate
ssl.com申请证书要求必须设置CAA记录表示允许ssl.com为该域名颁发证书
请按如下格式添加CAA记录
@@ -29,5 +29,18 @@ ssl.com申请证书要求必须设置CAA记录表示允许ssl.com为该域名
| 一级泛域名 | CAA | * | 0 | issue/issuewild | "ssl.com" |
| 固定子域名 | CAA | sub | 0 | issue |"ssl.com" |
## 5. address family not supported
启动时出现此错误是由于您的服务器不支持绑定ipv6地址
请配置环境变量 certd_koa_hostname=0.0.0.0
在docker-compose.yml中添加如下配置
```yaml
service:
certd:
environment:
certd_koa_hostname: 0.0.0.0
```

View File

@@ -7,10 +7,16 @@
https://certd.handsfree.work/
> 注意数据将不定期清理,不定期停止定时任务,生产使用请自行部署
注册 -> 创建证书流水线 -> 添加部署任务 -> 测试运行
> 注意demo的数据将不定期清理生产使用请自行部署
> 包含敏感信息,务必自己本地部署进行生产使用
![首页](../images/start/home-2.png)
## 二、私有化部署
由于证书、授权信息等属于高度敏感数据,请务必私有化部署,保障数据安全

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

View File

@@ -24,13 +24,13 @@ features:
- title: 全自动申请证书
details: 支持所有注册商注册的域名
- title: 全自动部署证书
details: 支持部署到主机、阿里云、腾讯云等,目前已支持60+部署插件
details: 支持部署到主机、阿里云、腾讯云等,目前已支持100+部署插件
- title: 多域名、泛域名打到一个证书上
details: 支持通配符域名/泛域名,支持多个域名打到一个证书上
- title: 多证书格式支持
details: 支持pem、pfx、der、jks等多种证书格式支持Google、Letsencrypt、ZeroSSL证书颁发机构
- title: 支持私有化部署
details: 授权数据加密存储,保障数据安全
- title: 多数据库支持
details: 支持SQLite、Postgresql、MySQL数据库
- title: 私有化部署,数据安全
details: 授权数据加密存储,保障数据安全支持SQLite、Postgresql、MySQL多种数据库
- title: 无痛升级
details: 有手就行,向下兼容,无需担心数据作废
---

View File

@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.36.23"
"version": "1.37.2"
}

View File

@@ -18,7 +18,7 @@
"devb": "lerna run dev-build",
"i-all": "lerna link && lerna exec npm install ",
"publish": "npm run prepublishOnly2 && lerna publish --force-publish=pro/plus-core --conventional-commits --create-release github && npm run afterpublishOnly && npm run commitAll",
"afterpublishOnly": "npm run copylogs && time /t >build.trigger && git add ./build.trigger && git commit -m \"build: trigger build image\" && TIMEOUT /T 10 && git push",
"afterpublishOnly": "npm run copylogs && time /t >trigger/build.trigger && git add ./trigger/build.trigger && git commit -m \"build: trigger build image\" && TIMEOUT /T 10 && git push",
"transform-sql": "cd ./packages/ui/certd-server/db/ && node --experimental-json-modules transform.js",
"commitAll": "git add . && git commit -m \"build: publish\" && git push && npm run commitPro",
"commitPro": "cd ./packages/pro/ && git add . && git commit -m \"build: publish\" && git push",
@@ -33,11 +33,11 @@
"docs:dev": "vitepress dev docs",
"docs:build": "npm run copylogs && vitepress build docs",
"docs:preview": "vitepress preview docs",
"pub": "echo 1"
"pub": "echo 1",
"dev": "pnpm run -r --parallel compile "
},
"license": "AGPL-3.0",
"dependencies": {
"@certd/ui-server": "link:packages/ui/certd-server",
"axios": "^1.7.7",
"copyfiles": "^2.4.1",
"lodash-es": "^4.17.21",

View File

@@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/publishlab/node-acme-client/compare/v1.37.1...v1.37.2) (2025-10-14)
### Bug Fixes
* aliyunoss 选择证书接入点选择新加坡无法上传的bug ([e00733a](https://github.com/publishlab/node-acme-client/commit/e00733a34644c23ffe926486b15dc96bf2fa4b57))
## [1.37.1](https://github.com/publishlab/node-acme-client/compare/v1.37.0...v1.37.1) (2025-09-29)
**Note:** Version bump only for package @certd/acme-client
# [1.37.0](https://github.com/publishlab/node-acme-client/compare/v1.36.25...v1.37.0) (2025-09-28)
**Note:** Version bump only for package @certd/acme-client
## [1.36.25](https://github.com/publishlab/node-acme-client/compare/v1.36.24...v1.36.25) (2025-09-27)
**Note:** Version bump only for package @certd/acme-client
## [1.36.24](https://github.com/publishlab/node-acme-client/compare/v1.36.23...v1.36.24) (2025-09-27)
**Note:** Version bump only for package @certd/acme-client
## [1.36.23](https://github.com/publishlab/node-acme-client/compare/v1.36.22...v1.36.23) (2025-09-26)
**Note:** Version bump only for package @certd/acme-client

View File

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

View File

@@ -255,7 +255,7 @@ export default async (client, userOpts) => {
await wait(waitDnsDiffuseTime * 1000)
}
log("开始向提供商请求挑战验证");
log("开始向提供商请求检查验证");
await runPromisePa(completeChallengeTasks, 1000);
} catch (e) {
log(`证书申请失败${e.message}`);

View File

@@ -502,7 +502,7 @@ class AcmeClient {
await verify[challenge.type](authz, challenge, keyAuthorization);
};
log('Waiting for ACME challenge verification等待ACME挑战验证)');
log('Waiting for ACME challenge verification等待ACME检查验证)');
return util.retry(verifyFn, this.backoffOpts);
}
@@ -570,7 +570,7 @@ class AcmeClient {
const resp = await this.api.apiRequest(item.url, null, [200]);
/* Verify status */
log(`[${d}] Item has status挑战状态): ${resp.data.status}`);
log(`[${d}] Item has status检查状态): ${resp.data.status}`);
if (invalidStates.includes(resp.data.status)) {
abort();

View File

@@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
### Performance Improvements
* 支持网络测试 ([2bef608](https://github.com/certd/certd/commit/2bef608e07ceb56d52007f290667e0afef401b22))
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
**Note:** Version bump only for package @certd/basic
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
**Note:** Version bump only for package @certd/basic
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
**Note:** Version bump only for package @certd/basic
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
**Note:** Version bump only for package @certd/basic
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
**Note:** Version bump only for package @certd/basic

View File

@@ -1 +1 @@
01:39
22:48

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.36.23",
"version": "1.37.2",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -13,7 +13,8 @@
"dev-build": "npm run build",
"preview": "vite preview",
"test": "mocha --loader=ts-node/esm",
"pub": "npm publish"
"pub": "npm publish",
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"axios": "^1.7.2",
@@ -45,5 +46,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "c98f43b98459d107910c95d23fda785b0edc4702"
"gitHead": "c725cee0445dbe1ebd1b6588373bde31697113da"
}

View File

@@ -22,12 +22,14 @@ import { sp } from "./util.sp.js";
import { hashUtils } from "./util.hash.js";
import { promises } from "./util.promise.js";
import { fileUtils } from "./util.file.js";
import * as _ from "lodash-es";
import { cache } from "./util.cache.js";
import dayjs from "dayjs";
import { domainUtils } from "./util.domain.js";
export * from "./util.domain.js";
import { optionsUtils } from "./util.options.js";
export * from "./util.options.js";
import { amountUtils } from "./util.amount.js";
export * from "./util.amount.js";
import { nanoid } from "nanoid";
import * as id from "./util.id.js";
import { locker } from "./util.lock.js";
@@ -43,7 +45,6 @@ export const utils = {
hash: hashUtils,
promises,
file: fileUtils,
_,
mergeUtils,
cache,
nanoid,

View File

@@ -1,8 +1,10 @@
//转换为import
import childProcess from 'child_process';
import { safePromise } from './util.promise.js';
import { ILogger, logger } from './util.log.js';
import iconv from 'iconv-lite';
//@ts-ignore
import childProcess from "child_process";
import { safePromise } from "./util.promise.js";
import { ILogger, logger } from "./util.log.js";
//@ts-ignore
import iconv from "iconv-lite";
export type ExecOption = {
cmd: string | string[];
env: any;
@@ -11,12 +13,12 @@ export type ExecOption = {
};
async function exec(opts: ExecOption): Promise<string> {
let cmd = '';
let cmd = "";
const log = opts.logger || logger;
if (opts.cmd instanceof Array) {
for (const item of opts.cmd) {
if (cmd) {
cmd += ' && ' + item;
cmd += " && " + item;
} else {
cmd = item;
}
@@ -28,17 +30,18 @@ async function exec(opts: ExecOption): Promise<string> {
cmd,
{
env: {
//@ts-ignore
...process.env,
...opts.env,
},
...opts.options,
},
(error, stdout, stderr) => {
(error: any, stdout: { toString: (arg0: string) => any }, stderr: any) => {
if (error) {
log.error(`exec error: ${error}`);
reject(error);
} else {
const res = stdout.toString('utf-8');
const res = stdout.toString("utf-8");
log.info(`stdout: ${res}`);
resolve(res);
}
@@ -57,11 +60,12 @@ export type SpawnOption = {
};
function isWindows() {
return process.platform === 'win32';
// @ts-ignore
return process.platform === "win32";
}
function convert(buffer: any) {
if (isWindows()) {
const decoded = iconv.decode(buffer, 'GBK');
const decoded = iconv.decode(buffer, "GBK");
// 检查是否有有效字符
return decoded && decoded.trim().length > 0 ? decoded : buffer.toString();
} else {
@@ -74,12 +78,12 @@ function convert(buffer: any) {
// }
async function spawn(opts: SpawnOption): Promise<string> {
let cmd = '';
let cmd = "";
const log = opts.logger || logger;
if (opts.cmd instanceof Array) {
for (const item of opts.cmd) {
if (cmd) {
cmd += ' && ' + item;
cmd += " && " + item;
} else {
cmd = item;
}
@@ -88,37 +92,47 @@ async function spawn(opts: SpawnOption): Promise<string> {
cmd = opts.cmd;
}
log.info(`执行命令: ${cmd}`);
let stdout = '';
let stderr = '';
let stdout = "";
let stderr = "";
return safePromise((resolve, reject) => {
const ls = childProcess.spawn(cmd, {
shell: true,
env: {
//@ts-ignore
...process.env,
...opts.env,
},
...opts.options,
});
ls.stdout.on('data', data => {
ls.stdout.on("data", (data: string) => {
data = convert(data);
log.info(`stdout: ${data}`);
stdout += data;
});
ls.stderr.on('data', data => {
ls.stderr.on("data", (data: string) => {
data = convert(data);
log.warn(`stderr: ${data}`);
stderr += data;
});
ls.on('error', error => {
ls.on("error", (error: any) => {
log.error(`child process error: ${error}`);
//@ts-ignore
error.stderr = stderr;
//@ts-ignore
error.stdout = stdout;
reject(error);
});
ls.on('close', (code: number) => {
ls.on("close", (code: number) => {
if (code !== 0) {
log.error(`child process exited with code ${code}`);
reject(new Error(stderr));
const e = new Error(stderr || "return " + code);
//@ts-ignore
e.stderr = stderr;
//@ts-ignore
e.stdout = stdout;
reject(e);
} else {
resolve(stdout);
}

View File

@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
**Note:** Version bump only for package @certd/pipeline
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
**Note:** Version bump only for package @certd/pipeline
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
**Note:** Version bump only for package @certd/pipeline
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
**Note:** Version bump only for package @certd/pipeline
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
**Note:** Version bump only for package @certd/pipeline
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
### Performance Improvements

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.36.23",
"version": "1.37.2",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -14,11 +14,12 @@
"build3": "rollup -c",
"preview": "vite preview",
"test": "mocha --loader=ts-node/esm",
"pub": "npm publish"
"pub": "npm publish",
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/basic": "^1.36.23",
"@certd/plus-core": "^1.36.23",
"@certd/basic": "^1.37.2",
"@certd/plus-core": "^1.37.2",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"reflect-metadata": "^0.1.13"
@@ -44,5 +45,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "c98f43b98459d107910c95d23fda785b0edc4702"
"gitHead": "c725cee0445dbe1ebd1b6588373bde31697113da"
}

View File

@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
**Note:** Version bump only for package @certd/lib-huawei
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
**Note:** Version bump only for package @certd/lib-huawei
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
**Note:** Version bump only for package @certd/lib-huawei
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
**Note:** Version bump only for package @certd/lib-huawei
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
**Note:** Version bump only for package @certd/lib-huawei
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
**Note:** Version bump only for package @certd/lib-huawei

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.36.23",
"version": "1.37.2",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -24,5 +24,5 @@
"prettier": "^2.8.8",
"tslib": "^2.8.1"
},
"gitHead": "c98f43b98459d107910c95d23fda785b0edc4702"
"gitHead": "c725cee0445dbe1ebd1b6588373bde31697113da"
}

View File

@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
**Note:** Version bump only for package @certd/lib-iframe
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
**Note:** Version bump only for package @certd/lib-iframe
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
**Note:** Version bump only for package @certd/lib-iframe
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
**Note:** Version bump only for package @certd/lib-iframe
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
**Note:** Version bump only for package @certd/lib-iframe
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
**Note:** Version bump only for package @certd/lib-iframe

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.36.23",
"version": "1.37.2",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -31,5 +31,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "c98f43b98459d107910c95d23fda785b0edc4702"
"gitHead": "c725cee0445dbe1ebd1b6588373bde31697113da"
}

View File

@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
**Note:** Version bump only for package @certd/jdcloud
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
**Note:** Version bump only for package @certd/jdcloud
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
**Note:** Version bump only for package @certd/jdcloud
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
**Note:** Version bump only for package @certd/jdcloud
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
**Note:** Version bump only for package @certd/jdcloud
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
**Note:** Version bump only for package @certd/jdcloud

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/jdcloud",
"version": "1.36.23",
"version": "1.37.2",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
@@ -61,5 +61,5 @@
"fetch"
]
},
"gitHead": "c98f43b98459d107910c95d23fda785b0edc4702"
"gitHead": "c725cee0445dbe1ebd1b6588373bde31697113da"
}

View File

@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
**Note:** Version bump only for package @certd/lib-k8s
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
**Note:** Version bump only for package @certd/lib-k8s
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
**Note:** Version bump only for package @certd/lib-k8s
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
**Note:** Version bump only for package @certd/lib-k8s
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
**Note:** Version bump only for package @certd/lib-k8s
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
**Note:** Version bump only for package @certd/lib-k8s

View File

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

View File

@@ -3,6 +3,32 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
**Note:** Version bump only for package @certd/lib-server
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
**Note:** Version bump only for package @certd/lib-server
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
### Features
* dist打包前检查 ([8f6e5bd](https://github.com/certd/certd/commit/8f6e5bd24b3b65fbfcba36c08f532a3abad2d606))
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
### Bug Fixes
* 固定midwayjs版本修复ui-server import 错误的bug ([eb4d125](https://github.com/certd/certd/commit/eb4d125eaf4a41e88c752d0c68993829589f8f27))
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
### Bug Fixes
* 修复 ui-server 加载失败问题 ([c2ccdbe](https://github.com/certd/certd/commit/c2ccdbec9dd08bca4688eeb2f34d0105eec43ba1))
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
### Performance Improvements

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.36.23",
"version": "1.37.2",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -17,7 +17,8 @@
"lint": "mwts check",
"lint:fix": "mwts fix",
"prepublish": "npm run build",
"pub": "npm publish"
"pub": "npm publish",
"compile": "tsc --skipLibCheck --watch"
},
"keywords": [],
"author": "greper",
@@ -27,19 +28,20 @@
],
"license": "AGPL",
"dependencies": {
"@certd/acme-client": "^1.36.23",
"@certd/basic": "^1.36.23",
"@certd/pipeline": "^1.36.23",
"@certd/plugin-lib": "^1.36.23",
"@certd/plus-core": "^1.36.23",
"@midwayjs/cache": "~3.14.0",
"@midwayjs/core": "~3.20.3",
"@midwayjs/i18n": "~3.20.3",
"@midwayjs/info": "~3.20.3",
"@midwayjs/koa": "~3.20.3",
"@midwayjs/logger": "~3.4.2",
"@midwayjs/typeorm": "~3.20.3",
"@midwayjs/upload": "^3.20.3",
"@certd/acme-client": "^1.37.2",
"@certd/basic": "^1.37.2",
"@certd/pipeline": "^1.37.2",
"@certd/plugin-lib": "^1.37.2",
"@certd/plus-core": "^1.37.2",
"@midwayjs/cache": "3.14.0",
"@midwayjs/core": "3.20.11",
"@midwayjs/i18n": "3.20.13",
"@midwayjs/info": "3.20.13",
"@midwayjs/koa": "3.20.13",
"@midwayjs/logger": "3.4.2",
"@midwayjs/typeorm": "3.20.11",
"@midwayjs/upload": "3.20.13",
"@midwayjs/validate": "3.20.13",
"better-sqlite3": "^11.1.2",
"cross-env": "^7.0.3",
"dayjs": "^1.11.7",
@@ -62,5 +64,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "c98f43b98459d107910c95d23fda785b0edc4702"
"gitHead": "c725cee0445dbe1ebd1b6588373bde31697113da"
}

View File

@@ -8,3 +8,5 @@ export * from './common-exception.js';
export * from './not-found-exception.js';
export * from './param-exception.js';
export * from './site-off-exception.js';
export * from './login-error-exception.js'
export * from './code-error-exception.js'

View File

@@ -3,7 +3,7 @@ import { AppKey, PlusRequestService } from '@certd/plus-core';
import { cache, http, HttpRequestConfig, logger } from '@certd/basic';
import { SysInstallInfo, SysLicenseInfo, SysSettingsService } from '../../settings/index.js';
import { merge } from 'lodash-es';
import fs from 'fs';
@Provide("plusService")
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class PlusService {
@@ -85,12 +85,31 @@ export class PlusService {
async sendEmail(email: any) {
const plusRequestService = await this.getPlusRequestService();
let attachments = email.attachments || [];
if (attachments.length > 0) {
const newAttachments: any[] = [];
attachments.forEach((item: any) => {
const name = item.filename || item.path.split('/').pop();
const body = item.content || fs.readFileSync(item.path);
const bodyBase64 = Buffer.from(body).toString('base64');
item = {
name,
body: bodyBase64,
};
newAttachments.push(item);
});
attachments = newAttachments;
}
await plusRequestService.request({
url: '/activation/emailSend',
data: {
subject: email.subject,
text: email.content,
to: email.receivers,
text: email.content,
html: email.html,
attachments,
},
});
}

View File

@@ -1,5 +1,3 @@
export * from './api/index.js'
export * from './entity/addon.js'
export * from './service/addon-service.js'
export * from './service/addon-getter.js'
export * from './service/addon-sys-getter.js'

View File

@@ -1,18 +0,0 @@
import { IAddonGetter } from "../api/index.js";
export class AddonGetter implements IAddonGetter {
userId: number;
getter: <T>(id: any, userId?: number) => Promise<T>;
constructor(userId: number, getter: (id: any, userId: number) => Promise<any>) {
this.userId = userId;
this.getter = getter;
}
async getById<T = any>(id: any) {
return await this.getter<T>(id, this.userId);
}
async getCommonById<T = any>(id: any) {
return await this.getter<T>(id, 0);
}
}

View File

@@ -1,24 +1,19 @@
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { InjectEntityModel } from "@midwayjs/typeorm";
import { In, Repository } from "typeorm";
import { AddonDefine, BaseService, PageReq, PermissionException, ValidateException } from "../../../index.js";
import { addonRegistry, newAddon } from "../api/index.js";
import { AddonDefine, BaseService, PageReq, ValidateException } from "../../../index.js";
import { addonRegistry } from "../api/index.js";
import { AddonEntity } from "../entity/addon.js";
import { http, logger, utils } from "@certd/basic";
import { TaskServiceBuilder } from "@certd/ui-server/dist/modules/pipeline/service/getter/task-service-getter.js";
/**
* Addon
*/
@Provide()
@Scope(ScopeEnum.Request, {allowDowngrade: true})
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class AddonService extends BaseService<AddonEntity> {
@InjectEntityModel(AddonEntity)
repository: Repository<AddonEntity>;
@Inject()
private taskServiceBuilder: TaskServiceBuilder;
//@ts-ignore
getRepository() {
return this.repository;
@@ -34,21 +29,21 @@ export class AddonService extends BaseService<AddonEntity> {
async add(param) {
let oldEntity = null;
if (param._copyFrom){
if (param._copyFrom) {
oldEntity = await this.info(param._copyFrom);
if (oldEntity == null) {
throw new ValidateException('该Addon配置不存在,请确认是否已被删除');
throw new ValidateException("该Addon配置不存在,请确认是否已被删除");
}
if (oldEntity.userId !== param.userId) {
throw new ValidateException('您无权查看该Addon配置');
if (oldEntity.userId !== param.userId) {
throw new ValidateException("您无权查看该Addon配置");
}
}
if (!param.userId){
param.isSystem = true
}else{
param.isSystem = false
if (!param.userId) {
param.isSystem = true;
} else {
param.isSystem = false;
}
delete param._copyFrom
delete param._copyFrom;
return await super.add(param);
}
@@ -60,7 +55,7 @@ export class AddonService extends BaseService<AddonEntity> {
async update(param) {
const oldEntity = await this.info(param.id);
if (oldEntity == null) {
throw new ValidateException('该Addon配置不存在,请确认是否已被删除');
throw new ValidateException("该Addon配置不存在,请确认是否已被删除");
}
return await super.update(param);
}
@@ -68,64 +63,24 @@ export class AddonService extends BaseService<AddonEntity> {
async getSimpleInfo(id: number) {
const entity = await this.info(id);
if (entity == null) {
throw new ValidateException('该Addon配置不存在,请确认是否已被删除');
throw new ValidateException("该Addon配置不存在,请确认是否已被删除");
}
return {
id: entity.id,
name: entity.name,
userId: entity.userId,
addonType: entity.addonType,
type: entity.type,
type: entity.type
};
}
async getAddonById(id: any, checkUserId: boolean, userId?: number): Promise<any> {
const serviceGetter = this.taskServiceBuilder.create({userId:userId??0})
const ctx = {
http: http,
logger: logger,
utils: utils,
serviceGetter
};
if (!id){
//使用图片验证码
return await newAddon("captcha", "image", {},ctx);
}
const entity = await this.info(id);
if (entity == null) {
//使用图片验证码
return await newAddon("captcha", "image", {},ctx);
}
if (checkUserId) {
if (userId == null) {
throw new ValidateException('userId不能为空');
}
if (userId !== entity.userId) {
throw new PermissionException('您对该Addon无访问权限');
}
}
const setting = JSON.parse(entity.setting ??"{}")
const input = {
id: entity.id,
...setting,
};
return await newAddon(entity.addonType, entity.type, input,ctx);
}
async getById(id: any, userId: number): Promise<any> {
return await this.getAddonById(id, true, userId);
}
getDefineList(addonType: string) {
return addonRegistry.getDefineList();
}
getDefineByType(type: string,prefix?: string) {
return addonRegistry.getDefine(type,prefix) as AddonDefine;
getDefineByType(type: string, prefix?: string) {
return addonRegistry.getDefine(type, prefix) as AddonDefine;
}
@@ -139,31 +94,30 @@ export class AddonService extends BaseService<AddonEntity> {
return await this.repository.find({
where: {
id: In(ids),
userId,
userId
},
select: {
id: true,
name: true,
addonType: true,
type: true,
userId:true,
isSystem: true,
},
userId: true,
isSystem: true
}
});
}
async getDefault(userId: number,addonType: string): Promise<any> {
async getDefault(userId: number, addonType: string): Promise<any> {
const res = await this.repository.findOne({
where: {
userId,
addonType
},
order: {
isDefault: 'DESC',
},
isDefault: "DESC"
}
});
if (!res) {
return null;
@@ -179,16 +133,16 @@ export class AddonService extends BaseService<AddonEntity> {
type: res.type,
name: res.name,
userId: res.userId,
setting,
setting
};
}
async setDefault(id: number, userId: number,addonType:string) {
async setDefault(id: number, userId: number, addonType: string) {
if (!id) {
throw new ValidateException('id不能为空');
throw new ValidateException("id不能为空");
}
if (!userId) {
throw new ValidateException('userId不能为空');
throw new ValidateException("userId不能为空");
}
await this.repository.update(
{
@@ -196,7 +150,7 @@ export class AddonService extends BaseService<AddonEntity> {
addonType
},
{
isDefault: false,
isDefault: false
}
);
await this.repository.update(
@@ -206,22 +160,22 @@ export class AddonService extends BaseService<AddonEntity> {
addonType
},
{
isDefault: true,
isDefault: true
}
);
}
async getOrCreateDefault(opts:{addonType:string,type:string, inputs: any, userId: any}) {
const {addonType,type,inputs,userId} = opts;
async getOrCreateDefault(opts: { addonType: string, type: string, inputs: any, userId: any }) {
const { addonType, type, inputs, userId } = opts;
const addonDefine = this.getDefineByType( type,addonType)
const addonDefine = this.getDefineByType(type, addonType);
const defaultConfig = await this.getDefault(userId,addonType);
const defaultConfig = await this.getDefault(userId, addonType);
if (defaultConfig) {
return defaultConfig;
}
const setting = {
...inputs,
...inputs
};
const res = await this.repository.save({
userId,
@@ -229,7 +183,7 @@ export class AddonService extends BaseService<AddonEntity> {
type: type,
name: addonDefine.title,
setting: JSON.stringify(setting),
isDefault: true,
isDefault: true
});
return this.buildAddonInstanceConfig(res);
}

View File

@@ -1,17 +0,0 @@
import { IAccessService } from '@certd/pipeline';
import { AddonService } from './addon-service.js';
export class AddonSysGetter implements IAccessService {
addonService: AddonService;
constructor(addonService: AddonService) {
this.addonService = addonService;
}
async getById<T = any>(id: any) {
return await this.addonService.getById(id, 0);
}
async getCommonById<T = any>(id: any) {
return await this.addonService.getById(id, 0);
}
}

View File

@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
**Note:** Version bump only for package @certd/midway-flyway-js
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
**Note:** Version bump only for package @certd/midway-flyway-js

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
"version": "1.36.23",
"version": "1.37.2",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -25,9 +25,9 @@
],
"license": "MIT",
"dependencies": {
"@midwayjs/core": "~3.20.3",
"@midwayjs/logger": "~3.4.2",
"@midwayjs/typeorm": "~3.20.3",
"@midwayjs/core": "3.20.11",
"@midwayjs/logger": "3.4.2",
"@midwayjs/typeorm": "3.20.11",
"better-sqlite3": "^11.1.2"
},
"devDependencies": {
@@ -46,5 +46,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "c98f43b98459d107910c95d23fda785b0edc4702"
"gitHead": "c725cee0445dbe1ebd1b6588373bde31697113da"
}

View File

@@ -3,6 +3,31 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
### Performance Improvements
* 证书监控支持设置证书即将过期天数 ([cd35568](https://github.com/certd/certd/commit/cd35568e042e6ab928685efad51cdbed823d2d4f))
* 支持新网代理方式 ([f612509](https://github.com/certd/certd/commit/f612509cac87b859e81a7a52fe94b2eaccad22f9))
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
### Performance Improvements
* cname主域名校验提示优化显示不一致的两方便于排查问题 ([6ebb365](https://github.com/certd/certd/commit/6ebb3659f42155e4e8da600c493fb5227cd08137))
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
**Note:** Version bump only for package @certd/plugin-cert
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
**Note:** Version bump only for package @certd/plugin-cert
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
**Note:** Version bump only for package @certd/plugin-cert
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
### Performance Improvements

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.36.23",
"version": "1.37.2",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -13,13 +13,14 @@
"build3": "rollup -c",
"build2": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"pub": "npm publish"
"pub": "npm publish",
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/acme-client": "^1.36.23",
"@certd/basic": "^1.36.23",
"@certd/pipeline": "^1.36.23",
"@certd/plugin-lib": "^1.36.23",
"@certd/acme-client": "^1.37.2",
"@certd/basic": "^1.37.2",
"@certd/pipeline": "^1.37.2",
"@certd/plugin-lib": "^1.37.2",
"@google-cloud/publicca": "^1.3.0",
"dayjs": "^1.11.7",
"jszip": "^3.10.1",
@@ -31,7 +32,6 @@
"devDependencies": {
"@types/chai": "^4.3.3",
"@types/mocha": "^10.0.0",
"@types/psl": "^1.1.3",
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"chai": "^4.3.6",
@@ -43,5 +43,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "c98f43b98459d107910c95d23fda785b0edc4702"
"gitHead": "c725cee0445dbe1ebd1b6588373bde31697113da"
}

View File

@@ -2,3 +2,4 @@ export * from "./api.js";
export * from "./registry.js";
export * from "./decorator.js";
export * from "./base.js";
export * from "./domain-parser.js";

View File

@@ -117,11 +117,11 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
],
},
required: true,
helper: `1. <b>DNS直接验证</b>域名dns解析是在阿里云/腾讯云/华为云/CF/NameSilo/西数/火山/dns.la/京东云/51dns的选它
2. <b>CNAME代理验证</b>:支持任何注册商的域名,第一次需要手动添加[CNAME记录](#/certd/cname/record)建议将DNS服务器修改为阿里云/腾讯云的然后使用DNS直接验证
helper: `1. <b>DNS直接验证</b>域名dns解析已被本系统支持时即下方DNS解析服务商选项中可选推荐选择此方式
2. <b>CNAME代理验证</b>:支持任何注册商的域名,第一次需要手动添加[CNAME记录](#/certd/cname/record)如果经常申请失败,建议将DNS服务器修改为阿里云/腾讯云的然后使用DNS直接验证
3. <b>HTTP文件验证</b>:不支持泛域名,需要配置网站文件上传
4. <b>多DNS提供商</b>每个域名可以选择独立的DNS提供商
5. <b>自动匹配</b>:需要在[域名管理](#/certd/cert/domain)中事先配置好校验方式
5. <b>自动匹配</b>此处无需选择校验方式,需要在[域名管理](#/certd/cert/domain)中提前配置好校验方式
`,
})
challengeType!: string;
@@ -133,9 +133,9 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
name: "icon-select",
vModel: "value",
options: [
{ value: "letsencrypt", label: "Let's Encrypt", icon: "simple-icons:letsencrypt" },
{ value: "google", label: "Google", icon: "flat-color-icons:google" },
{ value: "zerossl", label: "ZeroSSL", icon: "emojione:digit-zero" },
{ value: "letsencrypt", label: "Let's Encrypt(免费,新手推荐)", icon: "simple-icons:letsencrypt" },
{ value: "google", label: "Google(免费)", icon: "flat-color-icons:google" },
{ value: "zerossl", label: "ZeroSSL(免费)", icon: "emojione:digit-zero" },
{ value: "sslcom", label: "SSL.com仅主域名和www免费", icon: "la:expeditedssl" },
],
},
@@ -635,8 +635,8 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
}
// 主域名异常
if (cnameRecord.mainDomain !== mainDomain) {
throw new Error(`CNAME记录${domain}的域名与配置的主域名不一致请确认是否在流水线创建之后修改了子域名托管您需要重新校验CNAME记录的校验状态`);
if (cnameRecord.mainDomain && mainDomain && cnameRecord.mainDomain !== mainDomain) {
throw new Error(`CNAME记录${domain}的域名与配置的主域名不一致${cnameRecord.mainDomain}${mainDomain}请确认是否在流水线创建之后修改了子域名托管您需要重新校验CNAME记录的校验状态`);
}
let dnsProvider = cnameRecord.commonDnsProvider;

View File

@@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
**Note:** Version bump only for package @certd/plugin-lib
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
### Performance Improvements
* dns解析支持阿里esa ([9291fa6](https://github.com/certd/certd/commit/9291fa68aa7a88a05c2f888bf3048df36a8fbde3))
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
**Note:** Version bump only for package @certd/plugin-lib
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
**Note:** Version bump only for package @certd/plugin-lib
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
**Note:** Version bump only for package @certd/plugin-lib
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
**Note:** Version bump only for package @certd/plugin-lib

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
"version": "1.36.23",
"version": "1.37.2",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -13,7 +13,8 @@
"build3": "rollup -c",
"build2": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"pub": "npm publish"
"pub": "npm publish",
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@alicloud/openapi-client": "^0.4.14",
@@ -21,8 +22,8 @@
"@alicloud/pop-core": "^1.7.10",
"@alicloud/tea-util": "^1.4.10",
"@aws-sdk/client-s3": "^3.787.0",
"@certd/basic": "^1.36.23",
"@certd/pipeline": "^1.36.23",
"@certd/basic": "^1.37.2",
"@certd/pipeline": "^1.37.2",
"@kubernetes/client-node": "0.21.0",
"ali-oss": "^6.22.0",
"basic-ftp": "^5.0.5",
@@ -41,7 +42,6 @@
"devDependencies": {
"@types/chai": "^4.3.3",
"@types/mocha": "^10.0.0",
"@types/psl": "^1.1.3",
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"chai": "^4.3.6",
@@ -53,5 +53,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "c98f43b98459d107910c95d23fda785b0edc4702"
"gitHead": "c725cee0445dbe1ebd1b6588373bde31697113da"
}

View File

@@ -0,0 +1,45 @@
import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline";
@IsAccess({
name: "aliesa",
title: "阿里云ESA授权",
desc: "",
icon: "ant-design:aliyun-outlined",
order: 0,
})
export class AliesaAccess extends BaseAccess {
@AccessInput({
title: "阿里云授权",
component: {
name: "access-selector",
vModel: "modelValue",
type: "aliyun",
},
helper: "请选择阿里云授权",
required: true,
})
accessId = "";
@AccessInput({
title: "地区",
component: {
name: "a-select",
vModel: "value",
options: [
{
label: "杭州",
value: "cn-hangzhou",
},
{
label: "新加坡",
value: "ap-southeast-1",
},
],
},
helper: "请选择ESA地区",
required: true,
})
region = "";
}
new AliesaAccess();

View File

@@ -1,2 +1,3 @@
export * from "./aliyun-access.js";
export * from "./alioss-access.js";
export * from "./aliesa-access.js";

View File

@@ -10,6 +10,7 @@ RUN cp /workspace/certd-client/dist/* /workspace/certd-server/public/ -rf
RUN cd /workspace/certd-server && pnpm install && npm run build-on-docker
FROM node:22-alpine
EXPOSE 7001
EXPOSE 7002

View File

@@ -0,0 +1,2 @@
#登录与权限开启
VITE_APP_PM_ENABLED=false

View File

@@ -3,6 +3,31 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
### Performance Improvements
* 证书监控支持设置证书即将过期天数 ([cd35568](https://github.com/certd/certd/commit/cd35568e042e6ab928685efad51cdbed823d2d4f))
* 支持网络测试 ([2bef608](https://github.com/certd/certd/commit/2bef608e07ceb56d52007f290667e0afef401b22))
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
### Bug Fixes
* 修复版本比较bug ([109696e](https://github.com/certd/certd/commit/109696e965d68c50c8627ffd40203edd1d2daea5))
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
**Note:** Version bump only for package @certd/ui-client
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
**Note:** Version bump only for package @certd/ui-client
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
**Note:** Version bump only for package @certd/ui-client
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
### Bug Fixes

View File

@@ -1,11 +1,12 @@
{
"name": "@certd/ui-client",
"version": "1.36.23",
"version": "1.37.2",
"private": true,
"scripts": {
"dev": "vite --open",
"dev:pm": "vite --mode pm",
"dev:force": "vite --force",
"remote": "vite --mode remote --open",
"debug": "vite --mode debug --open",
"debug:pm": "vite --mode debugpm",
"debug:force": "vite --force --mode debug",
@@ -105,8 +106,8 @@
"zod-defaults": "^0.1.3"
},
"devDependencies": {
"@certd/lib-iframe": "^1.36.23",
"@certd/pipeline": "^1.36.23",
"@certd/lib-iframe": "^1.37.2",
"@certd/pipeline": "^1.37.2",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12",

View File

@@ -175,6 +175,7 @@ export default {
suiteSetting: "Suite Settings",
orderManager: "Order Management",
userSuites: "User Suites",
netTest: "Network Test",
},
certificateRepo: {
title: "Certificate Repository",
@@ -280,6 +281,8 @@ export default {
cronTrigger: "Scheduled trigger for monitoring",
dnsServer: "DNS Server",
dnsServerHelper: "Use a custom domain name resolution server, such as: 1.1.1.1 , support multiple",
certValidDays: "Certificate Valid Days",
certValidDaysHelper: "Number of days before expiration to send a notification",
},
},
checkStatus: {
@@ -447,6 +450,7 @@ export default {
description: "Description",
createTime: "Creation Time",
updateTime: "Update Time",
mainDomain: "Main Domain",
edit: "Edit",
groupName: "Group Name",
enterGroupName: "Please enter group name",

View File

@@ -181,6 +181,7 @@ export default {
suiteSetting: "套餐设置",
orderManager: "订单管理",
userSuites: "用户套餐",
netTest: "网络测试",
},
certificateRepo: {
title: "证书仓库",
@@ -285,6 +286,8 @@ export default {
cronTrigger: "定时触发监控",
dnsServer: "DNS服务器",
dnsServerHelper: "使用自定义的域名解析服务器1.1.1.1 , 支持多个",
certValidDays: "证书到期前天数",
certValidDaysHelper: "证书到期前多少天发送通知",
},
},
checkStatus: {
@@ -453,6 +456,7 @@ export default {
description: "说明",
createTime: "创建时间",
updateTime: "更新时间",
mainDomain: "主域名",
edit: "编辑",
groupName: "分组名称",
enterGroupName: "请输入分组名称",

View File

@@ -14,7 +14,7 @@ import { usePreferences } from "/@/vben/preferences";
import { LocalStorage } from "/@/utils/util.storage";
import { FsEditorCode } from "@fast-crud/editor-code";
import "@fast-crud/editor-code/dist/style.css"
import "@fast-crud/editor-code/dist/style.css";
class ColumnSizeSaver {
save: (key: string, size: number) => void;

View File

@@ -249,6 +249,17 @@ export const sysResources = [
},
],
},
{
title: "certd.sysResources.netTest",
name: "NetTest",
path: "/sys/nettest",
component: "/sys/nettest/index.vue",
meta: {
icon: "ion:build-outline",
auth: true,
keepAlive: true,
},
},
],
},
];

View File

@@ -19,6 +19,10 @@ div#app {
height: 100%;
}
pre.pre{
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
h1,
h2,
h3,

View File

@@ -54,8 +54,8 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
);
},
{
inheritAttrs: false,
name: "VbenParentModal",
inheritAttrs: false,
}
);
return [Modal, extendedApi as ExtendedModalApi] as const;
@@ -104,8 +104,8 @@ export function useVbenModal<TParentModalProps extends ModalProps = ModalProps>(
);
},
{
inheritAttrs: false,
name: "VbenModal",
inheritAttrs: false,
}
);
injectData.extendApi?.(extendedApi);

View File

@@ -57,4 +57,3 @@ export async function DeleteBatch(ids: any[]) {
data: { ids },
});
}

View File

@@ -189,7 +189,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
},
column: {
width: 120,
align: "center",
align: "left",
cellRender({ value, row }) {
async function resetStatus() {
Modal.confirm({
@@ -202,7 +202,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
});
}
return (
<div class={"flex flex-center"}>
<div class={"flex flex-left"}>
<fs-values-format modelValue={value} dict={dictRef}></fs-values-format>
{row.error && (
<a-tooltip title={row.error}>
@@ -268,8 +268,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
},
},
},
mainDomain: {
title: t("certd.mainDomain"),
type: "text",
form: {
show: false,
},
},
createTime: {
title: t("certd.create_time"),
title: t("certd.createTime"),
type: "datetime",
form: {
show: false,
@@ -281,7 +288,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
},
},
updateTime: {
title: t("certd.update_time"),
title: t("certd.updateTime"),
type: "datetime",
form: {
show: false,

View File

@@ -2,6 +2,7 @@
import { useI18n } from "/src/locales";
import { AddReq, ColumnCompositionProps, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
import { siteInfoApi } from "./api";
import * as settingApi from "./setting/api";
import dayjs from "dayjs";
import { Modal, notification } from "ant-design-vue";
import { useSettingStore } from "/@/store/settings";
@@ -9,6 +10,7 @@ import { mySuiteApi } from "/@/views/certd/suite/mine/api";
import { mitter } from "/@/utils/util.mitt";
import { useSiteIpMonitor } from "./ip/use";
import { useSiteImport } from "/@/views/certd/monitor/site/use";
import { ref } from "vue";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const { t } = useI18n();
@@ -47,6 +49,14 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const { openSiteIpMonitorDialog } = useSiteIpMonitor();
const { openSiteImportDialog } = useSiteImport();
const certValidDaysRef = ref(10);
async function loadSetting() {
const setting = await settingApi.SiteMonitorSettingsGet();
certValidDaysRef.value = setting?.certValidDays || 10;
}
loadSetting();
function checkAll() {
Modal.confirm({
title: t("certd.monitor.confirmTitle"), // "确认"
@@ -385,10 +395,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
column: {
conditionalRender: false,
cellRender({ row }) {
const {
certEffectiveTime: effectiveTime,
certExpiresTime: expiresTime,
} = row || {};
const certValidDays = certValidDaysRef.value;
const { certEffectiveTime: effectiveTime, certExpiresTime: expiresTime } = row || {};
if (!expiresTime) {
return "-";
}
@@ -400,7 +408,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const effectiveDays = Math.max(90, dayjs(expiresTime).diff(applyDate, "day"));
// 距离失效时间剩余天数
const leftDays = dayjs(expiresTime).diff(dayjs(), "day");
const color = leftDays < 20 ? "red" : "#389e0d";
const color = leftDays < certValidDays ? "red" : "#389e0d";
const percent = (leftDays / effectiveDays) * 100;
// console.log('cellRender', 'effectiveDays', effectiveDays, 'expiresTime', expiresTime, 'applyTime', applyTime, 'percent', percent, row)
return <a-progress title={expireDate + t("certd.monitor.expired")} percent={percent} strokeColor={color} format={(percent: number) => `${leftDays}${t("certd.monitor.days")}`} />;

View File

@@ -6,6 +6,7 @@ export type UserSiteMonitorSetting = {
retryTimes?: number;
cron?: string;
dnsServer?: string[];
certValidDays?: number;
};
export async function SiteMonitorSettingsGet() {

View File

@@ -17,6 +17,12 @@
</div>
<div class="helper">{{ t("certd.monitor.setting.monitorRetryTimes") }}</div>
</a-form-item>
<a-form-item :label="t('certd.monitor.setting.certValidDays')" :name="['certValidDays']">
<div class="flex">
<a-input-number v-model:value="formState.certValidDays" />
</div>
<div class="helper">{{ t("certd.monitor.setting.certValidDaysHelper") }}</div>
</a-form-item>
<a-form-item :label="t('certd.monitor.setting.dnsServer')" :name="['dnsServer']">
<div class="flex">
<a-select v-model:value="formState.dnsServer" mode="tags" :open="false" />

View File

@@ -366,10 +366,7 @@ export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys
},
column: {
cellRender({ row }) {
const {
certEffectiveTime: effectiveTime,
certExpiresTime: expiresTime,
} = row?.lastVars || {};
const { certEffectiveTime: effectiveTime, certExpiresTime: expiresTime } = row?.lastVars || {};
if (!expiresTime) {
return "-";
}

View File

@@ -177,6 +177,8 @@ function isNewVersion(version: string, latestVersion: string) {
for (let i = 0; i < current.length; i++) {
if (parseInt(latest[i]) > parseInt(current[i])) {
return true;
} else if (parseInt(latest[i]) < parseInt(current[i])) {
return false;
}
}
return false;
@@ -191,7 +193,6 @@ async function loadLatestVersion() {
const minVersion = settingsStore.productInfo?.app?.minVersion;
if (minVersion) {
//
if (isNewVersion(version.value, minVersion)) {
notification.error({
message: settingsStore.productInfo?.app?.minVersionTip ?? "版本过低,为了您的数据安全,请尽快升级",

View File

@@ -0,0 +1,288 @@
<template>
<div class="domain-test-card">
<div class="card-header flex flex-wrap justify-start">
<div v-if="title">{{ title }}</div>
<a-form v-if="editing" layout="inline" :model="formData">
<a-form-item label="域名">
<a-input v-model:value="formData.domain" placeholder="请输入要测试的域名或IP" style="width: 240px" />
</a-form-item>
<a-form-item label="端口">
<a-input-number v-model:value="formData.port" placeholder="请输入端口" :min="1" :max="65535" style="width: 120px" />
</a-form-item>
</a-form>
<div v-else class="domain-info">
<span>域名: {{ formData.domain }}</span>
<span>端口: {{ formData.port }}</span>
</div>
<a-button :disabled="!formData.domain" size="small" type="primary" :loading="loading" @click="runAllTests"> 开始测试 </a-button>
</div>
<div class="card-content">
<div class="test-results">
<!-- 域名解析结果 -->
<test-case ref="domainResolveRef" title="域名解析" :test-method="() => createDomainResolveMethod()" :disabled="!getCurrentDomain()" />
<!-- Ping测试结果 -->
<test-case ref="pingTestRef" title="Ping测试" :test-method="() => createPingTestMethod()" :disabled="!getCurrentDomain()" />
<!-- Telnet测试结果 -->
<test-case ref="telnetTestRef" title="Telnet测试" :port="getCurrentPort()" :test-method="() => createTelnetTestMethod()" :disabled="!getCurrentDomain() || !getCurrentPort()" />
</div>
<div class="summary">
<a-alert :message="testSummary.title" :type="testSummary.status === 'success' ? 'success' : testSummary.status === 'failed' ? 'error' : 'warning'" show-icon :closable="false">
<template v-if="testSummary.text" #description>
<pre class="summary-text pre">{{ testSummary.text }}</pre>
</template>
</a-alert>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, computed, onMounted, watch } from "vue";
import { message } from "ant-design-vue";
import { DomainResolve, PingTest, TelnetTest } from "./api";
import TestCase from "./TestCase.vue";
// 组件属性
const props = defineProps<{
title?: string;
domain?: string;
port?: number;
autoStart?: boolean;
}>();
const editing = ref(!props.domain);
// 测试组件的引用
const domainResolveRef = ref();
const pingTestRef = ref();
const telnetTestRef = ref();
// 表单数据
const formData = reactive({
domain: props.domain || "",
port: props.port || 443,
});
// 加载状态
const loading = ref(false);
// 创建域名解析测试方法
const createDomainResolveMethod = async () => {
const domain = getCurrentDomain();
return DomainResolve(domain);
};
// 创建Ping测试方法
const createPingTestMethod = async () => {
const domain = getCurrentDomain();
return PingTest(domain);
};
// 创建Telnet测试方法
const createTelnetTestMethod = async () => {
const domain = getCurrentDomain();
const port = getCurrentPort();
return TelnetTest(domain, port);
};
// 获取当前使用的域名
const getCurrentDomain = () => {
return formData.domain;
};
// 获取当前使用的端口
const getCurrentPort = () => {
return formData.port;
};
// 获取各测试用例的状态
const getTestStatus = (testRef: any) => {
const result = testRef?.getResult();
if (!result) {
return null;
}
const isNetTestResult = typeof result === "object" && result !== null && "success" in result && "message" in result;
return {
success: isNetTestResult ? result.success : false,
message: isNetTestResult ? result.message : "测试失败",
};
};
// 生成测试总结
const testSummary = computed(() => {
if (loading.value) {
return { status: "waiting", title: "测试中请稍后..." };
}
// 通过computed获取各测试结果
const domainResolveResult = getTestStatus(domainResolveRef.value);
const pingTestResult = getTestStatus(pingTestRef.value);
const telnetTestResult = getTestStatus(telnetTestRef.value);
// 检查是否有测试结果
const testDone = domainResolveResult != null && pingTestResult != null && telnetTestResult != null;
if (!testDone) {
return { status: "waiting", title: '请点击"开始测试"按钮进行网络测试' };
}
// 详细分析不同的测试结果组合
// 1. 三个测试都失败
if (domainResolveResult?.success === false && pingTestResult?.success === false && telnetTestResult?.success === false) {
return {
status: "failed",
title: "所有测试均未通过",
text: `这表明应用容器内的网络可能完全不通。建议:\n1. 检查宿主机的网络连接状态\n2. 确认容器网络配置是否正确\n3. 检查防火墙设置是否阻止了网络访问`,
};
}
// 2. 域名解析成功但Ping不通
if (domainResolveResult?.success === true && pingTestResult?.success === false) {
return {
status: "partial",
title: "域名解析成功但Ping不通",
text: `可能原因:\n1. DNS被劫持解析到了错误的IP地址\n2. 目标服务器禁止了Ping请求\n3. 目标服务器IP被墙\n4. 目标服务器网络不通或已下线`,
};
}
// 3. 域名解析和Ping都成功但Telnet连接失败
if (domainResolveResult?.success === true && pingTestResult?.success === true && telnetTestResult?.success === false) {
return {
status: "partial",
title: "域名解析和Ping测试均通过但Telnet连接失败",
text: `可能原因:\n1. 端口号输入错误,请确认目标服务使用的正确端口\n2. 目标服务器上该端口未开放或服务未启动\n3. 防火墙或安全组限制了该端口的访问\n4. 目标网站被墙`,
};
}
// 4. 域名解析失败,但其他测试可能成功或未执行
if (domainResolveResult?.success === false) {
return {
status: "partial",
title: "域名解析失败",
text: `可能原因:\n1. 域名输入错误或不存在\n2. DNS服务器配置问题\n3. 本地网络DNS解析故障\n4. 域名已过期或被注销`,
};
}
// 5. 所有测试都成功
if (domainResolveResult?.success === true && pingTestResult?.success === true && telnetTestResult?.success === true) {
return {
status: "success",
title: "所有测试均通过",
text: `域名${formData.domain}解析正常能够正常Ping通且端口${formData.port}可访问。`,
};
}
// 6. 其他部分成功的情况
return {
status: "partial",
title: "部分测试未通过",
text: `请结合具体测试结果进行分析:\n- 域名解析:${domainResolveResult ? (domainResolveResult.success ? "成功" : "失败") : "未执行"}\n- Ping测试${pingTestResult ? (pingTestResult.success ? "成功" : "失败") : "未执行"}\n- Telnet测试${telnetTestResult ? (telnetTestResult.success ? "成功" : "失败") : "未执行"}`,
};
});
// 运行全部测试
async function runAllTests() {
const domain = getCurrentDomain();
// 检查是否有域名
if (!domain) {
message.error("请输入域名");
return;
}
loading.value = true;
// 通过组件引用调用测试方法
try {
await Promise.allSettled([domainResolveRef.value?.test(), pingTestRef.value?.test(), telnetTestRef.value?.test()]);
} catch (error) {
message.error("部分测试执行失败请查看详细结果");
} finally {
loading.value = false;
}
}
onMounted(() => {
if (props.autoStart) {
runAllTests();
}
});
</script>
<style lang="less">
.domain-test-card {
border: 1px solid #e8e8e8;
border-radius: 4px;
overflow: hidden;
background-color: #fff;
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background-color: #fafafa;
border-bottom: 1px solid #e8e8e8;
}
.card-header h3 {
margin: 0;
font-size: 16px;
font-weight: 500;
}
.card-content {
padding: 16px;
}
.input-form {
margin-bottom: 12px;
padding: 12px;
background-color: #fafafa;
border-radius: 4px;
}
.domain-info {
padding: 5.5px 12px;
background-color: #f0f0f0;
border-radius: 4px;
display: flex;
gap: 16px;
font-size: 14px;
color: #666;
}
.test-buttons {
display: flex;
gap: 8px;
margin-bottom: 16px;
}
.test-results {
margin-top: 0px;
}
.summary {
margin-top: 16px;
padding: 12px;
background-color: #f8f9fa;
border-radius: 4px;
.summary-text {
}
}
/* 调整按钮大小 */
.ant-btn {
font-size: 12px;
padding: 2px 8px;
height: 24px;
}
}
</style>

View File

@@ -0,0 +1,140 @@
<template>
<a-card title="服务端信息" class="server-info-card">
<template #extra>
<a-button size="small" :loading="loading" @click="refreshServerInfo">
<template #icon>
<a-icon type="sync" :spin="loading" />
</template>
刷新
</a-button>
</template>
<div v-if="loading" class="loading">
<a-spin size="small" />
<span style="margin-left: 8px">加载中...</span>
</div>
<div v-else-if="error" class="error">
<a-alert message="获取服务器信息失败" :description="error" type="error" show-icon />
</div>
<div v-else class="server-info-grid">
<!-- 本地IP -->
<div class="info-item">
<div class="info-label">本地IP:</div>
<div v-if="serverInfo.localIP && serverInfo.localIP.length > 0" class="info-value">
<a-tag v-for="ip in serverInfo.localIP" :key="ip" type="info" color="blue">{{ ip }}</a-tag>
</div>
<div v-else class="info-empty">暂无信息</div>
</div>
<!-- 外网IP -->
<div class="info-item">
<div class="info-label">外网IP:</div>
<div v-if="serverInfo.publicIP && serverInfo.publicIP.length > 0" class="info-value">
<a-tag v-for="ip in serverInfo.publicIP" :key="ip" type="info" color="green">{{ ip }}</a-tag>
</div>
<div v-else class="info-empty">暂无信息</div>
</div>
<!-- DNS服务器 -->
<div class="info-item">
<div class="info-label">DNS服务器:</div>
<div v-if="serverInfo.dnsServers && serverInfo.dnsServers.length > 0" class="info-value">
<a-tag v-for="dns in serverInfo.dnsServers" :key="dns" type="info" color="cyan">{{ dns }}</a-tag>
</div>
<div v-else class="info-empty">暂无信息</div>
</div>
</div>
</a-card>
</template>
<script lang="ts" setup>
import { ref, onMounted } from "vue";
import { message } from "ant-design-vue";
import { GetServerInfo } from "./api";
// 服务器信息类型
interface ServerInfo {
localIP?: string[];
publicIP?: string[];
dnsServers?: string[];
}
const loading = ref(false);
const error = ref<string | null>(null);
const serverInfo = ref<ServerInfo>({});
// 加载服务器信息
const loadServerInfo = async () => {
loading.value = true;
error.value = null;
try {
serverInfo.value = await GetServerInfo();
} catch (e) {
error.value = e instanceof Error ? e.message : String(e);
message.error("获取服务器信息失败");
} finally {
loading.value = false;
}
};
// 刷新服务器信息
const refreshServerInfo = () => {
loadServerInfo();
};
// 组件挂载时加载数据
onMounted(() => {
loadServerInfo();
});
</script>
<style lang="less">
.server-info-card {
margin-bottom: 16px;
.loading {
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
color: #666;
}
.error {
margin: 0;
}
.server-info-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 16px;
}
.info-item {
background-color: #fafafa;
border-radius: 4px;
padding: 12px;
.info-label {
font-size: 14px;
font-weight: 500;
color: #666;
margin-bottom: 8px;
}
.info-value {
font-size: 14px;
color: #333;
.ant-list-item {
padding: 4px 0;
}
}
.info-empty {
font-size: 14px;
color: #999;
font-style: italic;
}
}
}
</style>

View File

@@ -0,0 +1,186 @@
<template>
<div class="test-case" :class="{ loading }">
<div class="case-header">
<span class="flex items-center">
<fs-button size="small" type="text" icon="ion:play-circle" :loading="loading" :disabled="disabled" class="test-button" @click="runTest" />
<a-tag color="blue" class="case-title">
{{ title }}
</a-tag>
<span v-if="port" class="port-info">{{ port }}</span>
</span>
<span v-if="result && isNetTestResult" class="result-status flex-1" :style="{ color: isSuccess ? 'green' : 'red' }">
<span>
{{ isSuccess ? "✓" : "✗" }}
</span>
<span class="ml-2">
{{ result.message }}
</span>
</span>
</div>
<div v-if="result" class="result-content">
<div v-if="error" class="error-message">
<span style="color: red">{{ error }}</span>
</div>
<div v-else-if="isNetTestResult">
<div v-if="resultTestLog" class="test-log">
<pre>{{ resultTestLog }}</pre>
</div>
</div>
<div v-else-if="typeof result === 'object'" class="object-result">
<pre>{{ JSON.stringify(result, null, 2) }}</pre>
</div>
<div v-else class="text-result">
<pre>{{ result }}</pre>
</div>
</div>
<div v-else class="no-result">
<p>暂无结果</p>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed } from "vue";
import { message } from "ant-design-vue";
// 组件属性
const props = defineProps<{
title: string;
port?: number | string;
testMethod: () => Promise<any>;
disabled?: boolean;
}>();
// 内部状态
const loading = ref(false);
const result = ref<any>(null);
const error = ref<string | null>(null);
// 运行测试
const runTest = async () => {
loading.value = true;
error.value = null;
result.value = null;
try {
const testResult = await props.testMethod();
// 如果结果有 data 属性,则使用 data否则使用整个结果
result.value = testResult.data || testResult;
} catch (err: any) {
result.value = null;
error.value = err.message || "测试失败";
message.error(`${props.title} 测试失败: ${error.value}`);
} finally {
loading.value = false;
}
};
// 暴露方法给父组件
defineExpose({
test: runTest,
getResult: () => result.value,
});
// 辅助计算属性,用于模板中显示结果
const isNetTestResult = computed(() => {
return typeof result.value === "object" && result.value !== null && "success" in result.value && "message" in result.value && "testLog" in result.value;
});
const isSuccess = computed(() => {
return isNetTestResult.value && result.value.success;
});
const resultMessage = computed(() => {
return isNetTestResult.value ? result.value.message : "";
});
const resultTestLog = computed(() => {
return isNetTestResult.value ? result.value.testLog : "";
});
const resultError = computed(() => {
return isNetTestResult.value ? result.value.error : "";
});
</script>
<style lang="less" scoped>
.test-case {
padding: 12px 16px;
border-bottom: 1px solid #f0f0f0;
position: relative;
&:last-child {
border-bottom: none;
}
&.loading {
opacity: 0.7;
}
}
.case-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
.result-status {
font-size: 14px;
color: #999;
margin-right: 10px;
}
}
.case-title {
font-weight: 500;
font-size: 14px;
}
.port-info {
font-size: 12px;
color: #999;
background-color: #f0f0f0;
padding: 2px 6px;
border-radius: 3px;
margin-right: 8px;
}
.test-button {
color: #1890ff;
font-size: 12px;
margin-right: 5px;
}
.result-content {
.error-message,
.object-result,
.text-result {
background-color: #f8f8f8;
padding: 8px 10px;
border-radius: 3px;
overflow-x: auto;
}
pre {
margin: 0;
font-size: 12px;
line-height: 1.4;
font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
white-space: pre-wrap;
word-wrap: break-word;
}
.test-log {
background-color: #f8f8f8;
padding: 8px 10px;
border-radius: 3px;
overflow-x: auto;
}
}
.no-result {
padding: 12px 0;
text-align: center;
color: #999;
font-size: 12px;
}
</style>

View File

@@ -0,0 +1,33 @@
import { request } from "/@/api/service";
export async function DomainResolve(domain: string) {
return await request({
url: "/sys/nettest/domainResolve",
method: "post",
data: { domain },
});
}
export async function PingTest(domain: string) {
return await request({
url: "/sys/nettest/ping",
method: "post",
data: { domain },
});
}
export async function TelnetTest(domain: string, port: number) {
return await request({
url: "/sys/nettest/telnet",
method: "post",
data: { domain, port },
});
}
// 获取服务器信息包括本地IP、外网IP和DNS服务器
export async function GetServerInfo() {
return await request({
url: "/sys/nettest/serverInfo",
method: "post",
});
}

View File

@@ -0,0 +1,46 @@
<template>
<fs-page class="page-sys-nettest">
<template #header>
<div class="title">
网络测试
<span class="sub">测试您的服务器容器网络连接是否正常</span>
</div>
</template>
<div class="nettest-container">
<!-- 服务端信息 -->
<server-info-card />
<!-- 测试区域 -->
<div class="test-areas flex-wrap md:flex-nowrap">
<!-- 百度域名测试 (用于对比) -->
<domain-test-card class="test-card" :domain="'baidu.com'" :port="443" :auto-start="true" />
<!-- 用户输入域名测试 -->
<domain-test-card class="test-card" :title="'自定义域名测试'" />
</div>
</div>
</fs-page>
</template>
<script lang="ts" setup>
import DomainTestCard from "./DomainTestCard.vue";
import ServerInfoCard from "./ServerInfoCard.vue";
</script>
<style lang="less">
.page-sys-nettest {
.nettest-container {
padding: 16px;
background-color: #fff;
}
.test-areas {
display: flex;
gap: 16px;
margin-top: 16px;
}
.test-card {
min-width: 50%;
}
}
</style>

View File

@@ -55,15 +55,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
groups: {
base: {
header: t("certd.basicInfo"),
columns: ["title", "type", "disabled", "order", "supportBuy", "intro"]
columns: ["title", "type", "disabled", "order", "supportBuy", "intro"],
},
content: {
header: t("certd.packageContent"),
columns: ["content.maxDomainCount", "content.maxPipelineCount", "content.maxDeployCount", "content.maxMonitorCount"]
columns: ["content.maxDomainCount", "content.maxPipelineCount", "content.maxDeployCount", "content.maxMonitorCount"],
},
price: {
header: t("certd.price"),
columns: ["durationPrices"]
columns: ["durationPrices"],
},
},
},

View File

@@ -84,6 +84,7 @@ export default ({ command, mode }) => {
host: "0.0.0.0",
port: 3008,
fs: devServerFs,
allowedHosts: ["localhost", "127.0.0.1", "yfy.docmirror.cn"],
proxy: {
// with options
"/api": {

View File

@@ -3,6 +3,53 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.37.2](https://github.com/certd/certd/compare/v1.37.1...v1.37.2) (2025-10-14)
### Bug Fixes
* 修复飞牛证书部署后无法生效的bug ([bf156a1](https://github.com/certd/certd/commit/bf156a13bd443cdadb73c9dff79bbef7231b4401))
* aliyunoss 选择证书接入点选择新加坡无法上传的bug ([e00733a](https://github.com/certd/certd/commit/e00733a34644c23ffe926486b15dc96bf2fa4b57))
### Performance Improvements
* 增加飞牛证书id选择的提示 ([5a4d812](https://github.com/certd/certd/commit/5a4d8121462b1afe921d028465687be8c9679814))
* 证书监控支持设置证书即将过期天数 ([cd35568](https://github.com/certd/certd/commit/cd35568e042e6ab928685efad51cdbed823d2d4f))
* 支持网络测试 ([2bef608](https://github.com/certd/certd/commit/2bef608e07ceb56d52007f290667e0afef401b22))
* 支持新网代理方式 ([f612509](https://github.com/certd/certd/commit/f612509cac87b859e81a7a52fe94b2eaccad22f9))
* dns支持新网互联 ([f415190](https://github.com/certd/certd/commit/f41519048326d971acd9e0a30462231f77a299a6))
## [1.37.1](https://github.com/certd/certd/compare/v1.37.0...v1.37.1) (2025-09-29)
### Bug Fixes
* 修复某些情况下cname申请证书报错主域名不一致的bug ([2671781](https://github.com/certd/certd/commit/2671781e1bb0838981728d85eacf0e1a25a0fa48))
### Performance Improvements
* dns解析支持阿里esa ([9291fa6](https://github.com/certd/certd/commit/9291fa68aa7a88a05c2f888bf3048df36a8fbde3))
# [1.37.0](https://github.com/certd/certd/compare/v1.36.25...v1.37.0) (2025-09-28)
### Features
* dist打包前检查 ([8f6e5bd](https://github.com/certd/certd/commit/8f6e5bd24b3b65fbfcba36c08f532a3abad2d606))
## [1.36.25](https://github.com/certd/certd/compare/v1.36.24...v1.36.25) (2025-09-27)
### Bug Fixes
* 固定midwayjs版本修复ui-server import 错误的bug ([eb4d125](https://github.com/certd/certd/commit/eb4d125eaf4a41e88c752d0c68993829589f8f27))
## [1.36.24](https://github.com/certd/certd/compare/v1.36.23...v1.36.24) (2025-09-27)
### Bug Fixes
* 修复 ui-server 加载失败问题 ([c2ccdbe](https://github.com/certd/certd/commit/c2ccdbec9dd08bca4688eeb2f34d0105eec43ba1))
### Performance Improvements
* 重置管理员密码同时会关闭验证码,防止验证码失效之后无法登录 ([03899d4](https://github.com/certd/certd/commit/03899d4d9c76fc2077dacc53ab88e2c9ca41af7c))
## [1.36.23](https://github.com/certd/certd/compare/v1.36.22...v1.36.23) (2025-09-26)
### Performance Improvements

View File

@@ -5,9 +5,8 @@ const { Bootstrap } = require('@midwayjs/bootstrap');
const DirectoryFileDetector = require('@midwayjs/core').DirectoryFileDetector;
const baseDir = process.cwd();
const pipelineDir = baseDir + './node_modules/@certd/pipeline/dist';
const customFileDetector = new DirectoryFileDetector({
loadDir: [baseDir, pipelineDir],
loadDir: [baseDir],
});
module.exports = async () => {

View File

@@ -1,12 +1,14 @@
{
"name": "@certd/ui-server",
"version": "1.36.23",
"version": "1.37.2",
"description": "fast-server base midway",
"private": true,
"type": "module",
"scripts": {
"start": "cross-env NODE_ENV=production node ./bootstrap.js",
"dev": "cross-env NODE_ENV=local mwtsc --watch --run @midwayjs/mock/app",
"dev-start": "mwtsc --watch --run @midwayjs/mock/app",
"dc": "cd ../../../ && pnpm run dev",
"dev": "cross-env NODE_ENV=local & pnpm run dev-start",
"dev-commlocal": "cross-env NODE_ENV=dev-commlocal mwtsc --watch --run @midwayjs/mock/app",
"dev-commpro": "cross-env NODE_ENV=dev-commpro mwtsc --watch --run @midwayjs/mock/app",
"dev-pg": "cross-env NODE_ENV=dev-pg mwtsc --watch --run @midwayjs/mock/app",
@@ -43,34 +45,34 @@
"@aws-sdk/client-cloudfront": "^3.699.0",
"@aws-sdk/client-iam": "^3.699.0",
"@aws-sdk/client-s3": "^3.705.0",
"@certd/acme-client": "^1.36.23",
"@certd/basic": "^1.36.23",
"@certd/commercial-core": "^1.36.23",
"@certd/acme-client": "^1.37.2",
"@certd/basic": "^1.37.2",
"@certd/commercial-core": "^1.37.2",
"@certd/cv4pve-api-javascript": "^8.4.2",
"@certd/jdcloud": "^1.36.23",
"@certd/lib-huawei": "^1.36.23",
"@certd/lib-k8s": "^1.36.23",
"@certd/lib-server": "^1.36.23",
"@certd/midway-flyway-js": "^1.36.23",
"@certd/pipeline": "^1.36.23",
"@certd/plugin-cert": "^1.36.23",
"@certd/plugin-lib": "^1.36.23",
"@certd/plugin-plus": "^1.36.23",
"@certd/plus-core": "^1.36.23",
"@certd/jdcloud": "^1.37.2",
"@certd/lib-huawei": "^1.37.2",
"@certd/lib-k8s": "^1.37.2",
"@certd/lib-server": "^1.37.2",
"@certd/midway-flyway-js": "^1.37.2",
"@certd/pipeline": "^1.37.2",
"@certd/plugin-cert": "^1.37.2",
"@certd/plugin-lib": "^1.37.2",
"@certd/plugin-plus": "^1.37.2",
"@certd/plus-core": "^1.37.2",
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",
"@koa/cors": "^5.0.0",
"@midwayjs/bootstrap": "~3.20.3",
"@midwayjs/cache": "~3.14.0",
"@midwayjs/core": "~3.20.3",
"@midwayjs/i18n": "~3.20.3",
"@midwayjs/info": "~3.20.3",
"@midwayjs/koa": "~3.20.3",
"@midwayjs/logger": "~3.4.2",
"@midwayjs/static-file": "~3.20.3",
"@midwayjs/typeorm": "~3.20.3",
"@midwayjs/upload": "~3.20.3",
"@midwayjs/validate": "~3.20.3",
"@midwayjs/bootstrap": "3.20.11",
"@midwayjs/cache": "3.14.0",
"@midwayjs/core": "3.20.11",
"@midwayjs/i18n": "3.20.13",
"@midwayjs/info": "3.20.13",
"@midwayjs/koa": "3.20.13",
"@midwayjs/logger": "3.4.2",
"@midwayjs/static-file": "3.20.13",
"@midwayjs/typeorm": "3.20.11",
"@midwayjs/upload": "3.20.13",
"@midwayjs/validate": "3.20.13",
"@volcengine/openapi": "^1.28.1",
"ali-oss": "^6.21.0",
"axios": "^1.7.2",
@@ -93,7 +95,6 @@
"jsonwebtoken": "^9.0.0",
"jszip": "^3.10.1",
"koa-send": "^5.0.1",
"kubernetes-client": "^9.0.0",
"lodash-es": "^4.17.21",
"log4js": "^6.7.1",
"lru-cache": "^11.0.1",
@@ -120,10 +121,11 @@
"svg-captcha": "^1.4.0",
"tencentcloud-sdk-nodejs": "^4.1.112",
"typeorm": "^0.3.20",
"uuid": "^10.0.0"
"uuid": "^10.0.0",
"xml2js": "^0.6.2"
},
"devDependencies": {
"@midwayjs/mock": "~3.20.3",
"@midwayjs/mock": "3.20.11",
"@types/ali-oss": "^6.16.11",
"@types/cache-manager": "^4.0.6",
"@types/jest": "^29.5.13",
@@ -142,7 +144,7 @@
"why-is-node-running": "^3.2.2"
},
"engines": {
"node": ">=18.0.0"
"node": ">=20.0.0"
},
"repository": {
"type": "git",

View File

@@ -27,7 +27,7 @@ const development = {
},
keys: 'certd',
koa: {
hostname:"::",
hostname: "::",
port: 7001,
},
https: {

View File

@@ -20,9 +20,13 @@ import * as commercial from '@certd/commercial-core';
import * as upload from '@midwayjs/upload';
import { setLogger } from '@certd/acme-client';
import {HiddenMiddleware} from "./middleware/hidden.js";
process.on('uncaughtException', error => {
console.error('未捕获的异常:', error);
// 在这里可以添加日志记录、发送错误通知等操作
if(error?.message?.includes('address family not supported')){
logger.error("您的服务器不支持监听IPV6格式的地址::),请配置环境变量: certd_koa_hostname=0.0.0.0");
}
});
@Configuration({
@@ -107,5 +111,6 @@ export class MainConfiguration {
});
logger.info('当前环境:', this.app.getEnv()); // prod
// throw new Error("address family not supported")
}
}

View File

@@ -0,0 +1,47 @@
import { BaseController } from '@certd/lib-server';
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
import { NetTestService } from '../../../modules/sys/nettest/nettest-service.js';
@Provide()
@Controller('/api/sys/nettest/')
export class SysNetTestController extends BaseController {
@Inject()
netTestService: NetTestService;
@Post('/domainResolve', { summary: 'sys:settings:view' })
public async domainResolve(@Body(ALL) body: { domain: string }) {
const { domain } = body;
const result = await this.netTestService.domainResolve(domain);
return this.ok(result);
}
// ping
@Post('/ping', { summary: 'sys:settings:view' })
public async ping(@Body(ALL) body: { domain: string }) {
const { domain } = body;
const result = await this.netTestService.ping(domain);
return this.ok(result);
}
// telnet
@Post('/telnet', { summary: 'sys:settings:view' })
public async telnet(@Body(ALL) body: { domain: string, port: number }) {
const { domain, port } = body;
const result = await this.netTestService.telnet(domain, port);
return this.ok(result);
}
// telnet
@Post('/serverInfo', { summary: 'sys:settings:view' })
public async serverInfo() {
const result = await this.netTestService.serverInfo();
return this.ok(result);
}
}

View File

@@ -1,7 +1,7 @@
import {ALL, Body, Controller, Inject, Post, Provide, Query} from '@midwayjs/core';
import {Constants, CrudController} from '@certd/lib-server';
import {SubDomainService} from "../../../modules/pipeline/service/sub-domain-service.js";
import {DomainParser} from '@certd/plugin-cert/dist/dns-provider/domain-parser.js';
import {DomainParser} from '@certd/plugin-cert';
import { SubDomainsGetter } from '../../../modules/pipeline/service/getter/sub-domain-getter.js';
/**

View File

@@ -1,6 +1,6 @@
import { Autoload, Config, Init, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
import { IMidwayKoaContext, IWebMiddleware, NextFunction } from '@midwayjs/koa';
import { CommonException } from '@certd/lib-server';
import { CommonException, SysSettingsService } from "@certd/lib-server";
import { UserService } from '../../modules/sys/authority/service/user-service.js';
import { logger } from '@certd/basic';
import {UserSettingsService} from "../../modules/mine/service/user-settings-service.js";
@@ -17,6 +17,8 @@ export class ResetPasswdMiddleware implements IWebMiddleware {
@Inject()
userSettingsService: UserSettingsService;
@Inject()
sysSettingsService: SysSettingsService;
@Config('system.resetAdminPasswd')
private resetAdminPasswd: boolean;
@@ -40,8 +42,12 @@ export class ResetPasswdMiddleware implements IWebMiddleware {
userId: 1,
key:"user.two.factor"
})
const publicSettings = await this.sysSettingsService.getPublicSettings()
publicSettings.captchaEnabled = false
await this.sysSettingsService.savePublicSettings(publicSettings);
const user = await this.userService.info(1);
logger.info(`重置1号管理员用户的密码完成2FA设置已删除用户名${user.username},新密码:${newPasswd},请在登录进去之后尽快修改密码`);
logger.info(`重置1号管理员用户的密码完成2FA设置已删除验证码登录已禁用,用户名:${user.username},新密码:${newPasswd},请在登录进去之后尽快修改密码`);
}
}
}

View File

@@ -2,7 +2,7 @@ import { Autoload, Init, Inject, Scope, ScopeEnum } from "@midwayjs/core";
import { CertInfoService } from "../monitor/index.js";
import { pipelineEmitter } from "@certd/pipeline";
import { CertInfo, EVENT_CERT_APPLY_SUCCESS } from "@certd/plugin-cert";
import { PipelineEvent } from "@certd/pipeline/dist/service/emit.js";
import { PipelineEvent } from "@certd/pipeline";
@Autoload()
@Scope(ScopeEnum.Request, { allowDowngrade: true })

View File

@@ -19,6 +19,8 @@ export class AutoZPrint {
@Config('https')
httpsConfig: HttpsServerOptions;
@Config('koa')
koaConfig: any;
@Init()
async init() {
@@ -58,6 +60,7 @@ export class AutoZPrint {
httpsServer.start({
...this.httpsConfig,
app: this.app,
hostname: this.httpsConfig.hostname || this.koaConfig.hostname,
});
}
}

View File

@@ -7,6 +7,7 @@ import {logger, safePromise} from '@certd/basic';
export type HttpsServerOptions = {
enabled: boolean;
app?: Application;
hostname?: string;
port: number;
key: string;
cert: string;
@@ -58,7 +59,7 @@ export class HttpsServer {
opts.app.callback()
);
this.server = httpServer;
const hostname = '::';
let hostname = opts.hostname || '::';
// A function that runs in the context of the http server
// and reports what type of server listens on which port
function listeningReporter() {
@@ -70,7 +71,19 @@ export class HttpsServer {
httpServer.listen(opts.port, hostname, listeningReporter);
return httpServer;
} catch (e) {
logger.error('启动https服务失败', e);
if ( e.message?.includes("address family not supported")) {
hostname = "0.0.0.0"
logger.error(`${e.message},尝试监听${hostname}`, e);
try{
httpServer.listen(opts.port, hostname, listeningReporter);
return httpServer;
}catch (e) {
logger.error('启动https服务失败', e);
}
}else{
logger.error('启动https服务失败', e);
}
}
}
}

View File

@@ -1,7 +1,8 @@
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { AddonService, SysSettingsService } from "@certd/lib-server";
import { SysSettingsService } from "@certd/lib-server";
import { logger } from "@certd/basic";
import { ICaptchaAddon } from "../../../plugins/plugin-captcha/api.js";
import { AddonGetterService } from "../../pipeline/service/addon-getter-service.js";
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
@@ -9,45 +10,48 @@ export class CaptchaService {
@Inject()
sysSettingsService: SysSettingsService;
@Inject()
addonService: AddonService;
addonGetterService: AddonGetterService;
async getCaptcha(captchaAddonId?:number){
async getCaptcha(captchaAddonId?: number) {
if (!captchaAddonId) {
const settings = await this.sysSettingsService.getPublicSettings()
captchaAddonId = settings.captchaAddonId ?? 0
const settings = await this.sysSettingsService.getPublicSettings();
captchaAddonId = settings.captchaAddonId ?? 0;
}
const addon:ICaptchaAddon = await this.addonService.getAddonById(captchaAddonId,true,0)
const addon: ICaptchaAddon = await this.addonGetterService.getAddonById(captchaAddonId, true, 0, {
type: "captcha",
name: "image"
});
if (!addon) {
throw new Error('验证码插件还未配置')
throw new Error("验证码插件还未配置");
}
return await addon.getCaptcha()
return await addon.getCaptcha();
}
async doValidate(opts:{form:any,must?:boolean,captchaAddonId?:number}){
async doValidate(opts: { form: any, must?: boolean, captchaAddonId?: number }) {
if (!opts.captchaAddonId) {
const settings = await this.sysSettingsService.getPublicSettings()
opts.captchaAddonId = settings.captchaAddonId ?? 0
const settings = await this.sysSettingsService.getPublicSettings();
opts.captchaAddonId = settings.captchaAddonId ?? 0;
}
const addon = await this.addonService.getById(opts.captchaAddonId,0)
const addon = await this.addonGetterService.getById(opts.captchaAddonId, 0);
if (!addon) {
if (opts.must) {
throw new Error('请先配置验证码插件');
throw new Error("请先配置验证码插件");
}
logger.warn('验证码插件还未配置,忽略验证码校验')
return true
logger.warn("验证码插件还未配置,忽略验证码校验");
return true;
}
if (!opts.form) {
throw new Error('请输入验证码');
throw new Error("请输入验证码");
}
const res = await addon.onValidate(opts.form)
const res = await addon.onValidate(opts.form);
if (!res) {
throw new Error('验证码错误');
throw new Error("验证码错误");
}
return true
return true;
}

View File

@@ -3,7 +3,7 @@ import { cache, isDev, randomNumber } from '@certd/basic';
import { SysSettingsService, SysSiteInfo } from '@certd/lib-server';
import { SmsServiceFactory } from '../sms/factory.js';
import { ISmsService } from '../sms/api.js';
import { CodeErrorException } from '@certd/lib-server/dist/basic/exception/code-error-exception.js';
import { CodeErrorException } from '@certd/lib-server';
import { EmailService } from './email-service.js';
import { AccessService } from '@certd/lib-server';
import { AccessSysGetter } from '@certd/lib-server';

View File

@@ -4,7 +4,7 @@ import {In, Not, Repository} from 'typeorm';
import {AccessService, BaseService} from '@certd/lib-server';
import {DomainEntity} from '../entity/domain.js';
import {SubDomainService} from "../../pipeline/service/sub-domain-service.js";
import {DomainParser} from "@certd/plugin-cert/dist/dns-provider/domain-parser.js";
import {DomainParser} from "@certd/plugin-cert";
import {DomainVerifiers} from "@certd/plugin-cert";
import { SubDomainsGetter } from '../../pipeline/service/getter/sub-domain-getter.js';
import { CnameRecordService } from '../../cname/service/cname-record-service.js';

View File

@@ -17,7 +17,7 @@ import { getAuthoritativeDnsResolver, walkTxtRecord } from "@certd/acme-client";
import { CnameProviderService } from "./cname-provider-service.js";
import { CnameProviderEntity } from "../entity/cname-provider.js";
import { CommonDnsProvider } from "./common-provider.js";
import { DomainParser } from "@certd/plugin-cert/dist/dns-provider/domain-parser.js";
import { DomainParser } from "@certd/plugin-cert";
import punycode from "punycode.js";
import { SubDomainService } from "../../pipeline/service/sub-domain-service.js";
import { SubDomainsGetter } from "../../pipeline/service/getter/sub-domain-getter.js";
@@ -37,7 +37,7 @@ type CnameCheckCacheValue = {
* 授权
*/
@Provide()
@Scope(ScopeEnum.Request, {allowDowngrade: true})
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class CnameRecordService extends BaseService<CnameRecordEntity> {
@InjectEntityModel(CnameRecordEntity)
repository: Repository<CnameRecordEntity>;
@@ -71,16 +71,16 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
*/
async add(param: any): Promise<CnameRecordEntity> {
if (!param.domain) {
throw new ValidateException('域名不能为空');
throw new ValidateException("域名不能为空");
}
if (!param.userId) {
throw new ValidateException('userId不能为空');
throw new ValidateException("userId不能为空");
}
if (param.domain.startsWith('*.')) {
if (param.domain.startsWith("*.")) {
param.domain = param.domain.substring(2);
}
param.domain = param.domain.trim()
const info = await this.getRepository().findOne({where: {domain: param.domain, userId: param.userId}});
param.domain = param.domain.trim();
const info = await this.getRepository().findOne({ where: { domain: param.domain, userId: param.userId } });
if (info) {
return info;
}
@@ -90,28 +90,28 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
//获取默认的cnameProviderId
cnameProvider = await this.cnameProviderService.getByPriority();
if (cnameProvider == null) {
throw new ValidateException('找不到CNAME服务请先前往“系统管理->CNAME服务设置”添加CNAME服务');
throw new ValidateException("找不到CNAME服务请先前往“系统管理->CNAME服务设置”添加CNAME服务");
}
} else {
cnameProvider = await this.cnameProviderService.info(param.cnameProviderId);
}
await this.cnameProviderChanged(param.userId, param, cnameProvider);
param.status = 'cname';
const {id} = await super.add(param);
param.status = "cname";
const { id } = await super.add(param);
return await this.info(id);
}
private async cnameProviderChanged(userId: number, param: any, cnameProvider: CnameProviderEntity) {
param.cnameProviderId = cnameProvider.id;
const subDomainGetter = new SubDomainsGetter(userId, this.subDomainService)
const subDomainGetter = new SubDomainsGetter(userId, this.subDomainService);
const domainParser = new DomainParser(subDomainGetter);
const realDomain = await domainParser.parse(param.domain);
const prefix = param.domain.replace(realDomain, '');
const prefix = param.domain.replace(realDomain, "");
let hostRecord = `_acme-challenge.${prefix}`;
if (hostRecord.endsWith('.')) {
if (hostRecord.endsWith(".")) {
hostRecord = hostRecord.substring(0, hostRecord.length - 1);
}
param.hostRecord = hostRecord;
@@ -119,33 +119,33 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
const randomKey = utils.id.simpleNanoId(6).toLowerCase();
const userIdHex = utils.hash.toHex(userId)
let userKeyHash = ""
const installInfo = await this.sysSettingsService.getSetting<SysInstallInfo>(SysInstallInfo)
userKeyHash = `${installInfo.siteId}_${userIdHex}_${randomKey}`
userKeyHash = utils.hash.md5(userKeyHash).substring(0, 10)
logger.info(`userKeyHash:${userKeyHash},subjectId:${installInfo.siteId},randomKey:${randomKey},userIdHex:${userIdHex}`)
const userIdHex = utils.hash.toHex(userId);
let userKeyHash = "";
const installInfo = await this.sysSettingsService.getSetting<SysInstallInfo>(SysInstallInfo);
userKeyHash = `${installInfo.siteId}_${userIdHex}_${randomKey}`;
userKeyHash = utils.hash.md5(userKeyHash).substring(0, 10);
logger.info(`userKeyHash:${userKeyHash},subjectId:${installInfo.siteId},randomKey:${randomKey},userIdHex:${userIdHex}`);
const cnameKey = `${userKeyHash}-${userIdHex}-${randomKey}`;
const safeDomain = param.domain.replaceAll('.', '-');
const safeDomain = param.domain.replaceAll(".", "-");
param.recordValue = `${safeDomain}.${cnameKey}.${cnameProvider.domain}`;
}
async update(param: any) {
if (!param.id) {
throw new ValidateException('id不能为空');
throw new ValidateException("id不能为空");
}
const old = await this.info(param.id);
if (!old) {
throw new ValidateException('数据不存在');
throw new ValidateException("数据不存在");
}
if (param.domain && old.domain !== param.domain) {
throw new ValidateException('域名不允许修改');
throw new ValidateException("域名不允许修改");
}
if (param.cnameProviderId && old.cnameProviderId !== param.cnameProviderId) {
const cnameProvider = await this.cnameProviderService.info(param.cnameProviderId);
await this.cnameProviderChanged(old.userId, param, cnameProvider);
param.status = 'cname';
param.status = "cname";
}
return await super.update(param);
}
@@ -170,7 +170,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
} else {
record.commonDnsProvider = new CommonDnsProvider({
config: record.cnameProvider,
plusService: this.plusService,
plusService: this.plusService
});
}
@@ -179,15 +179,15 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
async getByDomain(domain: string, userId: number, createOnNotFound = true) {
if (!domain) {
throw new ValidateException('domain不能为空');
throw new ValidateException("domain不能为空");
}
if (userId == null) {
throw new ValidateException('userId不能为空');
throw new ValidateException("userId不能为空");
}
let record = await this.getRepository().findOne({where: {domain, userId}});
let record = await this.getRepository().findOne({ where: { domain, userId } });
if (record == null) {
if (createOnNotFound) {
record = await this.add({domain, userId});
record = await this.add({ domain, userId });
} else {
throw new ValidateException(`找不到${domain}的CNAME记录`);
}
@@ -203,22 +203,34 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
return {
...record,
cnameProvider: {
...provider,
} as CnameProvider,
...provider
} as CnameProvider
} as CnameRecord;
}
private async fillMainDomain(record: CnameRecordEntity) {
if (!record.mainDomain) {
async fillMainDomain(record: CnameRecordEntity, update = true) {
const notMainDomain = !record.mainDomain;
const hasErrorMainDomain = record.mainDomain && !record.mainDomain.includes(".");
if (notMainDomain || hasErrorMainDomain) {
let domainPrefix = record.hostRecord.replace("_acme-challenge", "");
if (domainPrefix.startsWith(".")) {
domainPrefix = domainPrefix.substring(1);
}
record.mainDomain = record.domain.replace(domainPrefix+".", "");
await this.update({
id: record.id,
mainDomain: record.mainDomain
});
if (domainPrefix) {
const prefixStr = domainPrefix + ".";
record.mainDomain = record.domain.substring(prefixStr.length);
}else{
record.mainDomain = record.domain;
}
if (update) {
await this.update({
id: record.id,
mainDomain: record.mainDomain
});
}
}
}
@@ -231,11 +243,13 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
if (!bean) {
throw new ValidateException(`CnameRecord:${id} 不存在`);
}
if (bean.status === 'valid') {
if (bean.status === "valid") {
return true;
}
const subDomainGetter = new SubDomainsGetter(bean.userId, this.subDomainService)
await this.getByDomain(bean.domain, bean.userId);
const subDomainGetter = new SubDomainsGetter(bean.userId, this.subDomainService);
const domainParser = new DomainParser(subDomainGetter);
const cacheKey = `cname.record.verify.${bean.id}`;
@@ -245,7 +259,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
value = {
validating: false,
pass: false,
startTime: new Date().getTime(),
startTime: new Date().getTime()
};
}
let ttl = 5 * 60 * 1000;
@@ -267,16 +281,16 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
//公共CNAME
return new CommonDnsProvider({
config: cnameProvider,
plusService: this.plusService,
plusService: this.plusService
});
}
const serviceGetter = this.taskServiceBuilder.create({userId:cnameProvider.userId})
const serviceGetter = this.taskServiceBuilder.create({ userId: cnameProvider.userId });
const access = await this.accessService.getById(cnameProvider.accessId, cnameProvider.userId);
const context = {access, logger, http, utils, domainParser,serviceGetter};
const context = { access, logger, http, utils, domainParser, serviceGetter };
const dnsProvider: IDnsProvider = await createDnsProvider({
dnsProviderType: cnameProvider.dnsProviderType,
context,
context
});
return dnsProvider;
};
@@ -284,15 +298,15 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
const clearVerifyRecord = async () => {
cache.delete(cacheKey);
try {
let dnsProvider = value.dnsProvider
let dnsProvider = value.dnsProvider;
if (!dnsProvider) {
dnsProvider = await buildDnsProvider();
}
await dnsProvider.removeRecord({
recordReq: value.recordReq,
recordRes: value.recordRes,
recordRes: value.recordRes
});
logger.info('删除CNAME的校验DNS记录成功');
logger.info("删除CNAME的校验DNS记录成功");
} catch (e) {
logger.error(`删除CNAME的校验DNS记录失败 ${e.message}req:${JSON.stringify(value.recordReq)}recordRes:${JSON.stringify(value.recordRes)}`, e);
}
@@ -305,8 +319,8 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
if (value.startTime + ttl < new Date().getTime()) {
logger.warn(`cname验证超时,停止检查,${bean.domain} ${testRecordValue}`);
clearInterval(value.intervalId);
await this.updateStatus(bean.id, 'timeout');
await clearVerifyRecord()
await this.updateStatus(bean.id, "timeout");
await clearVerifyRecord();
return false;
}
@@ -317,7 +331,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
logger.info(`检查CNAME配置 ${fullDomain} ${testRecordValue}`);
//检查是否有重复的acme配置
await this.checkRepeatAcmeChallengeRecords(fullDomain,bean.recordValue)
await this.checkRepeatAcmeChallengeRecords(fullDomain, bean.recordValue);
// const txtRecords = await dns.promises.resolveTxt(fullDomain);
// if (txtRecords.length) {
@@ -334,9 +348,9 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
if (success) {
clearInterval(value.intervalId);
logger.info(`检测到CNAME配置,修改状态 ${fullDomain} ${testRecordValue}`);
await this.updateStatus(bean.id, 'valid', "");
await this.updateStatus(bean.id, "valid", "");
value.pass = true;
await clearVerifyRecord()
await clearVerifyRecord();
return success;
}
};
@@ -347,88 +361,88 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
}
cache.set(cacheKey, value, {
ttl: ttl,
ttl: ttl
});
const domain = await domainParser.parse(bean.recordValue);
const fullRecord = bean.recordValue;
const hostRecord = fullRecord.replace(`.${domain}`, '');
const hostRecord = fullRecord.replace(`.${domain}`, "");
const req = {
domain: domain,
fullRecord: fullRecord,
hostRecord: hostRecord,
type: 'TXT',
value: testRecordValue,
type: "TXT",
value: testRecordValue
};
const dnsProvider = await buildDnsProvider();
if(dnsProvider.usePunyCode()){
if (dnsProvider.usePunyCode()) {
//是否需要中文转英文
req.domain = dnsProvider.punyCodeEncode(req.domain)
req.fullRecord = dnsProvider.punyCodeEncode(req.fullRecord)
req.hostRecord = dnsProvider.punyCodeEncode(req.hostRecord)
req.value = dnsProvider.punyCodeEncode(req.value)
req.domain = dnsProvider.punyCodeEncode(req.domain);
req.fullRecord = dnsProvider.punyCodeEncode(req.fullRecord);
req.hostRecord = dnsProvider.punyCodeEncode(req.hostRecord);
req.value = dnsProvider.punyCodeEncode(req.value);
}
const recordRes = await dnsProvider.createRecord(req);
value.dnsProvider = dnsProvider;
value.validating = true;
value.recordReq = req;
value.recordRes = recordRes;
await this.updateStatus(bean.id, 'validating', "");
await this.updateStatus(bean.id, "validating", "");
value.intervalId = setInterval(async () => {
try {
await checkRecordValue();
} catch (e) {
logger.error('检查cname出错', e);
logger.error("检查cname出错", e);
await this.updateError(bean.id, e.message);
}
}, 10000);
}
async updateStatus(id: number, status: CnameRecordStatusType, error?: string) {
const updated: any = {status}
const updated: any = { status };
if (error != null) {
updated.error = error
updated.error = error;
}
await this.getRepository().update(id, updated);
}
async updateError(id: number, error: string) {
await this.getRepository().update(id, {error});
await this.getRepository().update(id, { error });
}
async checkRepeatAcmeChallengeRecords(acmeRecordDomain: string,targetCnameDomain:string) {
async checkRepeatAcmeChallengeRecords(acmeRecordDomain: string, targetCnameDomain: string) {
let dnsResolver = null
try{
dnsResolver = await getAuthoritativeDnsResolver(acmeRecordDomain)
}catch (e) {
logger.error(`获取${acmeRecordDomain}的权威DNS服务器失败${e.message}`)
return
let dnsResolver = null;
try {
dnsResolver = await getAuthoritativeDnsResolver(acmeRecordDomain);
} catch (e) {
logger.error(`获取${acmeRecordDomain}的权威DNS服务器失败${e.message}`);
return;
}
let cnameRecords = []
try{
let cnameRecords = [];
try {
cnameRecords = await dnsResolver.resolveCname(acmeRecordDomain);
}catch (e) {
logger.error(`查询CNAME记录失败${e.message}`)
return
} catch (e) {
logger.error(`查询CNAME记录失败${e.message}`);
return;
}
targetCnameDomain = targetCnameDomain.toLowerCase()
targetCnameDomain = punycode.toASCII(targetCnameDomain)
targetCnameDomain = targetCnameDomain.toLowerCase();
targetCnameDomain = punycode.toASCII(targetCnameDomain);
if (cnameRecords.length > 0) {
for (const cnameRecord of cnameRecords) {
if(cnameRecord.toLowerCase() !== targetCnameDomain){
if (cnameRecord.toLowerCase() !== targetCnameDomain) {
//确保只有一个cname记录
throw new Error(`${acmeRecordDomain}存在多个CNAME记录请删除多余的CNAME记录${cnameRecord}`)
throw new Error(`${acmeRecordDomain}存在多个CNAME记录请删除多余的CNAME记录${cnameRecord}`);
}
}
}
// 确保权威服务器里面没有纯粹的TXT记录
let txtRecords = []
try{
let txtRecords = [];
try {
const txtRecordRes = await dnsResolver.resolveTxt(acmeRecordDomain);
if (txtRecordRes && txtRecordRes.length > 0) {
@@ -436,13 +450,13 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
logger.info(`TXT records: ${JSON.stringify(txtRecords)}`);
txtRecords = txtRecords.concat(...txtRecordRes);
}
}catch (e) {
logger.error(`查询Txt记录失败${e.message}`)
} catch (e) {
logger.error(`查询Txt记录失败${e.message}`);
}
if (txtRecords.length === 0) {
//如果权威服务器中查不到txt无需继续检查
return
return;
}
if (cnameRecords.length > 0) {
// 从cname记录中获取txt记录
@@ -451,7 +465,7 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
if (res.length > 0) {
for (const txtRecord of txtRecords) {
if (!res.includes(txtRecord)) {
throw new Error(`${acmeRecordDomain}存在多个TXT记录请删除多余的TXT记录:${txtRecord}`)
throw new Error(`${acmeRecordDomain}存在多个TXT记录请删除多余的TXT记录:${txtRecord}`);
}
}
}
@@ -459,10 +473,10 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
}
async resetStatus (id: number) {
async resetStatus(id: number) {
if (!id) {
throw new ValidateException('id不能为空');
throw new ValidateException("id不能为空");
}
await this.getRepository().update(id, {status: 'cname',mainDomain: ""});
await this.getRepository().update(id, { status: "cname", mainDomain: "" });
}
}

View File

@@ -11,12 +11,12 @@ import {
import { RoleService } from "../../sys/authority/service/role-service.js";
import { UserEntity } from "../../sys/authority/entity/user.js";
import { cache, utils } from "@certd/basic";
import { LoginErrorException } from "@certd/lib-server/dist/basic/exception/login-error-exception.js";
import { LoginErrorException } from "@certd/lib-server";
import { CodeService } from "../../basic/service/code-service.js";
import { TwoFactorService } from "../../mine/service/two-factor-service.js";
import { UserSettingsService } from "../../mine/service/user-settings-service.js";
import { isPlus } from "@certd/plus-core";
import { AddonService } from "@certd/lib-server/dist/user/addon/service/addon-service.js";
import { AddonService } from "@certd/lib-server";
/**
* 系统用户

View File

@@ -28,6 +28,7 @@ export class UserSiteMonitorSetting extends BaseSettings {
cron?:string = undefined;
retryTimes?:number = 3;
dnsServer?:string[] = undefined;
certValidDays?:number = 10;
}
export class UserEmailSetting extends BaseSettings {

View File

@@ -275,13 +275,12 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
}
async sendExpiresNotify(site: SiteInfoEntity) {
const tipDays = 10;
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId, UserSiteMonitorSetting)
const tipDays = setting?.certValidDays || 10;
const expires = site.certExpiresTime;
const validDays = dayjs(expires).diff(dayjs(), "day");
const url = await this.notificationService.getBindUrl("#/certd/monitor/site");
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId, UserSiteMonitorSetting)
const content = `站点名称: ${site.name} \n站点域名 ${site.domain} \n证书域名 ${site.certDomains} \n颁发机构 ${site.certProvider} \n过期时间 ${dayjs(site.certExpiresTime).format("YYYY-MM-DD")} \n`;
if (validDays >= 0 && validDays < tipDays) {
// 发通知

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