diff --git a/CHANGELOG.md b/CHANGELOG.md index 10ff8c38c..816b51387 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +### Bug Fixes + +* 修复商业版退出登录后,丢失站点个性化设置的bug ([d75dd05](https://github.com/certd/certd/commit/d75dd058d65c85f80c49e1fa7a910e6c6f08e824)) +* 修复授权类型和名称字段排到最后的bug ([43b7977](https://github.com/certd/certd/commit/43b79778ea9034065f6a15af3296274315597c6b)) +* 修复证书监控某些情况下报 options.lookup不能为null的bug ([d2ecfe5](https://github.com/certd/certd/commit/d2ecfe5491b2639eb30b5cae293af6062d58bb9f)) +* 修复证书手动托管时新上传的证书无效的bug ([506385e](https://github.com/certd/certd/commit/506385e5a2600887fe30854e0713583caaa2e689)) +* 修复secret patch 类型多了type:的bug ([d04f383](https://github.com/certd/certd/commit/d04f3831611011a90ec0594724b9694490d5edd0)) + +### Performance Improvements + +* 登录支持极验验证码 ([370db62](https://github.com/certd/certd/commit/370db62bf0aece241859244927beabba32d6a257)) +* 登录注册、找回密码都支持极验验证码和图片验证码 ([7bdde68](https://github.com/certd/certd/commit/7bdde68ecea29fe2c570fd3cb082139db6c93d93)) +* 优化加量包展示效果 ([3c65f37](https://github.com/certd/certd/commit/3c65f37d84177ba107d4a6462648af12d2fc4b7a)) +* 证书到期剩余天数进度条根据实际证书有效期计算 ([#528](https://github.com/certd/certd/issues/528)) nicheng-he ([2d4586b](https://github.com/certd/certd/commit/2d4586b1c42c39f97d2a95b9453cca4bc8bfbe61)) +* add preferred chain option ([#519](https://github.com/certd/certd/issues/519)) @ZeroClover ([902359f](https://github.com/certd/certd/commit/902359f24ed12eee4f9b65178f1d6a60378351d2)) +* ssh配置增加脚本类型设置,bash还是sh ([ae41c60](https://github.com/certd/certd/commit/ae41c6038b27c9476e64a2402a8daf247c38a5b6)) +* start.sh增加sudo ([b7271d7](https://github.com/certd/certd/commit/b7271d7a464773a1bf87d7d1f24d933ba0f86915)) + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) ### Bug Fixes diff --git a/build.trigger b/build.trigger index 222b6abb3..bdd657b74 100644 --- a/build.trigger +++ b/build.trigger @@ -1 +1 @@ -00:34 +01:01 diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 8ac252506..4261d7efb 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -107,7 +107,6 @@ export default defineConfig({ text: "常见问题", items: [ {text: "QA", link: "/guide/qa/use.md"}, - {text: "常见报错处理", link: "/guide/qa/"}, {text: "群晖证书部署", link: "/guide/use/synology/"}, {text: "腾讯云密钥获取", link: "/guide/use/tencent/"}, {text: "连接windows主机", link: "/guide/use/host/windows.md"}, diff --git a/docs/guide/changelogs/CHANGELOG.md b/docs/guide/changelogs/CHANGELOG.md index 10ff8c38c..816b51387 100644 --- a/docs/guide/changelogs/CHANGELOG.md +++ b/docs/guide/changelogs/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +### Bug Fixes + +* 修复商业版退出登录后,丢失站点个性化设置的bug ([d75dd05](https://github.com/certd/certd/commit/d75dd058d65c85f80c49e1fa7a910e6c6f08e824)) +* 修复授权类型和名称字段排到最后的bug ([43b7977](https://github.com/certd/certd/commit/43b79778ea9034065f6a15af3296274315597c6b)) +* 修复证书监控某些情况下报 options.lookup不能为null的bug ([d2ecfe5](https://github.com/certd/certd/commit/d2ecfe5491b2639eb30b5cae293af6062d58bb9f)) +* 修复证书手动托管时新上传的证书无效的bug ([506385e](https://github.com/certd/certd/commit/506385e5a2600887fe30854e0713583caaa2e689)) +* 修复secret patch 类型多了type:的bug ([d04f383](https://github.com/certd/certd/commit/d04f3831611011a90ec0594724b9694490d5edd0)) + +### Performance Improvements + +* 登录支持极验验证码 ([370db62](https://github.com/certd/certd/commit/370db62bf0aece241859244927beabba32d6a257)) +* 登录注册、找回密码都支持极验验证码和图片验证码 ([7bdde68](https://github.com/certd/certd/commit/7bdde68ecea29fe2c570fd3cb082139db6c93d93)) +* 优化加量包展示效果 ([3c65f37](https://github.com/certd/certd/commit/3c65f37d84177ba107d4a6462648af12d2fc4b7a)) +* 证书到期剩余天数进度条根据实际证书有效期计算 ([#528](https://github.com/certd/certd/issues/528)) nicheng-he ([2d4586b](https://github.com/certd/certd/commit/2d4586b1c42c39f97d2a95b9453cca4bc8bfbe61)) +* add preferred chain option ([#519](https://github.com/certd/certd/issues/519)) @ZeroClover ([902359f](https://github.com/certd/certd/commit/902359f24ed12eee4f9b65178f1d6a60378351d2)) +* ssh配置增加脚本类型设置,bash还是sh ([ae41c60](https://github.com/certd/certd/commit/ae41c6038b27c9476e64a2402a8daf247c38a5b6)) +* start.sh增加sudo ([b7271d7](https://github.com/certd/certd/commit/b7271d7a464773a1bf87d7d1f24d933ba0f86915)) + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) ### Bug Fixes diff --git a/docs/guide/install/source/index.md b/docs/guide/install/source/index.md index 8f404264b..3c25c889d 100644 --- a/docs/guide/install/source/index.md +++ b/docs/guide/install/source/index.md @@ -12,7 +12,7 @@ git clone https://github.com/certd/certd --depth=1 # git checkout v1.x.x # 当v2主干分支代码无法正常启动时,可以尝试此命令,1.x.x换成最新版本号 cd certd # 启动服务 -./start.sh +./start.sh ``` >如果是windows,请先安装`git for windows` ,然后右键,选择`open git bash here`打开终端,再执行`./start.sh`命令 @@ -21,9 +21,9 @@ cd certd ### 访问测试 -http://your_server_ip:7001 -https://your_server_ip:7002 -默认账号密码:admin/123456 +http://your_server_ip:7001 +https://your_server_ip:7002 +默认账号密码:admin/123456 记得修改密码 @@ -37,7 +37,7 @@ cp -rf ./packages/ui/certd-server/data ../certd-data-backup git pull # 如果提示pull失败,可以尝试强制更新 -# git checkout v2 -f && git pull +# git checkout v2 -f && git pull # 先停止旧的服务,7001是certd的默认端口 kill -9 $(lsof -t -i:7001) @@ -45,16 +45,31 @@ kill -9 $(lsof -t -i:7001) ./start.sh ``` -::: warning -升级certd版本前,切记切记先备份一下数据 +::: warning +升级certd版本前,切记切记先备份一下数据 ::: ## 三、数据备份 -> 数据默认保存在 `./packages/ui/certd-server/data` 目录下 +> 数据默认保存在 `./packages/ui/certd-server/data` 目录下 > 建议配置一条[数据库备份流水线](../../use/backup/) 自动备份 ## 四、备份恢复 将备份的`db.sqlite`及同目录下的其他文件覆盖到原来的位置,重启certd即可 + +## 六、常见问题 + +### 1. npm install better-sqlite3 时,提示node-gyp需要vscode环境编译 + +1. 首先确保node版本为22以上 +2. 将下面两行加到 ~/.npmrc 里面 +3. 重新install +> better_sqlite3_binary_host=https://registry.npmmirror.com/-/binary/better-sqlite3 +> better_sqlite3_binary_host_mirror=https://registry.npmmirror.com/-/binary/better-sqlite3 + + + + + diff --git a/docs/guide/qa/use.md b/docs/guide/qa/use.md index 36f3ce4db..b7cbce7ba 100644 --- a/docs/guide/qa/use.md +++ b/docs/guide/qa/use.md @@ -1,4 +1,4 @@ -# 使用问题 +# 常见问题 ## 1. 是否支持IP证书 @@ -7,8 +7,14 @@ ## 2. 建议设置多长时间运行一次流水线 -建议每天运行一次,检查证书过期时间 +建议每天运行一次,检查证书过期时间 当证书没过期时,自动跳过部署 当证书到期前35天(创建流水线时可以修改),将会自动重新申请证书,自动部署 +## 3. too many certificates 错误 +当出现如下报错时,说明相同的域名短时间内申请超过5次 +解决方案:可以加多一个子域名,重新执行就可以规避次错误 +``` +"detail": too many certificates (5) already issued for this exact set of idantifiers in the last 168hm0s +``` \ No newline at end of file diff --git a/lerna.json b/lerna.json index 20b55aad1..f6c081b8d 100644 --- a/lerna.json +++ b/lerna.json @@ -9,5 +9,5 @@ } }, "npmClient": "pnpm", - "version": "1.36.19" + "version": "1.36.20" } diff --git a/packages/core/acme-client/CHANGELOG.md b/packages/core/acme-client/CHANGELOG.md index d9cff0293..744156ce9 100644 --- a/packages/core/acme-client/CHANGELOG.md +++ b/packages/core/acme-client/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/publishlab/node-acme-client/compare/v1.36.19...v1.36.20) (2025-09-13) + +**Note:** Version bump only for package @certd/acme-client + ## [1.36.19](https://github.com/publishlab/node-acme-client/compare/v1.36.18...v1.36.19) (2025-09-05) ### Performance Improvements diff --git a/packages/core/acme-client/package.json b/packages/core/acme-client/package.json index 3ff1e30cd..b546c4201 100644 --- a/packages/core/acme-client/package.json +++ b/packages/core/acme-client/package.json @@ -3,7 +3,7 @@ "description": "Simple and unopinionated ACME client", "private": false, "author": "nmorsman", - "version": "1.36.19", + "version": "1.36.20", "type": "module", "module": "scr/index.js", "main": "src/index.js", @@ -18,7 +18,7 @@ "types" ], "dependencies": { - "@certd/basic": "^1.36.19", + "@certd/basic": "^1.36.20", "@peculiar/x509": "^1.11.0", "asn1js": "^3.0.5", "axios": "^1.7.2", @@ -69,5 +69,5 @@ "bugs": { "url": "https://github.com/publishlab/node-acme-client/issues" }, - "gitHead": "6d8981479517b5de9634e242c1ebf22e70527ec4" + "gitHead": "ef46aeae6fd766883aeac61a59ce1dfec07f6ca6" } diff --git a/packages/core/acme-client/src/client.js b/packages/core/acme-client/src/client.js index f6b389fa3..b2ff14dc5 100644 --- a/packages/core/acme-client/src/client.js +++ b/packages/core/acme-client/src/client.js @@ -502,7 +502,7 @@ class AcmeClient { await verify[challenge.type](authz, challenge, keyAuthorization); }; - log('Waiting for ACME challenge verification(等待ACME挑战验证)', this.backoffOpts); + log('Waiting for ACME challenge verification(等待ACME挑战验证)'); return util.retry(verifyFn, this.backoffOpts); } diff --git a/packages/core/basic/CHANGELOG.md b/packages/core/basic/CHANGELOG.md index b3503bc8d..6ad6bcf8e 100644 --- a/packages/core/basic/CHANGELOG.md +++ b/packages/core/basic/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +**Note:** Version bump only for package @certd/basic + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) ### Bug Fixes diff --git a/packages/core/basic/build.md b/packages/core/basic/build.md index 52e26d31c..69cb5aa8e 100644 --- a/packages/core/basic/build.md +++ b/packages/core/basic/build.md @@ -1 +1 @@ -00:30 +00:56 diff --git a/packages/core/basic/package.json b/packages/core/basic/package.json index 68eefa3d9..1c1cea66e 100644 --- a/packages/core/basic/package.json +++ b/packages/core/basic/package.json @@ -1,7 +1,7 @@ { "name": "@certd/basic", "private": false, - "version": "1.36.19", + "version": "1.36.20", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -45,5 +45,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "6d8981479517b5de9634e242c1ebf22e70527ec4" + "gitHead": "ef46aeae6fd766883aeac61a59ce1dfec07f6ca6" } diff --git a/packages/core/pipeline/CHANGELOG.md b/packages/core/pipeline/CHANGELOG.md index c8c7396e3..cbbcb9556 100644 --- a/packages/core/pipeline/CHANGELOG.md +++ b/packages/core/pipeline/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +**Note:** Version bump only for package @certd/pipeline + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) ### Bug Fixes diff --git a/packages/core/pipeline/package.json b/packages/core/pipeline/package.json index 7025c7cd1..4c82e4ced 100644 --- a/packages/core/pipeline/package.json +++ b/packages/core/pipeline/package.json @@ -1,7 +1,7 @@ { "name": "@certd/pipeline", "private": false, - "version": "1.36.19", + "version": "1.36.20", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -17,8 +17,8 @@ "pub": "npm publish" }, "dependencies": { - "@certd/basic": "^1.36.19", - "@certd/plus-core": "^1.36.19", + "@certd/basic": "^1.36.20", + "@certd/plus-core": "^1.36.20", "dayjs": "^1.11.7", "lodash-es": "^4.17.21", "reflect-metadata": "^0.1.13" @@ -44,5 +44,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "6d8981479517b5de9634e242c1ebf22e70527ec4" + "gitHead": "ef46aeae6fd766883aeac61a59ce1dfec07f6ca6" } diff --git a/packages/core/pipeline/src/registry/registry.ts b/packages/core/pipeline/src/registry/registry.ts index b466bcc9f..3322d7080 100644 --- a/packages/core/pipeline/src/registry/registry.ts +++ b/packages/core/pipeline/src/registry/registry.ts @@ -69,9 +69,15 @@ export class Registry { return this.storage; } - getDefineList() { + getDefineList(prefix?: string) { let list = []; + if (prefix) { + prefix = prefix + ":"; + } for (const key in this.storage) { + if (prefix && !key.startsWith(prefix)) { + continue; + } const define = this.getDefine(key); if (define) { if (define?.deprecated) { @@ -90,7 +96,10 @@ export class Registry { return list; } - getDefine(key: string) { + getDefine(key: string, prefix?: string) { + if (prefix) { + key = prefix + ":" + key; + } const item = this.storage[key]; if (!item) { return; diff --git a/packages/libs/lib-huawei/CHANGELOG.md b/packages/libs/lib-huawei/CHANGELOG.md index fb1dba6aa..6490e01a0 100644 --- a/packages/libs/lib-huawei/CHANGELOG.md +++ b/packages/libs/lib-huawei/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +**Note:** Version bump only for package @certd/lib-huawei + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) **Note:** Version bump only for package @certd/lib-huawei diff --git a/packages/libs/lib-huawei/package.json b/packages/libs/lib-huawei/package.json index 37cd34dcd..27b502189 100644 --- a/packages/libs/lib-huawei/package.json +++ b/packages/libs/lib-huawei/package.json @@ -1,7 +1,7 @@ { "name": "@certd/lib-huawei", "private": false, - "version": "1.36.19", + "version": "1.36.20", "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": "6d8981479517b5de9634e242c1ebf22e70527ec4" + "gitHead": "ef46aeae6fd766883aeac61a59ce1dfec07f6ca6" } diff --git a/packages/libs/lib-iframe/CHANGELOG.md b/packages/libs/lib-iframe/CHANGELOG.md index e3608ca1e..9915d3469 100644 --- a/packages/libs/lib-iframe/CHANGELOG.md +++ b/packages/libs/lib-iframe/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +**Note:** Version bump only for package @certd/lib-iframe + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) **Note:** Version bump only for package @certd/lib-iframe diff --git a/packages/libs/lib-iframe/package.json b/packages/libs/lib-iframe/package.json index 468ba05b2..14f6e88ee 100644 --- a/packages/libs/lib-iframe/package.json +++ b/packages/libs/lib-iframe/package.json @@ -1,7 +1,7 @@ { "name": "@certd/lib-iframe", "private": false, - "version": "1.36.19", + "version": "1.36.20", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -31,5 +31,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "6d8981479517b5de9634e242c1ebf22e70527ec4" + "gitHead": "ef46aeae6fd766883aeac61a59ce1dfec07f6ca6" } diff --git a/packages/libs/lib-jdcloud/CHANGELOG.md b/packages/libs/lib-jdcloud/CHANGELOG.md index 852315b23..f4535e3ba 100644 --- a/packages/libs/lib-jdcloud/CHANGELOG.md +++ b/packages/libs/lib-jdcloud/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +**Note:** Version bump only for package @certd/jdcloud + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) **Note:** Version bump only for package @certd/jdcloud diff --git a/packages/libs/lib-jdcloud/package.json b/packages/libs/lib-jdcloud/package.json index 00bed88fb..b53bd8d2e 100644 --- a/packages/libs/lib-jdcloud/package.json +++ b/packages/libs/lib-jdcloud/package.json @@ -1,6 +1,6 @@ { "name": "@certd/jdcloud", - "version": "1.36.19", + "version": "1.36.20", "description": "jdcloud openApi sdk", "main": "./dist/bundle.js", "module": "./dist/bundle.js", @@ -61,5 +61,5 @@ "fetch" ] }, - "gitHead": "6d8981479517b5de9634e242c1ebf22e70527ec4" + "gitHead": "ef46aeae6fd766883aeac61a59ce1dfec07f6ca6" } diff --git a/packages/libs/lib-k8s/CHANGELOG.md b/packages/libs/lib-k8s/CHANGELOG.md index 0c147305c..236489ee8 100644 --- a/packages/libs/lib-k8s/CHANGELOG.md +++ b/packages/libs/lib-k8s/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +### Bug Fixes + +* 修复secret patch 类型多了type:的bug ([d04f383](https://github.com/certd/certd/commit/d04f3831611011a90ec0594724b9694490d5edd0)) + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) ### Bug Fixes diff --git a/packages/libs/lib-k8s/package.json b/packages/libs/lib-k8s/package.json index 2965a546c..983284f55 100644 --- a/packages/libs/lib-k8s/package.json +++ b/packages/libs/lib-k8s/package.json @@ -1,7 +1,7 @@ { "name": "@certd/lib-k8s", "private": false, - "version": "1.36.19", + "version": "1.36.20", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -17,7 +17,7 @@ "pub": "npm publish" }, "dependencies": { - "@certd/basic": "^1.36.19", + "@certd/basic": "^1.36.20", "@kubernetes/client-node": "0.21.0" }, "devDependencies": { @@ -32,5 +32,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "6d8981479517b5de9634e242c1ebf22e70527ec4" + "gitHead": "ef46aeae6fd766883aeac61a59ce1dfec07f6ca6" } diff --git a/packages/libs/lib-k8s/src/lib/k8s.client.ts b/packages/libs/lib-k8s/src/lib/k8s.client.ts index 5a980bf75..d5f8f6bd0 100644 --- a/packages/libs/lib-k8s/src/lib/k8s.client.ts +++ b/packages/libs/lib-k8s/src/lib/k8s.client.ts @@ -85,7 +85,6 @@ export class K8sClient { /** * 创建Secret * @param opts {namespace:default, body:yamlStr} - * @returns {Promise<*>} */ async createSecret(opts: { namespace: string; body: V1Secret }) { const namespace = opts.namespace || "default"; @@ -121,7 +120,7 @@ export class K8sClient { //没有找到,则创建 const body = merge( { - type: "type: kubernetes.io/tls", + type: "kubernetes.io/tls", }, opts.body ); diff --git a/packages/libs/lib-server/CHANGELOG.md b/packages/libs/lib-server/CHANGELOG.md index 9b9a0c16f..2557b4eeb 100644 --- a/packages/libs/lib-server/CHANGELOG.md +++ b/packages/libs/lib-server/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +### Performance Improvements + +* 登录支持极验验证码 ([370db62](https://github.com/certd/certd/commit/370db62bf0aece241859244927beabba32d6a257)) +* 登录注册、找回密码都支持极验验证码和图片验证码 ([7bdde68](https://github.com/certd/certd/commit/7bdde68ecea29fe2c570fd3cb082139db6c93d93)) + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) **Note:** Version bump only for package @certd/lib-server diff --git a/packages/libs/lib-server/package.json b/packages/libs/lib-server/package.json index 2181257bd..7c3cd749d 100644 --- a/packages/libs/lib-server/package.json +++ b/packages/libs/lib-server/package.json @@ -1,6 +1,6 @@ { "name": "@certd/lib-server", - "version": "1.36.19", + "version": "1.36.20", "description": "midway with flyway, sql upgrade way ", "private": false, "type": "module", @@ -27,10 +27,10 @@ ], "license": "AGPL", "dependencies": { - "@certd/acme-client": "^1.36.19", - "@certd/basic": "^1.36.19", - "@certd/pipeline": "^1.36.19", - "@certd/plus-core": "^1.36.19", + "@certd/acme-client": "^1.36.20", + "@certd/basic": "^1.36.20", + "@certd/pipeline": "^1.36.20", + "@certd/plus-core": "^1.36.20", "@midwayjs/cache": "~3.14.0", "@midwayjs/core": "~3.20.3", "@midwayjs/i18n": "~3.20.3", @@ -61,5 +61,5 @@ "typeorm": "^0.3.11", "typescript": "^5.4.2" }, - "gitHead": "6d8981479517b5de9634e242c1ebf22e70527ec4" + "gitHead": "ef46aeae6fd766883aeac61a59ce1dfec07f6ca6" } diff --git a/packages/libs/lib-server/src/index.ts b/packages/libs/lib-server/src/index.ts index a6d3f603b..f4bfaf593 100644 --- a/packages/libs/lib-server/src/index.ts +++ b/packages/libs/lib-server/src/index.ts @@ -1,8 +1,9 @@ import { SysSettingsEntity } from './system/index.js'; import { AccessEntity } from './user/access/entity/access.js'; +import { AddonEntity } from "./user/index.js"; export * from './basic/index.js'; export * from './system/index.js'; export * from './user/index.js'; export { LibServerConfiguration as Configuration } from './configuration.js'; -export const libServerEntities = [SysSettingsEntity, AccessEntity]; +export const libServerEntities = [SysSettingsEntity, AccessEntity,AddonEntity]; diff --git a/packages/libs/lib-server/src/system/settings/service/models.ts b/packages/libs/lib-server/src/system/settings/service/models.ts index c31f14fbe..af7ddf052 100644 --- a/packages/libs/lib-server/src/system/settings/service/models.ts +++ b/packages/libs/lib-server/src/system/settings/service/models.ts @@ -30,6 +30,13 @@ export class SysPublicSettings extends BaseSettings { mpsNo?: string; robots?: boolean = true; aiChatEnabled = true; + + + //验证码是否开启 + captchaEnabled = false; + //验证码类型 + captchaType?: string; + captchaAddonId?:number; } export class SysPrivateSettings extends BaseSettings { @@ -207,4 +214,3 @@ export class SysSafeSetting extends BaseSettings { }; } - diff --git a/packages/libs/lib-server/src/user/addon/api/api.ts b/packages/libs/lib-server/src/user/addon/api/api.ts new file mode 100644 index 000000000..4e65ce86c --- /dev/null +++ b/packages/libs/lib-server/src/user/addon/api/api.ts @@ -0,0 +1,97 @@ +import { HttpClient, ILogger, utils } from "@certd/basic"; +import {upperFirst} from "lodash-es"; +import { FormItemProps, PluginRequestHandleReq, Registrable } from "@certd/pipeline"; + + +export type AddonRequestHandleReqInput = { + id?: number; + title?: string; + addon: T; +}; + +export type AddonRequestHandleReq = { + addonType: string; +} &PluginRequestHandleReq>; + +export type AddonInputDefine = FormItemProps & { + title: string; + required?: boolean; +}; +export type AddonDefine = Registrable & { + addonType: string; + needPlus?: boolean; + input?: { + [key: string]: AddonInputDefine; + }; + showTest?: boolean; +}; + +export type AddonInstanceConfig = { + id: number; + addonType: string; + type: string; + name: string; + userId: number; + setting: { + [key: string]: any; + }; +}; + + + +export interface IAddon { + ctx: AddonContext; + [key: string]: any; +} + +export type AddonContext = { + http: HttpClient; + logger: ILogger; + utils: typeof utils; +}; + +export abstract class BaseAddon implements IAddon { + define!: AddonDefine; + ctx!: AddonContext; + http!: HttpClient; + logger!: ILogger; + + + + // eslint-disable-next-line @typescript-eslint/no-empty-function + async onInstance() {} + setCtx(ctx: AddonContext) { + this.ctx = ctx; + this.http = ctx.http; + this.logger = ctx.logger; + } + setDefine = (define:AddonDefine) => { + this.define = define; + }; + + async onRequest(req:AddonRequestHandleReq) { + if (!req.action) { + throw new Error("action is required"); + } + + let methodName = req.action; + if (!req.action.startsWith("on")) { + methodName = `on${upperFirst(req.action)}`; + } + + // @ts-ignore + const method = this[methodName]; + if (method) { + // @ts-ignore + return await this[methodName](req.data); + } + throw new Error(`action ${req.action} not found`); + } + +} + + +export interface IAddonGetter { + getById(id: any): Promise; + getCommonById(id: any): Promise; +} diff --git a/packages/libs/lib-server/src/user/addon/api/decorator.ts b/packages/libs/lib-server/src/user/addon/api/decorator.ts new file mode 100644 index 000000000..6a96b49ac --- /dev/null +++ b/packages/libs/lib-server/src/user/addon/api/decorator.ts @@ -0,0 +1,65 @@ +// src/decorator/memoryCache.decorator.ts +import * as _ from "lodash-es"; +import { merge } from "lodash-es"; +import { addonRegistry } from "./registry.js"; +import { AddonContext, AddonDefine, AddonInputDefine } from "./api.js"; +import { Decorator } from "@certd/pipeline"; + +// 提供一个唯一 key +export const ADDON_CLASS_KEY = "pipeline:addon"; +export const ADDON_INPUT_KEY = "pipeline:addon:input"; + +export function IsAddon(define: AddonDefine): ClassDecorator { + return (target: any) => { + target = Decorator.target(target); + + const inputs: any = {}; + const properties = Decorator.getClassProperties(target); + for (const property in properties) { + const input = Reflect.getMetadata(ADDON_INPUT_KEY, target, property); + if (input) { + inputs[property] = input; + } + } + _.merge(define, { input: inputs }); + Reflect.defineMetadata(ADDON_CLASS_KEY, define, target); + target.define = define; + const key = `${define.addonType}:${define.name}`; + addonRegistry.register(key, { + define, + target: async () => { + return target; + }, + }); + }; +} + +export function AddonInput(input?: AddonInputDefine): PropertyDecorator { + return (target, propertyKey) => { + target = Decorator.target(target, propertyKey); + // const _type = Reflect.getMetadata("design:type", target, propertyKey); + Reflect.defineMetadata(ADDON_INPUT_KEY, input, target, propertyKey); + }; +} + +export async function newAddon(addonType:string,type: string, input: any, ctx: AddonContext) { + const key = `${addonType}:${type}` + const register = addonRegistry.get(key); + if (register == null) { + throw new Error(`${addonType} ${type} not found`); + } + // @ts-ignore + const pluginCls = await register.target(); + // @ts-ignore + const plugin = new pluginCls(); + merge(plugin, input); + if (!ctx) { + throw new Error("ctx is required"); + } + plugin.setDefine(register.define); + plugin.setCtx(ctx); + await plugin.onInstance(); + return plugin; +} + + diff --git a/packages/libs/lib-server/src/user/addon/api/index.ts b/packages/libs/lib-server/src/user/addon/api/index.ts new file mode 100644 index 000000000..9b9e3a489 --- /dev/null +++ b/packages/libs/lib-server/src/user/addon/api/index.ts @@ -0,0 +1,3 @@ +export * from "./api.js"; +export * from "./registry.js"; +export * from "./decorator.js"; diff --git a/packages/libs/lib-server/src/user/addon/api/registry.ts b/packages/libs/lib-server/src/user/addon/api/registry.ts new file mode 100644 index 000000000..643de99cf --- /dev/null +++ b/packages/libs/lib-server/src/user/addon/api/registry.ts @@ -0,0 +1,3 @@ +import { createRegistry } from "@certd/pipeline"; + +export const addonRegistry = createRegistry("addon"); diff --git a/packages/libs/lib-server/src/user/addon/entity/addon.ts b/packages/libs/lib-server/src/user/addon/entity/addon.ts new file mode 100644 index 000000000..4d16fb43b --- /dev/null +++ b/packages/libs/lib-server/src/user/addon/entity/addon.ts @@ -0,0 +1,44 @@ +import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; + +/** + */ +@Entity('cd_addon') +export class AddonEntity { + @PrimaryGeneratedColumn() + id: number; + @Column({ name: 'user_id', comment: '用户id' }) + userId: number; + @Column({ comment: '名称', length: 100 }) + name: string; + + + @Column({ name: 'addon_type', comment: 'addon类型', length: 100 }) + addonType: string; + + + @Column({ comment: '类型', length: 100 }) + type: string; + + @Column({ name: 'setting', comment: '设置', length: 10240, nullable: true }) + setting: string; + + @Column({ name: 'is_system', comment: '是否系统级别', nullable: false, default: false }) + isSystem: boolean; + + @Column({ name: 'is_default', comment: '是否默认', nullable: false, default: false }) + isDefault: boolean; + + + @Column({ + name: 'create_time', + comment: '创建时间', + default: () => 'CURRENT_TIMESTAMP', + }) + createTime: Date; + @Column({ + name: 'update_time', + comment: '修改时间', + default: () => 'CURRENT_TIMESTAMP', + }) + updateTime: Date; +} diff --git a/packages/libs/lib-server/src/user/addon/index.ts b/packages/libs/lib-server/src/user/addon/index.ts new file mode 100644 index 000000000..727232b4f --- /dev/null +++ b/packages/libs/lib-server/src/user/addon/index.ts @@ -0,0 +1,5 @@ +export * from './api/index.js' +export * from './entity/addon.js' +export * from './service/addon-service.js' +export * from './service/addon-getter.js' +export * from './service/addon-sys-getter.js' diff --git a/packages/libs/lib-server/src/user/addon/service/addon-getter.ts b/packages/libs/lib-server/src/user/addon/service/addon-getter.ts new file mode 100644 index 000000000..8e91ad7e0 --- /dev/null +++ b/packages/libs/lib-server/src/user/addon/service/addon-getter.ts @@ -0,0 +1,18 @@ +import { IAddonGetter } from "../api/index.js"; + +export class AddonGetter implements IAddonGetter { + userId: number; + getter: (id: any, userId?: number) => Promise; + constructor(userId: number, getter: (id: any, userId: number) => Promise) { + this.userId = userId; + this.getter = getter; + } + + async getById(id: any) { + return await this.getter(id, this.userId); + } + + async getCommonById(id: any) { + return await this.getter(id, 0); + } +} diff --git a/packages/libs/lib-server/src/user/addon/service/addon-service.ts b/packages/libs/lib-server/src/user/addon/service/addon-service.ts new file mode 100644 index 000000000..d58d2453c --- /dev/null +++ b/packages/libs/lib-server/src/user/addon/service/addon-service.ts @@ -0,0 +1,231 @@ +import { Provide, Scope, ScopeEnum } from "@midwayjs/core"; +import { InjectEntityModel } from "@midwayjs/typeorm"; +import { In, Repository } from "typeorm"; +import { AddonDefine, BaseService, PageReq, PermissionException, ValidateException } from "../../../index.js"; +import { addonRegistry, newAddon } from "../api/index.js"; +import { AddonEntity } from "../entity/addon.js"; +import { http, logger, utils } from "@certd/basic"; + +/** + * Addon + */ +@Provide() +@Scope(ScopeEnum.Request, {allowDowngrade: true}) +export class AddonService extends BaseService { + @InjectEntityModel(AddonEntity) + repository: Repository; + + //@ts-ignore + getRepository() { + return this.repository; + } + + async page(pageReq: PageReq) { + const res = await super.page(pageReq); + res.records = res.records.map(item => { + return item; + }); + return res; + } + + async add(param) { + let oldEntity = null; + if (param._copyFrom){ + oldEntity = await this.info(param._copyFrom); + if (oldEntity == null) { + throw new ValidateException('该Addon配置不存在,请确认是否已被删除'); + } + if (oldEntity.userId !== param.userId) { + throw new ValidateException('您无权查看该Addon配置'); + } + } + if (!param.userId){ + param.isSystem = true + }else{ + param.isSystem = false + } + delete param._copyFrom + return await super.add(param); + } + + + /** + * 修改 + * @param param 数据 + */ + async update(param) { + const oldEntity = await this.info(param.id); + if (oldEntity == null) { + throw new ValidateException('该Addon配置不存在,请确认是否已被删除'); + } + return await super.update(param); + } + + async getSimpleInfo(id: number) { + const entity = await this.info(id); + if (entity == null) { + throw new ValidateException('该Addon配置不存在,请确认是否已被删除'); + } + return { + id: entity.id, + name: entity.name, + userId: entity.userId, + addonType: entity.addonType, + type: entity.type, + }; + } + + async getAddonById(id: any, checkUserId: boolean, userId?: number): Promise { + const ctx = { + http: http, + logger: logger, + utils: utils, + }; + + + if (!id){ + //使用图片验证码 + return await newAddon("captcha", "image", {},ctx); + } + const entity = await this.info(id); + if (entity == null) { + //使用图片验证码 + return await newAddon("captcha", "image", {},ctx); + } + if (checkUserId) { + if (userId == null) { + throw new ValidateException('userId不能为空'); + } + if (userId !== entity.userId) { + throw new PermissionException('您对该Addon无访问权限'); + } + } + + const setting = JSON.parse(entity.setting ??"{}") + const input = { + id: entity.id, + ...setting, + }; + + return await newAddon(entity.addonType, entity.type, input,ctx); + } + + async getById(id: any, userId: number): Promise { + return await this.getAddonById(id, true, userId); + } + + + getDefineList(addonType: string) { + return addonRegistry.getDefineList(); + } + + getDefineByType(type: string,prefix?: string) { + return addonRegistry.getDefine(type,prefix) as AddonDefine; + } + + + async getSimpleByIds(ids: number[], userId: any) { + if (ids.length === 0) { + return []; + } + if (!userId) { + return []; + } + return await this.repository.find({ + where: { + id: In(ids), + userId, + }, + select: { + id: true, + name: true, + addonType: true, + type: true, + userId:true, + isSystem: true, + }, + }); + + } + + + + async getDefault(userId: number,addonType: string): Promise { + const res = await this.repository.findOne({ + where: { + userId, + addonType + }, + order: { + isDefault: 'DESC', + }, + }); + if (!res) { + return null; + } + return this.buildAddonInstanceConfig(res); + } + + private buildAddonInstanceConfig(res: AddonEntity) { + const setting = JSON.parse(res.setting); + return { + id: res.id, + addonType: res.addonType, + type: res.type, + name: res.name, + userId: res.userId, + setting, + }; + } + + async setDefault(id: number, userId: number,addonType:string) { + if (!id) { + throw new ValidateException('id不能为空'); + } + if (!userId) { + throw new ValidateException('userId不能为空'); + } + await this.repository.update( + { + userId, + addonType + }, + { + isDefault: false, + } + ); + await this.repository.update( + { + id, + userId, + addonType + }, + { + isDefault: true, + } + ); + } + + async getOrCreateDefault(opts:{addonType:string,type:string, inputs: any, userId: any}) { + const {addonType,type,inputs,userId} = opts; + + const addonDefine = this.getDefineByType( type,addonType) + + const defaultConfig = await this.getDefault(userId,addonType); + if (defaultConfig) { + return defaultConfig; + } + const setting = { + ...inputs, + }; + const res = await this.repository.save({ + userId, + addonType, + type: type, + name: addonDefine.title, + setting: JSON.stringify(setting), + isDefault: true, + }); + return this.buildAddonInstanceConfig(res); + } +} diff --git a/packages/libs/lib-server/src/user/addon/service/addon-sys-getter.ts b/packages/libs/lib-server/src/user/addon/service/addon-sys-getter.ts new file mode 100644 index 000000000..773e1a7aa --- /dev/null +++ b/packages/libs/lib-server/src/user/addon/service/addon-sys-getter.ts @@ -0,0 +1,17 @@ +import { IAccessService } from '@certd/pipeline'; +import { AddonService } from './addon-service.js'; + +export class AddonSysGetter implements IAccessService { + addonService: AddonService; + constructor(addonService: AddonService) { + this.addonService = addonService; + } + + async getById(id: any) { + return await this.addonService.getById(id, 0); + } + + async getCommonById(id: any) { + return await this.addonService.getById(id, 0); + } +} diff --git a/packages/libs/lib-server/src/user/index.ts b/packages/libs/lib-server/src/user/index.ts index 17e3af2c4..f0fce929a 100644 --- a/packages/libs/lib-server/src/user/index.ts +++ b/packages/libs/lib-server/src/user/index.ts @@ -1 +1,2 @@ export * from './access/index.js'; +export * from './addon/index.js'; diff --git a/packages/libs/midway-flyway-js/CHANGELOG.md b/packages/libs/midway-flyway-js/CHANGELOG.md index a1b5e56ea..1904a95aa 100644 --- a/packages/libs/midway-flyway-js/CHANGELOG.md +++ b/packages/libs/midway-flyway-js/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +**Note:** Version bump only for package @certd/midway-flyway-js + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) **Note:** Version bump only for package @certd/midway-flyway-js diff --git a/packages/libs/midway-flyway-js/package.json b/packages/libs/midway-flyway-js/package.json index 34b30dec1..bc8dc5021 100644 --- a/packages/libs/midway-flyway-js/package.json +++ b/packages/libs/midway-flyway-js/package.json @@ -1,6 +1,6 @@ { "name": "@certd/midway-flyway-js", - "version": "1.36.19", + "version": "1.36.20", "description": "midway with flyway, sql upgrade way ", "private": false, "type": "module", @@ -46,5 +46,5 @@ "typeorm": "^0.3.11", "typescript": "^5.4.2" }, - "gitHead": "6d8981479517b5de9634e242c1ebf22e70527ec4" + "gitHead": "ef46aeae6fd766883aeac61a59ce1dfec07f6ca6" } diff --git a/packages/plugins/plugin-cert/CHANGELOG.md b/packages/plugins/plugin-cert/CHANGELOG.md index 43fbde4e5..a50ce7759 100644 --- a/packages/plugins/plugin-cert/CHANGELOG.md +++ b/packages/plugins/plugin-cert/CHANGELOG.md @@ -3,6 +3,17 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +### Bug Fixes + +* 修复证书手动托管时新上传的证书无效的bug ([506385e](https://github.com/certd/certd/commit/506385e5a2600887fe30854e0713583caaa2e689)) + +### Performance Improvements + +* 证书到期剩余天数进度条根据实际证书有效期计算 ([#528](https://github.com/certd/certd/issues/528)) nicheng-he ([2d4586b](https://github.com/certd/certd/commit/2d4586b1c42c39f97d2a95b9453cca4bc8bfbe61)) +* add preferred chain option ([#519](https://github.com/certd/certd/issues/519)) @ZeroClover ([902359f](https://github.com/certd/certd/commit/902359f24ed12eee4f9b65178f1d6a60378351d2)) + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) ### Performance Improvements diff --git a/packages/plugins/plugin-cert/package.json b/packages/plugins/plugin-cert/package.json index 6904eb4a3..41855806e 100644 --- a/packages/plugins/plugin-cert/package.json +++ b/packages/plugins/plugin-cert/package.json @@ -1,7 +1,7 @@ { "name": "@certd/plugin-cert", "private": false, - "version": "1.36.19", + "version": "1.36.20", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -16,10 +16,10 @@ "pub": "npm publish" }, "dependencies": { - "@certd/acme-client": "^1.36.19", - "@certd/basic": "^1.36.19", - "@certd/pipeline": "^1.36.19", - "@certd/plugin-lib": "^1.36.19", + "@certd/acme-client": "^1.36.20", + "@certd/basic": "^1.36.20", + "@certd/pipeline": "^1.36.20", + "@certd/plugin-lib": "^1.36.20", "@google-cloud/publicca": "^1.3.0", "dayjs": "^1.11.7", "jszip": "^3.10.1", @@ -43,5 +43,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "6d8981479517b5de9634e242c1ebf22e70527ec4" + "gitHead": "ef46aeae6fd766883aeac61a59ce1dfec07f6ca6" } diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/base-convert.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/base-convert.ts index e9d593139..e8ad9d756 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/base-convert.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/base-convert.ts @@ -99,6 +99,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin { const cert: CertInfo = certReader.toCertInfo(); this.cert = cert; + this._result.pipelineVars.certEffectiveTime = dayjs(certReader.detail.notBefore).valueOf(); this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.notAfter).valueOf(); if (!this._result.pipelinePrivateVars) { this._result.pipelinePrivateVars = {}; diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/cert-reader.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/cert-reader.ts index b2151871e..8fbaa32d3 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/cert-reader.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/cert-reader.ts @@ -35,6 +35,7 @@ export class CertReader { detail: CertificateInfo; //毫秒时间戳 + effective: number; expires: number; constructor(certInfo: CertInfo) { this.cert = certInfo; @@ -52,8 +53,9 @@ export class CertReader { } try { - const { detail, expires } = this.getCrtDetail(this.cert.crt); + const { detail, effective, expires } = this.getCrtDetail(this.cert.crt); this.detail = detail; + this.effective = effective.getTime(); this.expires = expires.getTime(); } catch (e) { throw new Error("证书解析失败:" + e.message); @@ -102,8 +104,9 @@ export class CertReader { static readCertDetail(crt: string) { const detail = crypto.readCertificateInfo(crt.toString()); + const effective = detail.notBefore; const expires = detail.notAfter; - return { detail, expires }; + return { detail, effective, expires }; } getAllDomains() { diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/custom/index.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/custom/index.ts index d11a719cb..841b8914e 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/custom/index.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/custom/index.ts @@ -118,7 +118,7 @@ export class CertApplyUploadPlugin extends CertApplyBaseConvertPlugin { } async execute(): Promise { - const certReader = await this.getCertFromStore(); + let certReader = await this.getCertFromStore(); const crtMd5 = this.ctx.utils.hash.md5(certReader.cert.crt); const leftDays = dayjs(certReader.expires).diff(dayjs(), "day"); @@ -141,9 +141,13 @@ export class CertApplyUploadPlugin extends CertApplyBaseConvertPlugin { this.logger.info("输入参数有变化,重新部署"); } + certReader = new CertReader(this.uploadCert); this.clearLastStatus(); //输出证书MD5 - this.certMd5 = crtMd5; + this.certMd5 = this.ctx.utils.hash.md5(certReader.cert.crt); + const newLeftDays = dayjs(certReader.expires).diff(dayjs(), "day"); + this.logger.info(`新证书过期时间${dayjs(certReader.expires).format("YYYY-MM-DD HH:mm:ss")},剩余${newLeftDays}天`); + await this.output(certReader, true); //必须output之后执行 diff --git a/packages/plugins/plugin-lib/CHANGELOG.md b/packages/plugins/plugin-lib/CHANGELOG.md index c9c150593..73200dd1d 100644 --- a/packages/plugins/plugin-lib/CHANGELOG.md +++ b/packages/plugins/plugin-lib/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +### Performance Improvements + +* ssh配置增加脚本类型设置,bash还是sh ([ae41c60](https://github.com/certd/certd/commit/ae41c6038b27c9476e64a2402a8daf247c38a5b6)) + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) ### Performance Improvements diff --git a/packages/plugins/plugin-lib/package.json b/packages/plugins/plugin-lib/package.json index ab7dd9717..921cd541b 100644 --- a/packages/plugins/plugin-lib/package.json +++ b/packages/plugins/plugin-lib/package.json @@ -1,7 +1,7 @@ { "name": "@certd/plugin-lib", "private": false, - "version": "1.36.19", + "version": "1.36.20", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -21,8 +21,8 @@ "@alicloud/pop-core": "^1.7.10", "@alicloud/tea-util": "^1.4.10", "@aws-sdk/client-s3": "^3.787.0", - "@certd/basic": "^1.36.19", - "@certd/pipeline": "^1.36.19", + "@certd/basic": "^1.36.20", + "@certd/pipeline": "^1.36.20", "@kubernetes/client-node": "0.21.0", "ali-oss": "^6.22.0", "basic-ftp": "^5.0.5", @@ -53,5 +53,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "6d8981479517b5de9634e242c1ebf22e70527ec4" + "gitHead": "ef46aeae6fd766883aeac61a59ce1dfec07f6ca6" } diff --git a/packages/plugins/plugin-lib/src/ssh/ssh-access.ts b/packages/plugins/plugin-lib/src/ssh/ssh-access.ts index 882071e44..4d20696d8 100644 --- a/packages/plugins/plugin-lib/src/ssh/ssh-access.ts +++ b/packages/plugins/plugin-lib/src/ssh/ssh-access.ts @@ -64,6 +64,22 @@ export class SshAccess extends BaseAccess { }) passphrase!: string; + @AccessInput({ + title: "脚本类型", + helper: "bash 、sh 、fish", + component: { + name: "a-select", + vModel: "value", + options: [ + { value: "default", label: "默认" }, + { value: "sh", label: "sh" }, + { value: "bash", label: "bash" }, + { value: "fish", label: "fish(不支持set -e)" }, + ], + }, + }) + scriptType: string; + @AccessInput({ title: "伪终端", helper: "如果登录报错:all authentication methods failed,可以尝试开启伪终端模式进行keyboard-interactive方式登录\n开启后对日志输出有一定的影响", diff --git a/packages/plugins/plugin-lib/src/ssh/ssh.ts b/packages/plugins/plugin-lib/src/ssh/ssh.ts index 32d83f303..197ebfc1d 100644 --- a/packages/plugins/plugin-lib/src/ssh/ssh.ts +++ b/packages/plugins/plugin-lib/src/ssh/ssh.ts @@ -543,8 +543,16 @@ export class SshClient { } } - if (isLinux && options.stopOnError !== false) { - script = "set -e\n" + script; + if (isLinux) { + if (options.connectConf.scriptType == "bash") { + script = "#!/usr/bin/env bash \n" + script; + } else if (options.connectConf.scriptType == "sh") { + script = "#!/bin/sh\n" + script; + } + + if (options.connectConf.scriptType != "fish" && options.stopOnError !== false) { + script = "set -e\n" + script; + } } return await conn.exec(script as string, { throwOnStdErr }); diff --git a/packages/ui/certd-client/CHANGELOG.md b/packages/ui/certd-client/CHANGELOG.md index b83431421..ebdf72c0d 100644 --- a/packages/ui/certd-client/CHANGELOG.md +++ b/packages/ui/certd-client/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +### Bug Fixes + +* 修复商业版退出登录后,丢失站点个性化设置的bug ([d75dd05](https://github.com/certd/certd/commit/d75dd058d65c85f80c49e1fa7a910e6c6f08e824)) +* 修复授权类型和名称字段排到最后的bug ([43b7977](https://github.com/certd/certd/commit/43b79778ea9034065f6a15af3296274315597c6b)) + +### Performance Improvements + +* 登录支持极验验证码 ([370db62](https://github.com/certd/certd/commit/370db62bf0aece241859244927beabba32d6a257)) +* 登录注册、找回密码都支持极验验证码和图片验证码 ([7bdde68](https://github.com/certd/certd/commit/7bdde68ecea29fe2c570fd3cb082139db6c93d93)) +* 优化加量包展示效果 ([3c65f37](https://github.com/certd/certd/commit/3c65f37d84177ba107d4a6462648af12d2fc4b7a)) +* 证书到期剩余天数进度条根据实际证书有效期计算 ([#528](https://github.com/certd/certd/issues/528)) nicheng-he ([2d4586b](https://github.com/certd/certd/commit/2d4586b1c42c39f97d2a95b9453cca4bc8bfbe61)) + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) ### Bug Fixes diff --git a/packages/ui/certd-client/index.html b/packages/ui/certd-client/index.html index 1740a304a..d760b2f58 100644 --- a/packages/ui/certd-client/index.html +++ b/packages/ui/certd-client/index.html @@ -23,5 +23,6 @@ + diff --git a/packages/ui/certd-client/package.json b/packages/ui/certd-client/package.json index a64fe5975..3cf954592 100644 --- a/packages/ui/certd-client/package.json +++ b/packages/ui/certd-client/package.json @@ -1,6 +1,6 @@ { "name": "@certd/ui-client", - "version": "1.36.19", + "version": "1.36.20", "private": true, "scripts": { "dev": "vite --open", @@ -32,10 +32,10 @@ "@aws-sdk/s3-request-presigner": "^3.535.0", "@certd/vue-js-cron-light": "^4.0.14", "@ctrl/tinycolor": "^4.1.0", - "@fast-crud/fast-crud": "^1.25.13", - "@fast-crud/fast-extends": "^1.25.13", - "@fast-crud/ui-antdv4": "^1.25.13", - "@fast-crud/ui-interface": "^1.25.13", + "@fast-crud/fast-crud": "^1.26.6", + "@fast-crud/fast-extends": "^1.26.6", + "@fast-crud/ui-antdv4": "^1.26.6", + "@fast-crud/ui-interface": "^1.26.6", "@iconify/tailwind": "^1.2.0", "@iconify/vue": "^4.1.1", "@manypkg/get-packages": "^2.2.2", @@ -103,8 +103,8 @@ "zod-defaults": "^0.1.3" }, "devDependencies": { - "@certd/lib-iframe": "^1.36.19", - "@certd/pipeline": "^1.36.19", + "@certd/lib-iframe": "^1.36.20", + "@certd/pipeline": "^1.36.20", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@types/chai": "^4.3.12", diff --git a/packages/ui/certd-client/src/components/captcha/captcha-input.vue b/packages/ui/certd-client/src/components/captcha/captcha-input.vue new file mode 100644 index 000000000..3a348c332 --- /dev/null +++ b/packages/ui/certd-client/src/components/captcha/captcha-input.vue @@ -0,0 +1,50 @@ + + diff --git a/packages/ui/certd-client/src/components/captcha/captchas/geetest_captcha.vue b/packages/ui/certd-client/src/components/captcha/captchas/geetest_captcha.vue new file mode 100644 index 000000000..926b9da96 --- /dev/null +++ b/packages/ui/certd-client/src/components/captcha/captchas/geetest_captcha.vue @@ -0,0 +1,96 @@ + + + diff --git a/packages/ui/certd-client/src/components/captcha/captchas/image_captcha.vue b/packages/ui/certd-client/src/components/captcha/captchas/image_captcha.vue new file mode 100644 index 000000000..50be867a0 --- /dev/null +++ b/packages/ui/certd-client/src/components/captcha/captchas/image_captcha.vue @@ -0,0 +1,59 @@ + + diff --git a/packages/ui/certd-client/src/components/plugins/common/remote-tree-select.vue b/packages/ui/certd-client/src/components/plugins/common/remote-tree-select.vue index 49fe5c35e..ca039db49 100644 --- a/packages/ui/certd-client/src/components/plugins/common/remote-tree-select.vue +++ b/packages/ui/certd-client/src/components/plugins/common/remote-tree-select.vue @@ -53,7 +53,6 @@ const pagerRef: Ref = ref({ current: 1, }); const getOptions = async () => { - debugger; if (loading.value) { return; } diff --git a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts index 19e35501a..2dbc451ad 100644 --- a/packages/ui/certd-client/src/locales/langs/en-US/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/en-US/certd.ts @@ -119,6 +119,7 @@ export default { scheduledTaskCount: "Scheduled Task Count", deployTaskCount: "Deployment Task Count", remainingValidity: "Remaining Validity", + effectiveTime: "Effective time", expiryTime: "Expiry Time", status: "Status", lastRun: "Last Run", @@ -250,7 +251,9 @@ export default { ok: "Valid", expired: "Expired", }, + certEffectiveTime: "Certificate Effective", certExpiresTime: "Certificate Expiration", + remainingValidity: "Remaining Validity", expired: "expired", days: "days", lastCheckTime: "Last Check Time", @@ -465,6 +468,7 @@ export default { validDays: "Valid Days", expires: " expires", days: " days", + effectiveTime: "Effective Time", expireTime: "Expiration Time", certIssuer: "Certificate Issuer", applyTime: "Application Time", @@ -707,10 +711,23 @@ export default { pipeline: "Pipeline", }, + addonType: "Type", + addonName: "Name", + addonNameHelper: "Fill freely, helps to distinguish when multiple same type exist", + addonTypeSelect: "Select type", sys: { setting: { showRunStrategy: "Show RunStrategy", showRunStrategyHelper: "Allow modify the run strategy of the task", + + captchaEnabled: "Enable Login Captcha", + captchaHelper: "Whether to enable captcha verification for login", + captchaType: "Captcha Setting", + + baseSetting: "Base Settings", + registerSetting: "Register Settings", + safeSetting: "Safe Settings", + paymentSetting: "Payment Settings", }, }, modal: { @@ -731,4 +748,8 @@ export default { challengeSetting: "Challenge Setting", gotoCnameTip: "Please go to CNAME Record Page", }, + addonSelector: { + select: "Select", + placeholder: "select please", + }, }; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts index a2966391c..49fb9d13b 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/certd.ts @@ -125,6 +125,7 @@ export default { scheduledTaskCount: "定时任务数", deployTaskCount: "部署任务数", remainingValidity: "到期剩余", + effectiveTime: "生效时间", expiryTime: "过期时间", status: "状态", lastRun: "最后运行", @@ -255,7 +256,9 @@ export default { ok: "正常", expired: "过期", }, + certEffectiveTime: "证书生效时间", certExpiresTime: "证书到期时间", + remainingValidity: "到期剩余", expired: "过期", days: "天", lastCheckTime: "上次检查时间", @@ -471,6 +474,7 @@ export default { validDays: "有效天数", expires: "过期", days: "天", + effectiveTime: "生效时间", expireTime: "过期时间", certIssuer: "证书颁发机构", applyTime: "申请时间", @@ -695,7 +699,10 @@ export default { setAsDefault: "设为默认", disabledLabel: "禁用", confirmToggleStatus: "确定要{action}吗?", - + addonType: "类型", + addonName: "名称", + addonNameHelper: "随意填写,相同类型助于区分即可", + addonTypeSelect: "请选择", template: { title: "流水线模版", edit: "流水线模版编辑", @@ -714,6 +721,15 @@ export default { setting: { showRunStrategy: "显示运行策略选择", showRunStrategyHelper: "任务设置中是否允许选择运行策略", + + captchaEnabled: "启用登录验证码", + captchaHelper: "登录时是否启用验证码", + captchaType: "验证码配置", + + baseSetting: "基本设置", + registerSetting: "注册设置", + safeSetting: "安全设置", + paymentSetting: "支付设置", }, }, modal: { @@ -734,4 +750,8 @@ export default { challengeSetting: "校验配置", gotoCnameTip: "CNAME域名配置请前往CNAME记录页面添加", }, + addonSelector: { + select: "选择", + placeholder: "请选择", + }, }; diff --git a/packages/ui/certd-client/src/store/plugin/index.ts b/packages/ui/certd-client/src/store/plugin/index.ts index 6ecb356e1..7f0880ee6 100644 --- a/packages/ui/certd-client/src/store/plugin/index.ts +++ b/packages/ui/certd-client/src/store/plugin/index.ts @@ -167,7 +167,7 @@ export const usePluginStore = defineStore({ }, async clear() { this.group = null; - this.originGroup = null + this.originGroup = null; }, async getList(): Promise { await this.init(); diff --git a/packages/ui/certd-client/src/store/settings/api.basic.ts b/packages/ui/certd-client/src/store/settings/api.basic.ts index ceaa16810..ebe7b6915 100644 --- a/packages/ui/certd-client/src/store/settings/api.basic.ts +++ b/packages/ui/certd-client/src/store/settings/api.basic.ts @@ -46,6 +46,10 @@ export type SysPublicSetting = { aiChatEnabled?: boolean; showRunStrategy?: boolean; + + captchaEnabled?: boolean; + captchaType?: number; + captchaAddonId?: number; }; export type SuiteSetting = { enabled?: boolean; diff --git a/packages/ui/certd-client/src/store/settings/index.ts b/packages/ui/certd-client/src/store/settings/index.ts index 2f6bf9a6f..fcfd98c76 100644 --- a/packages/ui/certd-client/src/store/settings/index.ts +++ b/packages/ui/certd-client/src/store/settings/index.ts @@ -12,6 +12,7 @@ import { utils } from "/@/utils"; import { cloneDeep, merge } from "lodash-es"; import { useI18n } from "/src/locales"; export interface SettingState { + skipReset?: boolean; // 注销登录时,不清空此store的状态 sysPublic?: SysPublicSetting; installInfo?: { siteId: string; @@ -64,6 +65,7 @@ const defaultSiteInfo: SiteInfo = { export const useSettingStore = defineStore({ id: "app.setting", state: (): SettingState => ({ + skipReset: true, plusInfo: { isPlus: false, vipType: "free", diff --git a/packages/ui/certd-client/src/vben/stores/setup.ts b/packages/ui/certd-client/src/vben/stores/setup.ts index ad2560a6c..09ee70fca 100644 --- a/packages/ui/certd-client/src/vben/stores/setup.ts +++ b/packages/ui/certd-client/src/vben/stores/setup.ts @@ -38,6 +38,9 @@ export function resetAllStores() { } const allStores = (pinia as any)._s; for (const [_key, store] of allStores) { + if (store.skipReset) { + continue; + } store.$reset(); } } diff --git a/packages/ui/certd-client/src/views/certd/access/common.tsx b/packages/ui/certd-client/src/views/certd/access/common.tsx index 65d30afe6..026065e51 100644 --- a/packages/ui/certd-client/src/views/certd/access/common.tsx +++ b/packages/ui/certd-client/src/views/certd/access/common.tsx @@ -87,6 +87,7 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) { order: -1, }, form: { + order: -1, component: { disabled: false, showSearch: true, diff --git a/packages/ui/certd-client/src/views/certd/access/crud.tsx b/packages/ui/certd-client/src/views/certd/access/crud.tsx index c0490c426..09b04133f 100644 --- a/packages/ui/certd-client/src/views/certd/access/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/access/crud.tsx @@ -79,6 +79,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat }, form: { rules: [{ required: true, message: "必填项" }], + order: -11, }, column: { width: 300, diff --git a/packages/ui/certd-client/src/views/certd/addon/addon-selector/index.vue b/packages/ui/certd-client/src/views/certd/addon/addon-selector/index.vue new file mode 100644 index 000000000..d64884aa5 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/addon/addon-selector/index.vue @@ -0,0 +1,178 @@ + + + + diff --git a/packages/ui/certd-client/src/views/certd/addon/api.ts b/packages/ui/certd-client/src/views/certd/addon/api.ts new file mode 100644 index 000000000..998d65cc0 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/addon/api.ts @@ -0,0 +1,142 @@ +import { request } from "/src/api/service"; +import { RequestHandleReq } from "/@/components/plugins/lib"; + +export function createAddonApi(opts: { from: any; addonType: string }) { + let apiPrefix = "/addon"; + if (opts.from === "sys") { + apiPrefix = "/sys/addon"; + } + return { + async GetList(query: any) { + return await request({ + url: apiPrefix + "/page", + method: "post", + data: { + ...query, + query: { + addonType: opts.addonType, + ...query.query, + }, + }, + }); + }, + + async AddObj(obj: any) { + return await request({ + url: apiPrefix + "/add", + method: "post", + data: { + ...obj, + addonType: opts.addonType, + }, + }); + }, + + async UpdateObj(obj: any) { + return await request({ + url: apiPrefix + "/update", + method: "post", + data: obj, + }); + }, + + async DelObj(id: number) { + return await request({ + url: apiPrefix + "/delete", + method: "post", + params: { id }, + }); + }, + + async GetObj(id: number) { + return await request({ + url: apiPrefix + "/info", + method: "post", + params: { id }, + }); + }, + + async GetOptions(id: number) { + return await request({ + url: apiPrefix + `/options?addonType=${opts.addonType}`, + method: "post", + }); + }, + + async SetDefault(id: number) { + return await request({ + url: apiPrefix + "/setDefault", + method: "post", + params: { id }, + }); + }, + + async GetDefaultId() { + return await request({ + url: apiPrefix + "/getDefaultId", + method: "post", + }); + }, + + async GetSimpleInfo(id: number) { + return await request({ + url: apiPrefix + `/simpleInfo?addonType=${opts.addonType}`, + method: "post", + params: { id }, + }); + }, + + async GetDefineTypes() { + return await request({ + url: apiPrefix + `/getTypeDict?addonType=${opts.addonType}`, + method: "post", + }); + }, + + async GetProviderDefine(type: string) { + return await request({ + url: apiPrefix + `/define?addonType=${opts.addonType}`, + method: "post", + params: { type }, + }); + }, + + async GetProviderDefineByType(type: string) { + return await request({ + url: apiPrefix + `/defineByType?addonType=${opts.addonType}`, + method: "post", + params: { type }, + }); + }, + + async Handle(req: RequestHandleReq, opts: any = {}) { + const url = `/handle/${req.type}?addonType=${opts.addonType}`; + const { typeName, action, data, input } = req; + const res = await request({ + url, + method: "post", + data: { + typeName, + action, + data, + input, + }, + ...opts, + }); + return res; + }, + }; +} + +export const AddonTypeDefines = { + captcha: { + name: "captcha", + title: "验证码", + showDefault: false, + showTest: false, + }, +}; + +export function getAddonTypeDefine(addonType: string) { + return AddonTypeDefines[addonType]; +} diff --git a/packages/ui/certd-client/src/views/certd/addon/common.tsx b/packages/ui/certd-client/src/views/certd/addon/common.tsx new file mode 100644 index 000000000..fa31e8c05 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/addon/common.tsx @@ -0,0 +1,280 @@ +import { ColumnCompositionProps, compute, dict } from "@fast-crud/fast-crud"; +import { computed, provide, ref, toRef } from "vue"; +import { useReference } from "/@/use/use-refrence"; +import { forEach, get, merge, set } from "lodash-es"; +import { Modal } from "ant-design-vue"; +import { mitter } from "/@/utils/util.mitt"; +import { useI18n } from "/src/locales"; +import * as pipelineApi from "/@/views/certd/pipeline/api"; +import { getAddonTypeDefine } from "/@/views/certd/addon/api"; + +export function addonProvide(api: any) { + provide("addonApi", api); + provide("get:plugin:type", () => { + return "addon"; + }); +} + +export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any, addonType: string) { + const { t } = useI18n(); + // const addonTypeTypeDictRef = dict({ + // data: [{ value: "captcha", label: "验证码" }], + // }); + const addonTypeDictRef = dict({ + url: `/addon/getTypeDict?addonType=${addonType}`, + }); + const defaultPluginConfig = { + component: { + name: "a-input", + vModel: "value", + }, + }; + + function buildDefineFields(define: any, form: any, mode: string) { + const formWrapperRef = crudExpose.getFormWrapperRef(); + const columnsRef = toRef(formWrapperRef.formOptions, "columns"); + + for (const key in columnsRef.value) { + if (key.indexOf(".") >= 0) { + delete columnsRef.value[key]; + } + } + console.log('crudBinding.value[mode + "Form"].columns', columnsRef.value); + forEach(define.input, (value: any, mapKey: any) => { + const key = "body." + mapKey; + const field = { + ...value, + key, + }; + const column = merge({ title: key }, defaultPluginConfig, field); + //eval + useReference(column); + + if (column.required) { + if (!column.rules) { + column.rules = []; + } + column.rules.push({ required: true, message: t("certd.requiredField") }); + } + + //设置默认值 + if (column.value != null && get(form, key) == null) { + set(form, key, column.value); + } + //字段配置赋值 + columnsRef.value[key] = column; + }); + } + + const currentDefine = ref(); + + return { + id: { + title: "ID", + key: "id", + type: "number", + column: { + width: 100, + }, + form: { + show: false, + }, + }, + // addonType: { + // title: "Addon类型", + // type: "dict-select", + // dict: addonTypeTypeDictRef, + // search: { + // show: false, + // }, + // column: { + // width: 200, + // component: { + // color: "auto", + // }, + // }, + // form: { + // onChange(ctx: { value: any }) { + // addonTypeDictRef.url = `/addon/getTypeDict?addonType=${ctx.value}`; + // }, + // }, + // editForm: { + // component: { + // disabled: false, + // }, + // }, + // }, + type: { + title: t("certd.addonType"), + type: "dict-select", + dict: addonTypeDictRef, + search: { + show: false, + }, + column: { + width: 200, + component: { + color: "auto", + }, + }, + editForm: { + component: { + disabled: false, + }, + }, + form: { + order: -22, + component: { + disabled: false, + showSearch: true, + filterOption: (input: string, option: any) => { + input = input?.toLowerCase(); + return option.value.toLowerCase().indexOf(input) >= 0 || option.label.toLowerCase().indexOf(input) >= 0; + }, + renderLabel(item: any) { + return ( + + {item.label} + {item.needPlus && } + + ); + }, + }, + rules: [{ required: true, message: t("certd.addonTypeSelect") }], + valueChange: { + immediate: true, + async handle({ value, mode, form, immediate }) { + if (value == null) { + return; + } + const lastTitle = currentDefine.value?.title; + const define = await api.GetProviderDefine(value); + currentDefine.value = define; + console.log("define", define); + + if (!immediate) { + form.body = {}; + if (define.needPlus) { + mitter.emit("openVipModal"); + } + } + + if (!form.name || form.name === lastTitle) { + form.name = define.title; + } + buildDefineFields(define, form, mode); + }, + }, + helper: computed(() => { + const define = currentDefine.value; + if (define == null) { + return ""; + } + return define.desc; + }), + }, + } as ColumnCompositionProps, + name: { + title: t("certd.addonName"), + search: { + show: true, + }, + type: ["text"], + form: { + order: -2, + rules: [{ required: true, message: t("certd.enterName") }], + helper: t("certd.addonNameHelper"), + }, + column: { + width: 200, + }, + }, + isDefault: { + title: t("certd.isDefault"), + type: "dict-switch", + dict: dict({ + data: [ + { label: t("certd.yes"), value: true, color: "success" }, + { label: t("certd.no"), value: false, color: "default" }, + ], + }), + form: { + show: computed(() => { + return getAddonTypeDefine(addonType)?.showDefault ?? false; + }), + value: false, + rules: [{ required: true, message: t("certd.selectIsDefault") }], + order: 999, + }, + column: { + align: "center", + width: 100, + show: computed(() => { + return getAddonTypeDefine(addonType)?.showDefault ?? false; + }), + component: { + name: "a-switch", + vModel: "checked", + disabled: compute(({ value }) => { + return value === true; + }), + on: { + // @ts-ignore + change({ row }) { + Modal.confirm({ + title: t("certd.prompt"), + content: t("certd.confirmSetDefaultNotification"), + onOk: async () => { + await api.SetDefault(row.id); + await crudExpose.doRefresh(); + }, + onCancel: async () => { + await crudExpose.doRefresh(); + }, + }); + }, + }, + }, + }, + }, + test: { + title: t("certd.test"), + form: { + show: compute(({ form }) => { + return !!form.type && currentDefine.value?.showTest === true; + }), + component: { + name: "api-test", + action: "TestRequest", + }, + order: 990, + col: { + span: 24, + }, + }, + column: { + show: false, + }, + }, + setting: { + column: { show: false }, + form: { + show: false, + valueBuilder({ value, form }) { + form.body = {}; + if (!value) { + return; + } + const setting = JSON.parse(value); + for (const key in setting) { + form.body[key] = setting[key]; + } + }, + valueResolve({ form }) { + const setting = form.body; + form.setting = JSON.stringify(setting); + }, + }, + } as ColumnCompositionProps, + }; +} diff --git a/packages/ui/certd-client/src/views/certd/addon/crud.tsx b/packages/ui/certd-client/src/views/certd/addon/crud.tsx new file mode 100644 index 000000000..73f00f03c --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/addon/crud.tsx @@ -0,0 +1,55 @@ +import { ref } from "vue"; +import { getCommonColumnDefine } from "./common"; +import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; + +export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet { + const api = context.api; + const addonType = context.addonType; + const pageRequest = async (query: UserPageQuery): Promise => { + return await api.GetList(query); + }; + const editRequest = async (req: EditReq) => { + const { form, row } = req; + form.id = row.id; + const res = await api.UpdateObj(form); + return res; + }; + const delRequest = async (req: DelReq) => { + const { row } = req; + return await api.DelObj(row.id); + }; + + const addRequest = async (req: AddReq) => { + const { form } = req; + const res = await api.AddObj(form); + return res; + }; + + const typeRef = ref(); + const commonColumnsDefine = getCommonColumnDefine(crudExpose, typeRef, api, addonType); + return { + crudOptions: { + request: { + pageRequest, + addRequest, + editRequest, + delRequest, + }, + form: { + labelCol: { + //固定label宽度 + span: null, + style: { + width: "145px", + }, + }, + }, + rowHandle: { + width: 200, + }, + columns: { + ...commonColumnsDefine, + }, + }, + }; +} diff --git a/packages/ui/certd-client/src/views/certd/addon/index.vue b/packages/ui/certd-client/src/views/certd/addon/index.vue new file mode 100644 index 000000000..3b4607db8 --- /dev/null +++ b/packages/ui/certd-client/src/views/certd/addon/index.vue @@ -0,0 +1,41 @@ + + + diff --git a/packages/ui/certd-client/src/views/certd/monitor/cert/crud.tsx b/packages/ui/certd-client/src/views/certd/monitor/cert/crud.tsx index 97e9754df..23fae9960 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/cert/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/monitor/cert/crud.tsx @@ -220,22 +220,47 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat sorter: true, conditionalRender: false, cellRender({ row }) { - const value = row.expiresTime; - if (!value) { + const { + applyTime, + effectiveTime, + expiresTime, + } = row || {}; + if (!expiresTime) { return "-"; } - const expireDate = dayjs(value).format("YYYY-MM-DD"); - const leftDays = dayjs(value).diff(dayjs(), "day"); + // 申请时间 ps:此处为证书在certd创建的时间而非实际证书申请时间 + const applyDate = dayjs(effectiveTime ?? applyTime ?? Date.now()).format("YYYY-MM-DD"); + // 失效时间 + const expireDate = dayjs(expiresTime).format("YYYY-MM-DD"); + // 有效天数 ps:此处证书最小设置为90d + const effectiveDays = Math.max(90, dayjs(expiresTime).diff(applyDate, "day")); + // 距离失效时间剩余天数 + const leftDays = dayjs(expiresTime).diff(dayjs(), "day"); const color = leftDays < 20 ? "red" : "#389e0d"; - const percent = (leftDays / 90) * 100; + const percent = (leftDays / effectiveDays) * 100; const textColor = leftDays < 20 ? "red" : leftDays > 60 ? "#389e0d" : ""; const format = () => { return {`${leftDays}${t("certd.days")}`}; }; + // console.log('cellRender', 'effectiveDays', effectiveDays, 'expiresTime', expiresTime, 'applyTime', applyTime, 'percent', percent, row) return ; }, }, }, + effectiveTime: { + title: t("certd.effectiveTime"), + search: { + show: false, + }, + type: "datetime", + form: { + show: false, + }, + column: { + sorter: true, + show: false, + }, + }, expiresTime: { title: t("certd.expireTime"), search: { diff --git a/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx b/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx index c53a32149..e11890836 100644 --- a/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/monitor/site/crud.tsx @@ -345,25 +345,64 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat align: "center", }, }, + certEffectiveTime: { + title: t("certd.monitor.certEffectiveTime"), + search: { + show: false, + }, + type: "datetime", + form: { + show: false, + }, + column: { + sorter: true, + width: 155, + }, + }, certExpiresTime: { title: t("certd.monitor.certExpiresTime"), search: { show: false, }, + type: "datetime", + form: { + show: false, + }, + column: { + sorter: true, + width: 155, + }, + }, + remainingValidity: { + title: t("certd.monitor.remainingValidity"), + search: { + show: false, + }, type: "date", form: { show: false, }, column: { - sorter: true, - cellRender({ value }) { - if (!value) { + conditionalRender: false, + cellRender({ row }) { + const { + certEffectiveTime: effectiveTime, + certExpiresTime: expiresTime, + } = row || {}; + if (!expiresTime) { return "-"; } - const expireDate = dayjs(value).format("YYYY-MM-DD"); - const leftDays = dayjs(value).diff(dayjs(), "day"); + // 申请时间 ps:此处为证书在certd创建的时间而非实际证书申请时间 + const applyDate = dayjs(effectiveTime ?? Date.now()).format("YYYY-MM-DD"); + // 失效时间 + const expireDate = dayjs(expiresTime).format("YYYY-MM-DD"); + // 有效天数 ps:此处证书最小设置为90d + const effectiveDays = Math.max(90, dayjs(expiresTime).diff(applyDate, "day")); + // 距离失效时间剩余天数 + const leftDays = dayjs(expiresTime).diff(dayjs(), "day"); const color = leftDays < 20 ? "red" : "#389e0d"; - const percent = (leftDays / 90) * 100; + const percent = (leftDays / effectiveDays) * 100; + // console.log('cellRender', 'effectiveDays', effectiveDays, 'expiresTime', expiresTime, 'applyTime', applyTime, 'percent', percent, row) return `${leftDays}${t("certd.monitor.days")}`} />; }, }, diff --git a/packages/ui/certd-client/src/views/certd/notification/common.tsx b/packages/ui/certd-client/src/views/certd/notification/common.tsx index 01d007b41..da648ffe0 100644 --- a/packages/ui/certd-client/src/views/certd/notification/common.tsx +++ b/packages/ui/certd-client/src/views/certd/notification/common.tsx @@ -96,6 +96,7 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) { }, }, form: { + order: -3, component: { disabled: false, showSearch: true, @@ -153,6 +154,7 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) { }, type: ["text"], form: { + order: -2, rules: [{ required: true, message: t("certd.enterName") }], helper: t("certd.helperNotificationName"), }, diff --git a/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx b/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx index 9e11102da..8ce42f4cc 100644 --- a/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx +++ b/packages/ui/certd-client/src/views/certd/pipeline/crud.tsx @@ -366,23 +366,49 @@ export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys }, column: { cellRender({ row }) { - const value = row.lastVars?.certExpiresTime; - if (!value) { + const { + certEffectiveTime: effectiveTime, + certExpiresTime: expiresTime, + } = row?.lastVars || {}; + if (!expiresTime) { return "-"; } - const expireDate = dayjs(value).format("YYYY-MM-DD"); - const leftDays = dayjs(value).diff(dayjs(), "day"); + // 申请时间 ps:此处为证书在certd创建的时间而非实际证书申请时间 + const applyDate = dayjs(effectiveTime ?? Date.now()).format("YYYY-MM-DD"); + // 失效时间 + const expireDate = dayjs(expiresTime).format("YYYY-MM-DD"); + // 有效天数 ps:此处证书最小设置为90d + const effectiveDays = Math.max(90, dayjs(expiresTime).diff(applyDate, "day")); + // 距离失效时间剩余天数 + const leftDays = dayjs(expiresTime).diff(dayjs(), "day"); const color = leftDays < 20 ? "red" : "#389e0d"; - const percent = (leftDays / 90) * 100; + const percent = (leftDays / effectiveDays) * 100; const textColor = leftDays < 20 ? "red" : leftDays > 60 ? "#389e0d" : ""; const format = () => { return {`${leftDays}${t("certd.days")}`}; }; + // console.log('cellRender', 'effectiveDays', effectiveDays, 'expiresTime', expiresTime, 'applyTime', applyTime, 'percent', percent, row) return ; }, width: 150, }, }, + "lastVars.certEffectiveTime": { + title: t("certd.fields.effectiveTime"), + search: { + show: false, + }, + type: "datetime", + form: { + show: false, + }, + column: { + sorter: false, + show: false, + width: 150, + align: "center", + }, + }, "lastVars.certExpiresTime": { title: t("certd.fields.expiryTime"), search: { diff --git a/packages/ui/certd-client/src/views/certd/suite/mine/api.ts b/packages/ui/certd-client/src/views/certd/suite/mine/api.ts index f647164d9..1e1faa42b 100644 --- a/packages/ui/certd-client/src/views/certd/suite/mine/api.ts +++ b/packages/ui/certd-client/src/views/certd/suite/mine/api.ts @@ -9,6 +9,8 @@ export type SuiteValue = { export type SuiteDetail = { enabled?: boolean; suites?: any[]; + suiteList?: any[]; + addonList?: any[]; expiresTime?: number; pipelineCount?: SuiteValue; domainCount?: SuiteValue; diff --git a/packages/ui/certd-client/src/views/framework/forgot-password/index.vue b/packages/ui/certd-client/src/views/framework/forgot-password/index.vue index 32b01d29e..f4e2befbc 100644 --- a/packages/ui/certd-client/src/views/framework/forgot-password/index.vue +++ b/packages/ui/certd-client/src/views/framework/forgot-password/index.vue @@ -20,8 +20,11 @@ + + + - + @@ -32,23 +35,15 @@ + + + - + - - - - + + + + @@ -32,12 +36,12 @@ - - + + - + @@ -87,14 +91,13 @@ import { defineComponent, nextTick, reactive, ref, toRaw } from "vue"; import { useUserStore } from "/src/store/user"; import { useSettingStore } from "/@/store/settings"; import { utils } from "@fast-crud/fast-crud"; -import ImageCode from "/@/views/framework/login/image-code.vue"; import SmsCode from "/@/views/framework/login/sms-code.vue"; import { useI18n } from "/@/locales"; import { LanguageToggle } from "/@/vben/layouts"; - +import CaptchaInput from "/@/components/captcha/captcha-input.vue"; export default defineComponent({ name: "LoginPage", - components: { LanguageToggle, SmsCode, ImageCode }, + components: { LanguageToggle, SmsCode, CaptchaInput }, setup() { const { t } = useI18n(); const verifyCodeInputRef = ref(); @@ -108,9 +111,9 @@ export default defineComponent({ mobile: "", password: "", loginType: "password", //password - imgCode: "", smsCode: "", - randomStr: "", + captcha: null, + smsCaptcha: null, }); const rules = { @@ -138,6 +141,12 @@ export default defineComponent({ message: "请输入短信验证码", }, ], + captcha: [ + { + required: true, + message: "请进行验证码验证", + }, + ], }; const layout = { labelCol: { @@ -160,6 +169,10 @@ export default defineComponent({ const handleFinish = async (values: any) => { loading.value = true; try { + // formState.captcha = await doCaptchaValidate(); + // if (!formState.captcha) { + // return; + // } const loginType = formState.loginType; await userStore.login(loginType, toRaw(formState)); } catch (e: any) { @@ -194,6 +207,21 @@ export default defineComponent({ return sysPublicSettings.registerEnabled && (sysPublicSettings.usernameRegisterEnabled || sysPublicSettings.emailRegisterEnabled); } + const captchaInputRef = ref(); + const captchaInputForSmsCode = ref(); + async function doCaptchaValidate() { + if (!sysPublicSettings.captchaEnabled) { + return {}; + } + const res = await captchaInputRef.value.getValidatedForm(); + if (!res) { + return false; + } + return { + ...res, + }; + } + return { t, loading, @@ -211,6 +239,8 @@ export default defineComponent({ handleTwoFactorSubmit, verifyCodeInputRef, settingStore, + captchaInputRef, + captchaInputForSmsCode, }; }, }); diff --git a/packages/ui/certd-client/src/views/framework/login/sms-code.vue b/packages/ui/certd-client/src/views/framework/login/sms-code.vue index efbf6ab36..80d85e4d1 100644 --- a/packages/ui/certd-client/src/views/framework/login/sms-code.vue +++ b/packages/ui/certd-client/src/views/framework/login/sms-code.vue @@ -5,7 +5,7 @@ -
+
{{ smsTime <= 0 ? "发送" : smsTime + " s" }} @@ -21,8 +21,7 @@ const props = defineProps<{ value?: string; mobile?: string; phoneCode?: string; - imgCode?: string; - randomStr?: string; + captcha?: any; verificationType?: string; }>(); const emit = defineEmits(["update:value", "change"]); @@ -48,8 +47,8 @@ async function sendSmsCode() { notification.error({ message: "请输入手机号" }); return; } - if (!props.imgCode) { - notification.error({ message: "请输入图片验证码" }); + if (!props.captcha) { + notification.error({ message: "请输入验证码" }); return; } loading.value = true; @@ -57,8 +56,7 @@ async function sendSmsCode() { await api.sendSmsCode({ phoneCode: props.phoneCode, mobile: props.mobile, - imgCode: props.imgCode, - randomStr: props.randomStr, + captcha: props.captcha, verificationType: props.verificationType, }); } finally { diff --git a/packages/ui/certd-client/src/views/framework/register/email-code.vue b/packages/ui/certd-client/src/views/framework/register/email-code.vue index 6ee92e1f8..5403f3270 100644 --- a/packages/ui/certd-client/src/views/framework/register/email-code.vue +++ b/packages/ui/certd-client/src/views/framework/register/email-code.vue @@ -5,7 +5,7 @@ -
+
{{ smsTime <= 0 ? "发送" : smsTime + " s" }} @@ -20,8 +20,7 @@ import * as api from "/@/store/settings/api.basic"; const props = defineProps<{ value?: string; email?: string; - imgCode?: string; - randomStr?: string; + captcha?: any; verificationType?: string; }>(); const emit = defineEmits(["update:value", "change"]); @@ -44,16 +43,15 @@ async function sendSmsCode() { notification.error({ message: "请输入邮箱" }); return; } - if (!props.imgCode) { - notification.error({ message: "请输入图片验证码" }); + if (!props.captcha) { + notification.error({ message: "请输入验证码" }); return; } loading.value = true; try { await api.sendEmailCode({ email: props.email, - imgCode: props.imgCode, - randomStr: props.randomStr, + captcha: props.captcha, verificationType: props.verificationType, }); } finally { diff --git a/packages/ui/certd-client/src/views/framework/register/index.vue b/packages/ui/certd-client/src/views/framework/register/index.vue index 38602cf67..23d85ee2e 100644 --- a/packages/ui/certd-client/src/views/framework/register/index.vue +++ b/packages/ui/certd-client/src/views/framework/register/index.vue @@ -25,8 +25,8 @@ - - + + @@ -61,12 +61,12 @@ - - + + - + @@ -86,13 +86,13 @@ import { defineComponent, reactive, ref, toRaw } from "vue"; import { useUserStore } from "/src/store/user"; import { utils } from "@fast-crud/fast-crud"; -import ImageCode from "/@/views/framework/login/image-code.vue"; import EmailCode from "./email-code.vue"; import { useSettingStore } from "/@/store/settings"; import { notification } from "ant-design-vue"; +import CaptchaInput from "/@/components/captcha/captcha-input.vue"; export default defineComponent({ name: "RegisterPage", - components: { EmailCode, ImageCode }, + components: { CaptchaInput, EmailCode }, setup() { const settingsStore = useSettingStore(); const registerType = ref("email"); @@ -114,7 +114,7 @@ export default defineComponent({ username: "", password: "", confirmPassword: "", - randomStr: "", + captcha: null, }); const rules = { @@ -159,17 +159,6 @@ export default defineComponent({ }, ], - imgCode: [ - { - required: true, - message: "请输入图片验证码", - }, - { - min: 4, - max: 4, - message: "请输入4位图片验证码", - }, - ], smsCode: [ { required: true, @@ -198,9 +187,8 @@ export default defineComponent({ type: registerType.value, password: formState.password, username: formState.username, - imgCode: formState.imgCode, - randomStr: formState.randomStr, email: formState.email, + captcha: formState.captcha, validateCode: formState.validateCode, }) as any ); @@ -214,16 +202,7 @@ export default defineComponent({ formRef.value.resetFields(); }; - const imageCodeUrl = ref(); - function resetImageCode() { - let url = "/basic/code"; - imageCodeUrl.value = url + "?t=" + new Date().getTime(); - } - resetImageCode(); - return { - resetImageCode, - imageCodeUrl, formState, formRef, rules, diff --git a/packages/ui/certd-client/src/views/sys/settings/index.vue b/packages/ui/certd-client/src/views/sys/settings/index.vue index c16a76c54..2c5af5de9 100644 --- a/packages/ui/certd-client/src/views/sys/settings/index.vue +++ b/packages/ui/certd-client/src/views/sys/settings/index.vue @@ -5,17 +5,17 @@
- + - + - + - - + +
@@ -30,9 +30,11 @@ import SettingSafe from "/@/views/sys/settings/tabs/safe.vue"; import { useRoute, useRouter } from "vue-router"; import { ref } from "vue"; import { useSettingStore } from "/@/store/settings"; +import { useI18n } from "/@/locales"; defineOptions({ name: "SysSettings", }); +const { t } = useI18n(); const settingsStore = useSettingStore(); const activeKey = ref("base"); const route = useRoute(); diff --git a/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue b/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue index a3024ae4c..a7f53c56e 100644 --- a/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue +++ b/packages/ui/certd-client/src/views/sys/settings/tabs/base.vue @@ -47,6 +47,18 @@
+ + +
+
+ + + + + + {{ t("certd.saveButton") }} @@ -63,7 +75,7 @@ import { useSettingStore } from "/@/store/settings"; import { notification } from "ant-design-vue"; import { util } from "/@/utils"; import { useI18n } from "/src/locales"; - +import AddonSelector from "../../../certd/addon/addon-selector/index.vue"; const { t } = useI18n(); defineOptions({ @@ -115,6 +127,10 @@ async function stopOtherUserTimer() { }); } +function onAddonChanged(target: any) { + formState.public.captchaType = target.type; +} + const testProxyLoading = ref(false); async function testProxy() { testProxyLoading.value = true; diff --git a/packages/ui/certd-server/CHANGELOG.md b/packages/ui/certd-server/CHANGELOG.md index 6ab273d6f..ad0da6c95 100644 --- a/packages/ui/certd-server/CHANGELOG.md +++ b/packages/ui/certd-server/CHANGELOG.md @@ -3,6 +3,20 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.20](https://github.com/certd/certd/compare/v1.36.19...v1.36.20) (2025-09-13) + +### Bug Fixes + +* 修复证书监控某些情况下报 options.lookup不能为null的bug ([d2ecfe5](https://github.com/certd/certd/commit/d2ecfe5491b2639eb30b5cae293af6062d58bb9f)) +* 修复证书手动托管时新上传的证书无效的bug ([506385e](https://github.com/certd/certd/commit/506385e5a2600887fe30854e0713583caaa2e689)) + +### Performance Improvements + +* 登录支持极验验证码 ([370db62](https://github.com/certd/certd/commit/370db62bf0aece241859244927beabba32d6a257)) +* 登录注册、找回密码都支持极验验证码和图片验证码 ([7bdde68](https://github.com/certd/certd/commit/7bdde68ecea29fe2c570fd3cb082139db6c93d93)) +* 证书到期剩余天数进度条根据实际证书有效期计算 ([#528](https://github.com/certd/certd/issues/528)) nicheng-he ([2d4586b](https://github.com/certd/certd/commit/2d4586b1c42c39f97d2a95b9453cca4bc8bfbe61)) +* add preferred chain option ([#519](https://github.com/certd/certd/issues/519)) @ZeroClover ([902359f](https://github.com/certd/certd/commit/902359f24ed12eee4f9b65178f1d6a60378351d2)) + ## [1.36.19](https://github.com/certd/certd/compare/v1.36.18...v1.36.19) (2025-09-05) ### Bug Fixes diff --git a/packages/ui/certd-server/db/migration-mysql/v10029__cert_effective_time.sql b/packages/ui/certd-server/db/migration-mysql/v10029__cert_effective_time.sql new file mode 100644 index 000000000..1f4761ee5 --- /dev/null +++ b/packages/ui/certd-server/db/migration-mysql/v10029__cert_effective_time.sql @@ -0,0 +1,2 @@ +ALTER TABLE cd_cert_info ADD COLUMN effective_time bigint; +ALTER TABLE cd_site_info ADD COLUMN cert_effective_time bigint; diff --git a/packages/ui/certd-server/db/migration-mysql/v10030__addon.sql b/packages/ui/certd-server/db/migration-mysql/v10030__addon.sql new file mode 100644 index 000000000..2dabb526c --- /dev/null +++ b/packages/ui/certd-server/db/migration-mysql/v10030__addon.sql @@ -0,0 +1,13 @@ + +CREATE TABLE `cd_addon` ( + `id` bigint PRIMARY KEY AUTO_INCREMENT NOT NULL, + `user_id` bigint NOT NULL, + `name` varchar(100) NOT NULL, + `type` varchar(100) NOT NULL, + `addon_type` varchar(100) NOT NULL, + `is_default` boolean NOT NULL DEFAULT false, + `is_system` boolean NOT NULL DEFAULT false, + `setting` longtext, + `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/packages/ui/certd-server/db/migration-pg/v10029__cert_effective_time.sql b/packages/ui/certd-server/db/migration-pg/v10029__cert_effective_time.sql new file mode 100644 index 000000000..1f4761ee5 --- /dev/null +++ b/packages/ui/certd-server/db/migration-pg/v10029__cert_effective_time.sql @@ -0,0 +1,2 @@ +ALTER TABLE cd_cert_info ADD COLUMN effective_time bigint; +ALTER TABLE cd_site_info ADD COLUMN cert_effective_time bigint; diff --git a/packages/ui/certd-server/db/migration-pg/v10030__addon.sql b/packages/ui/certd-server/db/migration-pg/v10030__addon.sql new file mode 100644 index 000000000..c53785b2a --- /dev/null +++ b/packages/ui/certd-server/db/migration-pg/v10030__addon.sql @@ -0,0 +1,13 @@ + +CREATE TABLE "cd_addon" ( + "id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL, + "user_id" bigint NOT NULL, + "name" varchar(100) NOT NULL, + "type" varchar(100) NOT NULL, + "addon_type" varchar(100) NOT NULL, + "is_default" boolean NOT NULL DEFAULT (false), + "is_system" boolean NOT NULL DEFAULT (false), + "setting" text, + "create_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP), + "update_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP) +); diff --git a/packages/ui/certd-server/db/migration/v10029__cert_effective_time.sql b/packages/ui/certd-server/db/migration/v10029__cert_effective_time.sql new file mode 100644 index 000000000..b38feb3c2 --- /dev/null +++ b/packages/ui/certd-server/db/migration/v10029__cert_effective_time.sql @@ -0,0 +1,2 @@ +ALTER TABLE cd_cert_info ADD COLUMN effective_time INTEGER; +ALTER TABLE cd_site_info ADD COLUMN cert_effective_time INTEGER; diff --git a/packages/ui/certd-server/db/migration/v10030__addon.sql b/packages/ui/certd-server/db/migration/v10030__addon.sql new file mode 100644 index 000000000..be7c3a951 --- /dev/null +++ b/packages/ui/certd-server/db/migration/v10030__addon.sql @@ -0,0 +1,13 @@ + +CREATE TABLE "cd_addon" ( + "id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, + "user_id" integer NOT NULL, + "name" varchar(100) NOT NULL, + "type" varchar(100) NOT NULL, + "addon_type" varchar(100) NOT NULL, + "is_default" boolean NOT NULL DEFAULT (false), + "is_system" boolean NOT NULL DEFAULT (false), + "setting" text, + "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), + "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP) +); diff --git a/packages/ui/certd-server/db/transform.js b/packages/ui/certd-server/db/transform.js index 9ae039605..ff414845e 100644 --- a/packages/ui/certd-server/db/transform.js +++ b/packages/ui/certd-server/db/transform.js @@ -39,6 +39,7 @@ function transformPG() { pgSql = pgSql.replaceAll(/boolean DEFAULT \(0\)/g, 'boolean DEFAULT (false)'); pgSql = pgSql.replaceAll(/boolean.*NOT NULL DEFAULT \(0\)/g, 'boolean NOT NULL DEFAULT (false)'); pgSql = pgSql.replaceAll(/integer/g, 'bigint'); + pgSql = pgSql.replaceAll(/INTEGER/g, 'bigint'); pgSql = pgSql.replaceAll(/last_insert_rowid\(\)/g, 'LASTVAL()'); fs.writeFileSync(`./migration-pg/${notFile}`, pgSql); } @@ -66,6 +67,7 @@ function transformMysql() { //DEFAULT (xxx) 替换成 DEFAULT xxx pgSql = pgSql.replaceAll(/DEFAULT \(([^)]*)\)/g, 'DEFAULT $1'); pgSql = pgSql.replaceAll(/integer/g, 'bigint'); + pgSql = pgSql.replaceAll(/INTEGER/g, 'bigint'); pgSql = pgSql.replaceAll(/last_insert_rowid\(\)/g, 'LAST_INSERT_ID()'); //text 改成longtext pgSql = pgSql.replaceAll(/text/g, 'longtext'); diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json index 1b675ef0b..8970ccfdd 100644 --- a/packages/ui/certd-server/package.json +++ b/packages/ui/certd-server/package.json @@ -1,6 +1,6 @@ { "name": "@certd/ui-server", - "version": "1.36.19", + "version": "1.36.20", "description": "fast-server base midway", "private": true, "type": "module", @@ -43,20 +43,20 @@ "@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.19", - "@certd/basic": "^1.36.19", - "@certd/commercial-core": "^1.36.19", + "@certd/acme-client": "^1.36.20", + "@certd/basic": "^1.36.20", + "@certd/commercial-core": "^1.36.20", "@certd/cv4pve-api-javascript": "^8.4.2", - "@certd/jdcloud": "^1.36.19", - "@certd/lib-huawei": "^1.36.19", - "@certd/lib-k8s": "^1.36.19", - "@certd/lib-server": "^1.36.19", - "@certd/midway-flyway-js": "^1.36.19", - "@certd/pipeline": "^1.36.19", - "@certd/plugin-cert": "^1.36.19", - "@certd/plugin-lib": "^1.36.19", - "@certd/plugin-plus": "^1.36.19", - "@certd/plus-core": "^1.36.19", + "@certd/jdcloud": "^1.36.20", + "@certd/lib-huawei": "^1.36.20", + "@certd/lib-k8s": "^1.36.20", + "@certd/lib-server": "^1.36.20", + "@certd/midway-flyway-js": "^1.36.20", + "@certd/pipeline": "^1.36.20", + "@certd/plugin-cert": "^1.36.20", + "@certd/plugin-lib": "^1.36.20", + "@certd/plugin-plus": "^1.36.20", + "@certd/plus-core": "^1.36.20", "@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120", "@huaweicloud/huaweicloud-sdk-core": "^3.1.120", "@koa/cors": "^5.0.0", diff --git a/packages/ui/certd-server/src/controller/basic/code-controller.ts b/packages/ui/certd-server/src/controller/basic/code-controller.ts index bb7c0f6e9..dee5a0954 100644 --- a/packages/ui/certd-server/src/controller/basic/code-controller.ts +++ b/packages/ui/certd-server/src/controller/basic/code-controller.ts @@ -1,8 +1,9 @@ -import { Rule, RuleType } from '@midwayjs/validate'; -import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from '@midwayjs/core'; -import { BaseController, Constants } from '@certd/lib-server'; -import { CodeService } from '../../modules/basic/service/code-service.js'; -import { EmailService } from '../../modules/basic/service/email-service.js'; +import { Rule, RuleType } from "@midwayjs/validate"; +import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core"; +import { BaseController, Constants, SysSettingsService } from "@certd/lib-server"; +import { CodeService } from "../../modules/basic/service/code-service.js"; +import { EmailService } from "../../modules/basic/service/email-service.js"; +import { CaptchaService } from "../../modules/basic/service/captcha-service.js"; export class SmsCodeReq { @Rule(RuleType.string().required()) @@ -11,11 +12,8 @@ export class SmsCodeReq { @Rule(RuleType.string().required()) mobile: string; - @Rule(RuleType.string().required().max(10)) - randomStr: string; - - @Rule(RuleType.string().required().max(4)) - imgCode: string; + @Rule(RuleType.required()) + captcha: any; @Rule(RuleType.string()) verificationType: string; @@ -25,11 +23,8 @@ export class EmailCodeReq { @Rule(RuleType.string().required()) email: string; - @Rule(RuleType.string().required().max(10)) - randomStr: string; - - @Rule(RuleType.string().required().max(4)) - imgCode: string; + @Rule(RuleType.required()) + captcha: any; @Rule(RuleType.string()) verificationType: string; @@ -48,6 +43,17 @@ export class BasicController extends BaseController { @Inject() emailService: EmailService; + @Inject() + sysSettingsService: SysSettingsService; + + @Inject() + captchaService: CaptchaService; + + @Post('/captcha/get', { summary: Constants.per.guest }) + async getCaptcha(@Query("captchaAddonId") captchaAddonId:number) { + const form = await this.captchaService.getCaptcha(captchaAddonId) + return this.ok(form); + } @Post('/sendSmsCode', { summary: Constants.per.guest }) public async sendSmsCode( @@ -64,8 +70,8 @@ export class BasicController extends BaseController { // opts.verificationCodeLength = 6; //部分厂商这里会设置参数长度这里就不改了 } - await this.codeService.checkCaptcha(body.randomStr, body.imgCode); - await this.codeService.sendSmsCode(body.phoneCode, body.mobile, body.randomStr, opts); + await this.codeService.checkCaptcha(body.captcha); + await this.codeService.sendSmsCode(body.phoneCode, body.mobile, opts); return this.ok(null); } @@ -88,16 +94,10 @@ export class BasicController extends BaseController { opts.verificationCodeLength = 6; } - await this.codeService.checkCaptcha(body.randomStr, body.imgCode); - await this.codeService.sendEmailCode(body.email, body.randomStr, opts); + await this.codeService.checkCaptcha(body.captcha); + await this.codeService.sendEmailCode(body.email, opts); // 设置缓存内容 return this.ok(null); } - @Get('/captcha', { summary: Constants.per.guest }) - public async getCaptcha(@Query('randomStr') randomStr: any) { - const captcha = await this.codeService.generateCaptcha(randomStr); - this.ctx.res.setHeader('Content-Type', 'image/svg+xml'); - return captcha.data; - } } diff --git a/packages/ui/certd-server/src/controller/sys/addon/addon-controller.ts b/packages/ui/certd-server/src/controller/sys/addon/addon-controller.ts new file mode 100644 index 000000000..bd5864237 --- /dev/null +++ b/packages/ui/certd-server/src/controller/sys/addon/addon-controller.ts @@ -0,0 +1,83 @@ +import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core"; +import { AddonRequestHandleReq, AddonService, Constants } from "@certd/lib-server"; +import { AddonController } from "../../user/addon/addon-controller.js"; + +@Provide() +@Controller('/api/sys/addon') +export class SysAddonController extends AddonController { + @Inject() + service2: AddonService; + + getService(): AddonService { + return this.service2; + } + + getUserId() { + // checkComm(); + return 0; + } + + @Post('/page', { summary: 'sys:settings:view' }) + async page(@Body(ALL) body: any) { + return await super.page(body); + } + + @Post('/list', { summary: 'sys:settings:view' }) + async list(@Body(ALL) body: any) { + return await super.list(body); + } + + @Post('/add', { summary: 'sys:settings:edit' }) + async add(@Body(ALL) bean: any) { + return await super.add(bean); + } + + @Post('/update', { summary: 'sys:settings:edit' }) + async update(@Body(ALL) bean: any) { + return await super.update(bean); + } + @Post('/info', { summary: 'sys:settings:view' }) + async info(@Query('id') id: number) { + return await super.info(id); + } + + @Post('/delete', { summary: 'sys:settings:edit' }) + async delete(@Query('id') id: number) { + return await super.delete(id); + } + @Post('/define', { summary: Constants.per.authOnly }) + async define(@Query('type') type: string,@Query('addonType') addonType: string) { + return await super.define(type,addonType); + } + + @Post('/getTypeDict', { summary: Constants.per.authOnly }) + async getTypeDict(@Query('addonType') addonType: string) { + return await super.getTypeDict(addonType); + } + + @Post('/simpleInfo', { summary: Constants.per.authOnly }) + async simpleInfo(@Query('addonType') addonType: string,@Query('id') id: number) { + return await super.simpleInfo(addonType,id); + } + + @Post('/getDefaultId', { summary: Constants.per.authOnly }) + async getDefaultId(@Query('addonType') addonType: string) { + return await super.getDefaultId(addonType); + } + + @Post('/setDefault', { summary: Constants.per.authOnly }) + async setDefault(@Query('addonType') addonType: string,@Query('id') id: number) { + return await super.setDefault(addonType,id); + } + + + @Post('/options', { summary: Constants.per.authOnly }) + async options(@Query('addonType') addonType: string) { + return await super.options(addonType); + } + + @Post('/handle', { summary: Constants.per.authOnly }) + async handle(@Body(ALL) body: AddonRequestHandleReq) { + return await super.handle(body); + } +} diff --git a/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts b/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts index ac9bb2521..587a4743e 100644 --- a/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts +++ b/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts @@ -1,4 +1,4 @@ -import {ALL, Body, Controller, Inject, Post, Provide, Query} from '@midwayjs/core'; +import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core"; import { CrudController, SysPrivateSettings, @@ -6,14 +6,14 @@ import { SysSafeSetting, SysSettingsEntity, SysSettingsService -} from '@certd/lib-server'; -import {cloneDeep, merge} from 'lodash-es'; -import {PipelineService} from '../../../modules/pipeline/service/pipeline-service.js'; -import {UserSettingsService} from '../../../modules/mine/service/user-settings-service.js'; -import {getEmailSettings} from '../../../modules/sys/settings/fix.js'; -import {http, logger, simpleNanoId, utils} from '@certd/basic'; -import {CodeService} from '../../../modules/basic/service/code-service.js'; -import {SmsServiceFactory} from '../../../modules/basic/sms/factory.js'; +} from "@certd/lib-server"; +import { cloneDeep, merge } from "lodash-es"; +import { PipelineService } from "../../../modules/pipeline/service/pipeline-service.js"; +import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js"; +import { getEmailSettings } from "../../../modules/sys/settings/fix.js"; +import { http, logger, utils } from "@certd/basic"; +import { CodeService } from "../../../modules/basic/service/code-service.js"; +import { SmsServiceFactory } from "../../../modules/basic/sms/factory.js"; /** @@ -158,7 +158,7 @@ export class SysSettingsController extends CrudController { @Post('/testSms', { summary: 'sys:settings:edit' }) async testSms(@Body(ALL) body) { - await this.codeService.sendSmsCode(body.phoneCode, body.mobile, simpleNanoId()); + await this.codeService.sendSmsCode(body.phoneCode, body.mobile ); return this.ok({}); } diff --git a/packages/ui/certd-server/src/controller/user/addon/addon-controller.ts b/packages/ui/certd-server/src/controller/user/addon/addon-controller.ts new file mode 100644 index 000000000..299f7f5f0 --- /dev/null +++ b/packages/ui/certd-server/src/controller/user/addon/addon-controller.ts @@ -0,0 +1,199 @@ +import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core"; +import { + AddonDefine, + AddonRequestHandleReq, + AddonService, + Constants, + CrudController, + newAddon, + ValidateException +} from "@certd/lib-server"; +import { AuthService } from "../../../modules/sys/authority/service/auth-service.js"; +import { checkPlus } from "@certd/plus-core"; +import { http, logger, utils } from "@certd/basic"; + +/** + * Addon + */ +@Provide() +@Controller('/api/addon') +export class AddonController extends CrudController { + @Inject() + service: AddonService; + @Inject() + authService: AuthService; + + getService(): AddonService { + return this.service; + } + + @Post('/page', { summary: Constants.per.authOnly }) + async page(@Body(ALL) body) { + body.query = body.query ?? {}; + delete body.query.userId; + const buildQuery = qb => { + qb.andWhere('user_id = :userId', { userId: this.getUserId() }); + }; + const res = await this.service.page({ + query: body.query, + page: body.page, + sort: body.sort, + buildQuery, + }); + return this.ok(res); + } + + @Post('/list', { summary: Constants.per.authOnly }) + async list(@Body(ALL) body) { + body.query = body.query ?? {}; + body.query.userId = this.getUserId(); + return super.list(body); + } + + @Post('/add', { summary: Constants.per.authOnly }) + async add(@Body(ALL) bean) { + bean.userId = this.getUserId(); + const type = bean.type; + const addonType = bean.addonType; + if (! type || !addonType){ + throw new ValidateException('请选择Addon类型'); + } + const define: AddonDefine = this.service.getDefineByType(type,addonType); + if (!define) { + throw new ValidateException('Addon类型不存在'); + } + if (define.needPlus) { + checkPlus(); + } + return super.add(bean); + } + + @Post('/update', { summary: Constants.per.authOnly }) + async update(@Body(ALL) bean) { + await this.service.checkUserId(bean.id, this.getUserId()); + const old = await this.service.info(bean.id); + if (!old) { + throw new ValidateException('Addon配置不存在'); + } + if (old.type !== bean.type ) { + const addonType = old.type; + const type = bean.type; + const define: AddonDefine = this.service.getDefineByType(type,addonType); + if (!define) { + throw new ValidateException('Addon类型不存在'); + } + if (define.needPlus) { + checkPlus(); + } + } + delete bean.userId; + return super.update(bean); + } + @Post('/info', { summary: Constants.per.authOnly }) + async info(@Query('id') id: number) { + await this.service.checkUserId(id, this.getUserId()); + return super.info(id); + } + + @Post('/delete', { summary: Constants.per.authOnly }) + async delete(@Query('id') id: number) { + await this.service.checkUserId(id, this.getUserId()); + return super.delete(id); + } + + @Post('/define', { summary: Constants.per.authOnly }) + async define(@Query('type') type: string,@Query('addonType') addonType: string) { + const notification = this.service.getDefineByType(type,addonType); + return this.ok(notification); + } + + @Post('/getTypeDict', { summary: Constants.per.authOnly }) + async getTypeDict(@Query('addonType') addonType: string) { + const list: any = this.service.getDefineList(addonType); + let dict = []; + for (const item of list) { + dict.push({ + value: item.name, + label: item.title, + needPlus: item.needPlus ?? false, + icon: item.icon, + }); + } + dict = dict.sort(a => { + return a.needPlus ? 0 : -1; + }); + return this.ok(dict); + } + + @Post('/simpleInfo', { summary: Constants.per.authOnly }) + async simpleInfo(@Query('addonType') addonType: string,@Query('id') id: number) { + if (id === 0) { + //获取默认 + const res = await this.service.getDefault(this.getUserId(),addonType); + if (!res) { + throw new ValidateException('默认Addon配置不存在'); + } + const simple = await this.service.getSimpleInfo(res.id); + return this.ok(simple); + } + await this.authService.checkEntityUserId(this.ctx, this.service, id); + const res = await this.service.getSimpleInfo(id); + return this.ok(res); + } + + @Post('/getDefaultId', { summary: Constants.per.authOnly }) + async getDefaultId(@Query('addonType') addonType: string) { + const res = await this.service.getDefault(this.getUserId(),addonType); + return this.ok(res?.id); + } + + @Post('/setDefault', { summary: Constants.per.authOnly }) + async setDefault(@Query('addonType') addonType: string,@Query('id') id: number) { + await this.service.checkUserId(id, this.getUserId()); + const res = await this.service.setDefault(id, this.getUserId(),addonType); + return this.ok(res); + } + + + @Post('/options', { summary: Constants.per.authOnly }) + async options(@Query('addonType') addonType: string) { + const res = await this.service.list({ + query: { + userId: this.getUserId(), + addonType + }, + }); + for (const item of res) { + delete item.setting; + } + return this.ok(res); + } + + + @Post('/handle', { summary: Constants.per.authOnly }) + async handle(@Body(ALL) body: AddonRequestHandleReq) { + const userId = this.getUserId(); + let inputAddon = body.input.addon; + if (body.input.id > 0) { + const oldEntity = await this.service.info(body.input.id); + if (oldEntity) { + if (oldEntity.userId !== userId) { + throw new Error('addon not found'); + } + // const param: any = { + // type: body.typeName, + // setting: JSON.stringify(body.input.access), + // }; + inputAddon = JSON.parse( oldEntity.setting) + } + } + const ctx = { + http: http, + logger:logger, + utils:utils, + } + const addon = await newAddon(body.addonType,body.typeName, inputAddon,ctx); + const res = await addon.onRequest(body); + return this.ok(res); + } +} diff --git a/packages/ui/certd-server/src/controller/user/login/forgot-password-controller.ts b/packages/ui/certd-server/src/controller/user/login/forgot-password-controller.ts index 4c60d394c..2e496cf9e 100644 --- a/packages/ui/certd-server/src/controller/user/login/forgot-password-controller.ts +++ b/packages/ui/certd-server/src/controller/user/login/forgot-password-controller.ts @@ -29,25 +29,23 @@ export class LoginController extends BaseController { throw new CommonException('暂未开启自助找回'); } // 找回密码的验证码允许错误次数 - const errorNum = 5; + const maxErrorCount = 5; if(body.type === 'email') { this.codeService.checkEmailCode({ verificationType: 'forgotPassword', email: body.input, - randomStr: body.randomStr, validateCode: body.validateCode, - errorNum, + maxErrorCount: maxErrorCount, throwError: true, }); } else if(body.type === 'mobile') { await this.codeService.checkSmsCode({ verificationType: 'forgotPassword', mobile: body.input, - randomStr: body.randomStr, phoneCode: body.phoneCode, smsCode: body.validateCode, - errorNum, + maxErrorCount: maxErrorCount, throwError: true, }); } else { diff --git a/packages/ui/certd-server/src/controller/user/login/login-controller.ts b/packages/ui/certd-server/src/controller/user/login/login-controller.ts index 2877c257c..7a863769b 100644 --- a/packages/ui/certd-server/src/controller/user/login/login-controller.ts +++ b/packages/ui/certd-server/src/controller/user/login/login-controller.ts @@ -1,8 +1,9 @@ -import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core'; -import { LoginService } from '../../../modules/login/service/login-service.js'; -import { BaseController, Constants, SysPublicSettings, SysSettingsService } from '@certd/lib-server'; -import { CodeService } from '../../../modules/basic/service/code-service.js'; -import { checkComm } from '@certd/plus-core'; +import { ALL, Body, Controller, Inject, Post, Provide } from "@midwayjs/core"; +import { LoginService } from "../../../modules/login/service/login-service.js"; +import { AddonService, BaseController, Constants, SysPublicSettings, SysSettingsService } from "@certd/lib-server"; +import { CodeService } from "../../../modules/basic/service/code-service.js"; +import { checkComm } from "@certd/plus-core"; +import { CaptchaService } from "../../../modules/basic/service/captcha-service.js"; /** */ @@ -16,13 +17,22 @@ export class LoginController extends BaseController { @Inject() sysSettingsService: SysSettingsService; + @Inject() + addonService: AddonService; + + @Inject() + captchaService: CaptchaService; @Post('/login', { summary: Constants.per.guest }) public async login( @Body(ALL) - user: any + body: any ) { - const token = await this.loginService.loginByPassword(user); + const settings = await this.sysSettingsService.getPublicSettings() + if (settings.captchaEnabled === true) { + await this.captchaService.doValidate({form:body.captcha,must:false,captchaAddonId:settings.captchaAddonId}) + } + const token = await this.loginService.loginByPassword(body); this.writeTokenCookie(token); return this.ok(token); } diff --git a/packages/ui/certd-server/src/controller/user/login/register-controller.ts b/packages/ui/certd-server/src/controller/user/login/register-controller.ts index 579bcef42..f31f0ef7f 100644 --- a/packages/ui/certd-server/src/controller/user/login/register-controller.ts +++ b/packages/ui/certd-server/src/controller/user/login/register-controller.ts @@ -13,8 +13,7 @@ export type RegisterReq = { phoneCode?: string; validateCode: string; - imgCode: string; - randomStr: string; + captcha:any; }; /** @@ -52,7 +51,7 @@ export class RegisterController extends BaseController { throw new Error('用户名不能为空'); } - await this.codeService.checkCaptcha(body.randomStr, body.imgCode); + await this.codeService.checkCaptcha(body.captcha); const newUser = await this.userService.register(body.type, { username: body.username, password: body.password, @@ -68,7 +67,6 @@ export class RegisterController extends BaseController { mobile: body.mobile, phoneCode: body.phoneCode, smsCode: body.validateCode, - randomStr: body.randomStr, throwError: true, }); const newUser = await this.userService.register(body.type, { @@ -85,7 +83,6 @@ export class RegisterController extends BaseController { checkPlus(); this.codeService.checkEmailCode({ email: body.email, - randomStr: body.randomStr, validateCode: body.validateCode, throwError: true, }); diff --git a/packages/ui/certd-server/src/modules/basic/service/captcha-service.ts b/packages/ui/certd-server/src/modules/basic/service/captcha-service.ts new file mode 100644 index 000000000..e5b6b951e --- /dev/null +++ b/packages/ui/certd-server/src/modules/basic/service/captcha-service.ts @@ -0,0 +1,54 @@ +import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; +import { AddonService, SysSettingsService } from "@certd/lib-server"; +import { logger } from "@certd/basic"; +import { ICaptchaAddon } from "../../../plugins/plugin-captcha/api.js"; + +@Provide() +@Scope(ScopeEnum.Request, { allowDowngrade: true }) +export class CaptchaService { + @Inject() + sysSettingsService: SysSettingsService; + @Inject() + addonService: AddonService; + + + async getCaptcha(captchaAddonId?:number){ + if (!captchaAddonId) { + const settings = await this.sysSettingsService.getPublicSettings() + captchaAddonId = settings.captchaAddonId ?? 0 + } + const addon:ICaptchaAddon = await this.addonService.getAddonById(captchaAddonId,true,0) + if (!addon) { + throw new Error('验证码插件还未配置') + } + return await addon.getCaptcha() + } + + + async doValidate(opts:{form:any,must?:boolean,captchaAddonId?:number}){ + if (!opts.captchaAddonId) { + const settings = await this.sysSettingsService.getPublicSettings() + opts.captchaAddonId = settings.captchaAddonId ?? 0 + } + const addon = await this.addonService.getById(opts.captchaAddonId,0) + if (!addon) { + if (opts.must) { + throw new Error('请先配置验证码插件'); + } + logger.warn('验证码插件还未配置,忽略验证码校验') + return true + } + + if (!opts.form) { + throw new Error('请输入验证码'); + } + const res = await addon.onValidate(opts.form) + if (!res) { + throw new Error('验证码错误'); + } + + return true + + } + +} diff --git a/packages/ui/certd-server/src/modules/basic/service/code-service.ts b/packages/ui/certd-server/src/modules/basic/service/code-service.ts index 1a252efc7..2cfd1843c 100644 --- a/packages/ui/certd-server/src/modules/basic/service/code-service.ts +++ b/packages/ui/certd-server/src/modules/basic/service/code-service.ts @@ -8,6 +8,7 @@ import { EmailService } from './email-service.js'; import { AccessService } from '@certd/lib-server'; import { AccessSysGetter } from '@certd/lib-server'; import { isComm } from '@certd/plus-core'; +import { CaptchaService } from "./captcha-service.js"; // {data: '', text: 'abcd'} /** @@ -23,44 +24,19 @@ export class CodeService { @Inject() accessService: AccessService; - /** - */ - async generateCaptcha(randomStr) { - const svgCaptcha = await import('svg-captcha'); - const c = svgCaptcha.create(); - //{data: '', text: 'abcd'} - const imgCode = c.text; // = RandomUtil.randomStr(4, true); - cache.set('imgCode:' + randomStr, imgCode, { - ttl: 2 * 60 * 1000, //过期时间 2分钟 - }); - return c; - } + @Inject() + captchaService: CaptchaService; - async getCaptchaText(randomStr) { - return cache.get('imgCode:' + randomStr); - } - async removeCaptcha(randomStr) { - cache.delete('imgCode:' + randomStr); - } - async checkCaptcha(randomStr: string, userCaptcha: string) { - const code = await this.getCaptchaText(randomStr); - if (code == null) { - throw new Error('验证码已过期'); - } - if (code.toLowerCase() !== userCaptcha.toLowerCase()) { - throw new Error('验证码不正确'); - } - await this.removeCaptcha(randomStr); - return true; + async checkCaptcha(body:any) { + return await this.captchaService.doValidate({form:body}) } /** */ async sendSmsCode( phoneCode = '86', mobile: string, - randomStr: string, opts?: { duration?: number, verificationType?: string, @@ -70,9 +46,6 @@ export class CodeService { if (!mobile) { throw new Error('手机号不能为空'); } - if (!randomStr) { - throw new Error('randomStr不能为空'); - } const verificationCodeLength = Math.floor(Math.max(Math.min(opts?.verificationCodeLength || 4, 8), 4)); const duration = Math.floor(Math.max(Math.min(opts?.duration || 5, 15), 1)); @@ -96,7 +69,7 @@ export class CodeService { phoneCode, }); - const key = this.buildSmsCodeKey(phoneCode, mobile, randomStr, opts?.verificationType); + const key = this.buildSmsCodeKey(phoneCode, mobile, opts?.verificationType); cache.set(key, smsCode, { ttl: duration * 60 * 1000, //5分钟 }); @@ -106,12 +79,10 @@ export class CodeService { /** * * @param email 收件邮箱 - * @param randomStr * @param opts title标题 content内容模版 duration有效时间单位分钟 verificationType验证类型 */ async sendEmailCode( email: string, - randomStr: string, opts?: { title?: string, content?: string, @@ -123,9 +94,7 @@ export class CodeService { if (!email) { throw new Error('Email不能为空'); } - if (!randomStr) { - throw new Error('randomStr不能为空'); - } + let siteTitle = 'Certd'; if (isComm()) { @@ -149,7 +118,7 @@ export class CodeService { receivers: [email], }); - const key = this.buildEmailCodeKey(email, randomStr, opts?.verificationType); + const key = this.buildEmailCodeKey(email,opts?.verificationType); cache.set(key, code, { ttl: duration * 60 * 1000, //5分钟 }); @@ -159,31 +128,32 @@ export class CodeService { /** * checkSms */ - async checkSmsCode(opts: { mobile: string; phoneCode: string; smsCode: string; randomStr: string; verificationType?: string; throwError: boolean; errorNum?: number }) { - const key = this.buildSmsCodeKey(opts.phoneCode, opts.mobile, opts.randomStr, opts.verificationType); - if (isDev()) { + async checkSmsCode(opts: { mobile: string; phoneCode: string; smsCode: string; verificationType?: string; throwError: boolean; maxErrorCount?: number }) { + const key = this.buildSmsCodeKey(opts.phoneCode, opts.mobile, opts.verificationType); + return this.checkValidateCode("sms",key, opts.smsCode, opts.throwError, opts.maxErrorCount); + + } + + buildSmsCodeKey(phoneCode: string, mobile: string, verificationType?: string) { + return ['sms', verificationType, phoneCode, mobile].filter(item => !!item).join(':'); + } + + buildEmailCodeKey(email: string, verificationType?: string) { + return ['email', verificationType, email].filter(item => !!item).join(':'); + } + checkValidateCode(type:string,key: string, userCode: string, throwError = true, maxErrorCount = 3) { + // 记录异常次数key + if (isDev() && userCode==="1234567") { return true; } - return this.checkValidateCode(key, opts.smsCode, opts.throwError, opts.errorNum); - } - - buildSmsCodeKey(phoneCode: string, mobile: string, randomStr: string, verificationType?: string) { - return ['sms', verificationType, phoneCode, mobile, randomStr].filter(item => !!item).join(':'); - } - - buildEmailCodeKey(email: string, randomStr: string, verificationType?: string) { - return ['email', verificationType, email, randomStr].filter(item => !!item).join(':'); - } - checkValidateCode(key: string, userCode: string, throwError = true, errorNum = 3) { - // 记录异常次数key const err_num_key = key + ':err_num'; - //验证图片验证码 + //验证邮件验证码 const code = cache.get(key); if (code == null || code !== userCode) { let maxRetryCount = false; - if (!!code && errorNum > 0) { + if (!!code && maxErrorCount > 0) { const err_num = cache.get(err_num_key) || 0 - if(err_num >= errorNum - 1) { + if(err_num >= maxErrorCount - 1) { maxRetryCount = true; cache.delete(key); cache.delete(err_num_key); @@ -194,7 +164,8 @@ export class CodeService { } } if (throwError) { - throw new CodeErrorException(!maxRetryCount ? '验证码错误': '验证码错误请获取新的验证码'); + const label = type ==='sms' ? '手机' : '邮箱'; + throw new CodeErrorException(!maxRetryCount ? `${label}验证码错误`: `${label}验证码错误请获取新的验证码`); } return false; } @@ -203,9 +174,9 @@ export class CodeService { return true; } - checkEmailCode(opts: { randomStr: string; validateCode: string; email: string; verificationType?: string; throwError: boolean; errorNum?: number }) { - const key = this.buildEmailCodeKey(opts.email, opts.randomStr, opts.verificationType); - return this.checkValidateCode(key, opts.validateCode, opts.throwError, opts.errorNum); + checkEmailCode(opts: { validateCode: string; email: string; verificationType?: string; throwError: boolean; maxErrorCount?: number }) { + const key = this.buildEmailCodeKey(opts.email, opts.verificationType); + return this.checkValidateCode('email',key, opts.validateCode, opts.throwError, opts.maxErrorCount); } compile(templateString: string) { diff --git a/packages/ui/certd-server/src/modules/login/service/login-service.ts b/packages/ui/certd-server/src/modules/login/service/login-service.ts index ed7cc39b9..681d3b566 100644 --- a/packages/ui/certd-server/src/modules/login/service/login-service.ts +++ b/packages/ui/certd-server/src/modules/login/service/login-service.ts @@ -1,17 +1,22 @@ -import {Config, Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core'; -import {UserService} from '../../sys/authority/service/user-service.js'; -import jwt from 'jsonwebtoken'; -import {AuthException, CommonException, Need2FAException} from "@certd/lib-server"; -import {RoleService} from '../../sys/authority/service/role-service.js'; -import {UserEntity} from '../../sys/authority/entity/user.js'; -import {SysSettingsService} from '@certd/lib-server'; -import {SysPrivateSettings} from '@certd/lib-server'; -import {cache, utils} from '@certd/basic'; -import {LoginErrorException} from '@certd/lib-server/dist/basic/exception/login-error-exception.js'; -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 { Config, Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core"; +import { UserService } from "../../sys/authority/service/user-service.js"; +import jwt from "jsonwebtoken"; +import { + AuthException, + CommonException, + Need2FAException, + SysPrivateSettings, + SysSettingsService +} from "@certd/lib-server"; +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 { 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"; /** * 系统用户 @@ -35,6 +40,8 @@ export class LoginService { userSettingsService: UserSettingsService; @Inject() twoFactorService: TwoFactorService; + @Inject() + addonService: AddonService; checkIsBlocked(username: string) { const blockDurationKey = `login_block_duration:${username}`; @@ -106,13 +113,12 @@ export class LoginService { mobile: req.mobile, phoneCode: req.phoneCode, smsCode: req.smsCode, - randomStr: req.randomStr, throwError: false, }); const {mobile, phoneCode} = req; if (!smsChecked) { - this.addErrorTimes(mobile, '验证码错误'); + this.addErrorTimes(mobile, '手机验证码错误'); } let info = await this.userService.findOne({phoneCode, mobile: mobile}); if (info == null) { diff --git a/packages/ui/certd-server/src/modules/monitor/entity/cert-info.ts b/packages/ui/certd-server/src/modules/monitor/entity/cert-info.ts index f16ca87dc..807489c91 100644 --- a/packages/ui/certd-server/src/modules/monitor/entity/cert-info.ts +++ b/packages/ui/certd-server/src/modules/monitor/entity/cert-info.ts @@ -30,6 +30,9 @@ export class CertInfoEntity { @Column({ name: 'cert_provider', comment: '证书颁发机构' }) certProvider: string; + @Column({ name: 'effective_time', comment: '生效时间' }) + effectiveTime: number; + @Column({ name: 'expires_time', comment: '过期时间' }) expiresTime: number; diff --git a/packages/ui/certd-server/src/modules/monitor/entity/site-info.ts b/packages/ui/certd-server/src/modules/monitor/entity/site-info.ts index 1c1e4eda7..f4dc95016 100644 --- a/packages/ui/certd-server/src/modules/monitor/entity/site-info.ts +++ b/packages/ui/certd-server/src/modules/monitor/entity/site-info.ts @@ -26,6 +26,8 @@ export class SiteInfoEntity { @Column({ name: 'cert_provider', comment: '证书颁发机构', length: 100 }) certProvider: string; + @Column({ name: 'cert_effective_time', comment: '证书生效时间' }) + certEffectiveTime: number; @Column({ name: 'cert_expires_time', comment: '证书到期时间' }) certExpiresTime: number; @Column({ name: 'last_check_time', comment: '上次检查时间' }) diff --git a/packages/ui/certd-server/src/modules/monitor/service/cert-info-service.ts b/packages/ui/certd-server/src/modules/monitor/service/cert-info-service.ts index 8bb2e8ac6..4819bda92 100644 --- a/packages/ui/certd-server/src/modules/monitor/service/cert-info-service.ts +++ b/packages/ui/certd-server/src/modules/monitor/service/cert-info-service.ts @@ -164,6 +164,7 @@ export class CertInfoService extends BaseService { bean.domains = domains.join(','); bean.domain = domains[0]; bean.domainCount = domains.length; + bean.effectiveTime = certReader.effective; bean.expiresTime = certReader.expires; bean.certProvider = certReader.detail.issuer.commonName; bean.userId = userId diff --git a/packages/ui/certd-server/src/modules/monitor/service/site-info-service.ts b/packages/ui/certd-server/src/modules/monitor/service/site-info-service.ts index 4f9f7f9f3..8c21d9d45 100644 --- a/packages/ui/certd-server/src/modules/monitor/service/site-info-service.ts +++ b/packages/ui/certd-server/src/modules/monitor/service/site-info-service.ts @@ -134,6 +134,7 @@ export class SiteInfoService extends BaseService { if (!certi) { throw new Error("没有发现证书"); } + const effective = certi.valid_from; const expires = certi.valid_to; const allDomains = certi.subjectaltname?.replaceAll("DNS:", "").split(",") || []; const mainDomain = certi.subject?.CN; @@ -149,12 +150,13 @@ export class SiteInfoService extends BaseService { certDomains: domains.join(","), certStatus: status, certProvider: issuer, + certEffectiveTime: dayjs(effective).valueOf(), certExpiresTime: dayjs(expires).valueOf(), lastCheckTime: dayjs().valueOf(), error: null, checkStatus: "ok" }; - logger.info(`测试站点成功:id=${updateData.id},site=${site.name},expiresTime=${updateData.certExpiresTime}`) + logger.info(`测试站点成功:id=${updateData.id},site=${site.name},certEffectiveTime=${updateData.certEffectiveTime},expiresTime=${updateData.certExpiresTime}`) if (site.ipCheck) { delete updateData.checkStatus } diff --git a/packages/ui/certd-server/src/modules/monitor/service/site-tester.ts b/packages/ui/certd-server/src/modules/monitor/service/site-tester.ts index e2b997310..f7e57b1b8 100644 --- a/packages/ui/certd-server/src/modules/monitor/service/site-tester.ts +++ b/packages/ui/certd-server/src/modules/monitor/service/site-tester.ts @@ -80,7 +80,11 @@ export class SiteTester { } } - options.agent = new https.Agent({ keepAlive: false, lookup: customLookup }); + const agentOptions:any = { keepAlive: false }; + if (customLookup) { + agentOptions.lookup = customLookup + } + options.agent = new https.Agent(agentOptions); // 创建 HTTPS 请求 const requestPromise = safePromise((resolve, reject) => { diff --git a/packages/ui/certd-server/src/modules/sys/authority/service/user-service.ts b/packages/ui/certd-server/src/modules/sys/authority/service/user-service.ts index c0d4358ed..de913194f 100644 --- a/packages/ui/certd-server/src/modules/sys/authority/service/user-service.ts +++ b/packages/ui/certd-server/src/modules/sys/authority/service/user-service.ts @@ -238,9 +238,12 @@ export class UserService extends BaseService { async forgotPassword( data: { - type: ForgotPasswordType; input?: string, phoneCode?: string, - randomStr: string, imgCode:string, validateCode: string, - password: string, confirmPassword: string, + type: ForgotPasswordType; + input?: string, + phoneCode?: string, + validateCode: string, + password: string, + confirmPassword: string, } ) { if(!data.type) { @@ -249,7 +252,13 @@ export class UserService extends BaseService { if(data.password !== data.confirmPassword) { throw new CommonException('两次输入的密码不一致'); } - const user = await this.findOne([{ [data.type]: data.input }]); + const where :any= { + [data.type]: data.input, + }; + if (data.type === 'mobile' ) { + where.phoneCode = data.phoneCode ?? '86'; + } + const user = await this.findOne({ [data.type]: data.input }); console.log('user', user) if(!user) { throw new CommonException('用户不存在'); diff --git a/packages/ui/certd-server/src/plugins/index.ts b/packages/ui/certd-server/src/plugins/index.ts index 92da08bca..85e4d93a3 100644 --- a/packages/ui/certd-server/src/plugins/index.ts +++ b/packages/ui/certd-server/src/plugins/index.ts @@ -35,3 +35,4 @@ export * from './plugin-ksyun/index.js' export * from './plugin-apisix/index.js' export * from './plugin-dokploy/index.js' export * from './plugin-godaddy/index.js' +export * from './plugin-captcha/index.js' diff --git a/packages/ui/certd-server/src/plugins/plugin-captcha/api.ts b/packages/ui/certd-server/src/plugins/plugin-captcha/api.ts new file mode 100644 index 000000000..cee513a66 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-captcha/api.ts @@ -0,0 +1,4 @@ +export interface ICaptchaAddon{ + onValidate(data?:any):Promise; + getCaptcha():Promise; +} diff --git a/packages/ui/certd-server/src/plugins/plugin-captcha/geetest/index.ts b/packages/ui/certd-server/src/plugins/plugin-captcha/geetest/index.ts new file mode 100644 index 000000000..a5272988a --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-captcha/geetest/index.ts @@ -0,0 +1,122 @@ +import { AddonInput, BaseAddon, IsAddon } from "@certd/lib-server"; +import crypto from "crypto"; +import { ICaptchaAddon } from "../api.js"; + +@IsAddon({ + addonType:"captcha", + name: 'geetest', + title: '极验验证码v4', + desc: '', + showTest:false, +}) +export class GeeTestCaptcha extends BaseAddon implements ICaptchaAddon{ + + @AddonInput({ + title: '验证ID', + component: { + placeholder: 'captchaId', + }, + helper:"[极验验证码v4](https://console.geetest.com/sensbot/management) -> 创建业务模块 -> 新增业务场景", + required: true, + }) + captchaId = ''; + + @AddonInput({ + title: '验证Key', + component: { + placeholder: 'captchaKey', + }, + required: true, + }) + captchaKey = ''; + + + async onValidate(data?:any) { + if (!data) { + return false + } + // geetest 服务地址 +// geetest server url + const API_SERVER = "http://gcaptcha4.geetest.com"; + +// geetest 验证接口 +// geetest server interface + const API_URL = API_SERVER + "/validate" + "?captcha_id=" + this.captchaId; + + + // 前端参数 + // web parameter + var lot_number = data['lot_number']; + var captcha_output = data['captcha_output']; + var pass_token = data['pass_token']; + var gen_time = data['gen_time']; + if (!lot_number || !captcha_output || !pass_token || !gen_time) { + return false; + } + + // 生成签名, 使用标准的hmac算法,使用用户当前完成验证的流水号lot_number作为原始消息message,使用客户验证私钥作为key + // 采用sha256散列算法将message和key进行单向散列生成最终的 “sign_token” 签名 + // use lot_number + CAPTCHA_KEY, generate the signature + var sign_token = this.hmac_sha256_encode(lot_number, this.captchaKey); + + // 向极验转发前端数据 + “sign_token” 签名 + // send web parameter and “sign_token” to geetest server + var datas = { + 'lot_number': lot_number, + 'captcha_output': captcha_output, + 'pass_token': pass_token, + 'gen_time': gen_time, + 'sign_token': sign_token + }; + + // post request + // 根据极验返回的用户验证状态, 网站主进行自己的业务逻辑 + // According to the user authentication status returned by the geetest, the website owner carries out his own business logic + try{ + const res = await this.doRequest(datas, API_URL) + if (res.result == "success") { + // 验证成功 + // verification successful + return true; + } else { + // 验证失败 + // verification failed + this.logger.error("极验验证不通过 ",res.reason) + return false; + } + }catch (e) { + this.ctx.logger.error("极验验证服务异常",e) + return true + } + } + + // 生成签名 +// Generate signature + hmac_sha256_encode(value, key){ + var hash = crypto.createHmac("sha256", key) + .update(value, 'utf8') + .digest('hex'); + return hash; + } + + +// 发送post请求, 响应json数据如:{"result": "success", "reason": "", "captcha_args": {}} +// Send a post request and respond to JSON data, such as: {result ":" success "," reason ":" "," captcha_args ": {}} + async doRequest(datas, url){ + var options = { + url: url, + method: "POST", + params: datas, + timeout: 5000 + }; + const result = await this.ctx.http.request(options); + return result; + } + + async getCaptcha(): Promise { + return { + captchaId: this.captchaId, + } + } + +} diff --git a/packages/ui/certd-server/src/plugins/plugin-captcha/image/index.ts b/packages/ui/certd-server/src/plugins/plugin-captcha/image/index.ts new file mode 100644 index 000000000..ff3b79bd9 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-captcha/image/index.ts @@ -0,0 +1,56 @@ +import { BaseAddon, IsAddon } from "@certd/lib-server"; +import { ICaptchaAddon } from "../api.js"; +import { cache } from "@certd/basic"; +import { nanoid } from "nanoid"; + +@IsAddon({ + addonType:"captcha", + name: 'image', + title: '图片验证码', + desc: '', + showTest:false, +}) +export class ImageCaptcha extends BaseAddon implements ICaptchaAddon{ + + async onValidate(data?:any) { + if (!data) { + return false; + } + return await this.checkCaptcha(data.randomStr, data.imageCode) + } + + async getCaptchaText(randomStr:string) { + return cache.get('imgCode:' + randomStr); + } + + async removeCaptcha(randomStr:string) { + cache.delete('imgCode:' + randomStr); + } + + async checkCaptcha(randomStr: string, userCaptcha: string) { + const code = await this.getCaptchaText(randomStr); + if (code == null) { + throw new Error('验证码已过期'); + } + if (code.toLowerCase() !== userCaptcha?.toLowerCase()) { + throw new Error('验证码不正确'); + } + await this.removeCaptcha(randomStr); + return true; + } + + async getCaptcha(): Promise { + const svgCaptcha = await import('svg-captcha'); + const c = svgCaptcha.create(); + //{data: '', text: 'abcd'} + const imgCode = c.text; // = RandomUtil.randomStr(4, true); + const randomStr = nanoid(10) + cache.set('imgCode:' + randomStr, imgCode, { + ttl: 2 * 60 * 1000, //过期时间 2分钟 + }) + return { + randomStr: randomStr, + imageData: c.data, + } + } +} diff --git a/packages/ui/certd-server/src/plugins/plugin-captcha/index.ts b/packages/ui/certd-server/src/plugins/plugin-captcha/index.ts new file mode 100644 index 000000000..8b32cdb84 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-captcha/index.ts @@ -0,0 +1,2 @@ +export * from './geetest/index.js'; +export * from './image/index.js'; diff --git a/packages/ui/certd-server/src/plugins/plugin-upyun/client.ts b/packages/ui/certd-server/src/plugins/plugin-upyun/client.ts index 05c9c4e53..262d247d1 100644 --- a/packages/ui/certd-server/src/plugins/plugin-upyun/client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-upyun/client.ts @@ -65,7 +65,7 @@ export class UpyunClient { Cookie: req.cookie } }); - if (res.msg.errors.length > 0) { + if (res.msg?.errors?.length > 0) { throw new Error(JSON.stringify(res.msg)); } if(res.data?.error_code){ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1735c82cc..0f97fda6e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,7 +49,7 @@ importers: packages/core/acme-client: dependencies: '@certd/basic': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../basic '@peculiar/x509': specifier: ^1.11.0 @@ -210,10 +210,10 @@ importers: packages/core/pipeline: dependencies: '@certd/basic': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../basic '@certd/plus-core': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../pro/plus-core dayjs: specifier: ^1.11.7 @@ -418,7 +418,7 @@ importers: packages/libs/lib-k8s: dependencies: '@certd/basic': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/basic '@kubernetes/client-node': specifier: 0.21.0 @@ -458,16 +458,16 @@ importers: packages/libs/lib-server: dependencies: '@certd/acme-client': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/acme-client '@certd/basic': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/basic '@certd/pipeline': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/pipeline '@certd/plus-core': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../pro/plus-core '@midwayjs/cache': specifier: ~3.14.0 @@ -610,16 +610,16 @@ importers: packages/plugins/plugin-cert: dependencies: '@certd/acme-client': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/acme-client '@certd/basic': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/basic '@certd/pipeline': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/pipeline '@certd/plugin-lib': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../plugin-lib '@google-cloud/publicca': specifier: ^1.3.0 @@ -701,10 +701,10 @@ importers: specifier: ^3.787.0 version: 3.810.0(aws-crt@1.26.2) '@certd/basic': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/basic '@certd/pipeline': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/pipeline '@kubernetes/client-node': specifier: 0.21.0 @@ -792,19 +792,19 @@ importers: packages/pro/commercial-core: dependencies: '@certd/basic': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/basic '@certd/lib-server': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../libs/lib-server '@certd/pipeline': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/pipeline '@certd/plugin-plus': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../plugin-plus '@certd/plus-core': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../plus-core '@midwayjs/core': specifier: ~3.20.3 @@ -889,22 +889,22 @@ importers: specifier: ^1.0.2 version: 1.0.3 '@certd/basic': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/basic '@certd/lib-k8s': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../libs/lib-k8s '@certd/pipeline': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/pipeline '@certd/plugin-cert': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../plugins/plugin-cert '@certd/plugin-lib': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../plugins/plugin-lib '@certd/plus-core': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../plus-core ali-oss: specifier: ^6.21.0 @@ -1007,7 +1007,7 @@ importers: packages/pro/plus-core: dependencies: '@certd/basic': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/basic dayjs: specifier: ^1.11.7 @@ -1089,17 +1089,17 @@ importers: specifier: ^4.1.0 version: 4.1.0 '@fast-crud/fast-crud': - specifier: ^1.25.13 - version: 1.25.13(vue@3.5.14(typescript@5.8.3)) + specifier: ^1.26.6 + version: 1.26.6(vue@3.5.14(typescript@5.8.3)) '@fast-crud/fast-extends': - specifier: ^1.25.13 - version: 1.25.13(aws-crt@1.26.2)(vue@3.5.14(typescript@5.8.3)) + specifier: ^1.26.6 + version: 1.26.6(aws-crt@1.26.2)(vue@3.5.14(typescript@5.8.3)) '@fast-crud/ui-antdv4': - specifier: ^1.25.13 - version: 1.25.13 + specifier: ^1.26.6 + version: 1.26.6 '@fast-crud/ui-interface': - specifier: ^1.25.13 - version: 1.25.13 + specifier: ^1.26.6 + version: 1.26.6 '@iconify/tailwind': specifier: ^1.2.0 version: 1.2.0 @@ -1297,10 +1297,10 @@ importers: version: 0.1.3(zod@3.24.4) devDependencies: '@certd/lib-iframe': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../libs/lib-iframe '@certd/pipeline': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/pipeline '@rollup/plugin-commonjs': specifier: ^25.0.7 @@ -1483,46 +1483,46 @@ importers: specifier: ^3.705.0 version: 3.810.0(aws-crt@1.26.2) '@certd/acme-client': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/acme-client '@certd/basic': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/basic '@certd/commercial-core': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../pro/commercial-core '@certd/cv4pve-api-javascript': specifier: ^8.4.2 version: 8.4.2 '@certd/jdcloud': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../libs/lib-jdcloud '@certd/lib-huawei': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../libs/lib-huawei '@certd/lib-k8s': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../libs/lib-k8s '@certd/lib-server': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../libs/lib-server '@certd/midway-flyway-js': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../libs/midway-flyway-js '@certd/pipeline': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../core/pipeline '@certd/plugin-cert': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../plugins/plugin-cert '@certd/plugin-lib': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../plugins/plugin-lib '@certd/plugin-plus': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../pro/plugin-plus '@certd/plus-core': - specifier: ^1.36.18 + specifier: ^1.36.19 version: link:../../pro/plus-core '@huaweicloud/huaweicloud-sdk-cdn': specifier: ^3.1.120 @@ -3408,17 +3408,17 @@ packages: resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@fast-crud/fast-crud@1.25.13': - resolution: {integrity: sha512-2UDp0Wzsf9CXA9qJnyYjHYOR2usBRPij/8/15ksyFhBPLC8JJgXHFUae0lXqhib6QCV0CYrF8t7REEtxKmkUfw==} + '@fast-crud/fast-crud@1.26.6': + resolution: {integrity: sha512-m44F0qPUaswEBAn+bUqpjNm/+zn/icsTEpr4rGnDd692eZKzx7uPP1szVFwCc0nZeVteI5hNNAioIKB/6Xns1w==} - '@fast-crud/fast-extends@1.25.13': - resolution: {integrity: sha512-Hn4p/sdO96KB/+umfrZhgPM3ZbaU78uMamjl+UKhyEB5U42jN45XSEsP4ax1IHFCpvxLvTMnmTzRHqkbgTS18g==} + '@fast-crud/fast-extends@1.26.6': + resolution: {integrity: sha512-OHWxN7ZWmE0I0rGyqLjgx9RfZxHakWO68cAX5b6ADwFfFlGR2zfQuj6VjfYayPe6VSt5p3ZoUSsxQgmLbHYnKA==} - '@fast-crud/ui-antdv4@1.25.13': - resolution: {integrity: sha512-3BDfZRGH9rBEYKw6hUyXD1kI9XtUFl9U/dX4PDH98i1tMg3GhUJ51gDZjZ8as9HsMrCE88m8ZUPjjQU/HLVY1w==} + '@fast-crud/ui-antdv4@1.26.6': + resolution: {integrity: sha512-VeTt75ejjhIzii5wcQFWY5ugEizLGZhU2cowLNTGskCBmMEAP3GGUwn4TP33dtmQWvTu7FMjwaKtyWnFaiYmuA==} - '@fast-crud/ui-interface@1.25.13': - resolution: {integrity: sha512-hWjN6j6H2e9YxtqKL+fpls4/TTLZX6TuazjuxE+VoH0EdJF9QRk/D8GLxORGvnRgPABClbQvVPyofb6rPHjbAw==} + '@fast-crud/ui-interface@1.26.6': + resolution: {integrity: sha512-C6lWTWs8Vl76qQjORGbPmOy53clo3rO5KnVX2FhTcu5TTecR9JOc85IcQPSWRnWEn+dCdted2wXR83TgQK41eA==} '@fidm/asn1@1.0.4': resolution: {integrity: sha512-esd1jyNvRb2HVaQGq2Gg8Z0kbQPXzV9Tq5Z14KNIov6KfFD6PTaRIO8UpcsYiTNzOqJpmyzWgVTrUwFV3UF4TQ==} @@ -15969,7 +15969,7 @@ snapshots: '@eslint/js@8.57.0': {} - '@fast-crud/fast-crud@1.25.13(vue@3.5.14(typescript@5.8.3))': + '@fast-crud/fast-crud@1.26.6(vue@3.5.14(typescript@5.8.3))': dependencies: '@iconify/types': 2.0.0 file-saver: 2.0.5 @@ -15979,7 +15979,7 @@ snapshots: transitivePeerDependencies: - vue - '@fast-crud/fast-extends@1.25.13(aws-crt@1.26.2)(vue@3.5.14(typescript@5.8.3))': + '@fast-crud/fast-extends@1.26.6(aws-crt@1.26.2)(vue@3.5.14(typescript@5.8.3))': dependencies: '@aws-sdk/client-s3': 3.810.0(aws-crt@1.26.2) '@aws-sdk/s3-request-presigner': 3.810.0 @@ -15996,8 +15996,6 @@ snapshots: js-yaml: 4.1.0 jsoneditor: 9.10.5 lodash-es: 4.17.21 - monaco-editor: 0.52.2 - monaco-yaml: 5.4.0(monaco-editor@0.52.2) object-assign: 4.1.1 qiniu-js: 3.4.1 quill: 1.3.7 @@ -16011,9 +16009,9 @@ snapshots: - utf-8-validate - vue - '@fast-crud/ui-antdv4@1.25.13': {} + '@fast-crud/ui-antdv4@1.26.6': {} - '@fast-crud/ui-interface@1.25.13': + '@fast-crud/ui-interface@1.26.6': dependencies: lodash-es: 4.17.21 diff --git a/start.sh b/start.sh index 78bb46ce0..1f3bb7ca9 100755 --- a/start.sh +++ b/start.sh @@ -25,30 +25,30 @@ done echo "安装pnpm, 前提是已经安装了nodejs" -npm install -g pnpm --registry https://registry.npmmirror.com +sudo npm install -g pnpm --registry https://registry.npmmirror.com echo "安装依赖" -pnpm install --registry https://registry.npmmirror.com +sudo pnpm install --registry https://registry.npmmirror.com echo "开始构建" echo "构建certd-client" export NODE_OPTIONS=--max-old-space-size=32768 cd packages/ui/certd-client -pnpm run build +sudo -E pnpm run build cp -r dist/* ../certd-server/public echo "构建certd-server" cd ../certd-server -pnpm run build +sudo -E pnpm run build echo "构建完成" echo "启动服务" # 前台运行 if [ $confirmNohup != "y" ]; then echo "当前运行模式为前台运行,ctrl+c或者关闭ssh将会停止运行" - pnpm run start + sudo pnpm run start else echo "当前运行模式为后台运行,可以通过tail -f ./certd.log 命令查看日志" - nohup pnpm run start > certd.log & + nohup sudo pnpm run start > certd.log & fi