diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..ab4176944 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# +# http://editorconfig.org +# + +root = true + +[*] +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true + diff --git a/.github/ISSUE_TEMPLATE/plugin.md b/.github/ISSUE_TEMPLATE/1plugin.md similarity index 86% rename from .github/ISSUE_TEMPLATE/plugin.md rename to .github/ISSUE_TEMPLATE/1plugin.md index dc473210d..0439f1c84 100644 --- a/.github/ISSUE_TEMPLATE/plugin.md +++ b/.github/ISSUE_TEMPLATE/1plugin.md @@ -1,11 +1,11 @@ --- name: Plugin Apply -about: 请求支持新部署插件 +about: 部署插件申请支持 title: "[Plugin] " labels: feature --- -> > 感谢您支持certd,请按如下规范提交issue +> > 感谢您支持certd,请按如下规范提交issue > 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues) # 新部署插件申请支持 diff --git a/.github/ISSUE_TEMPLATE/dns.md b/.github/ISSUE_TEMPLATE/2dns.md similarity index 61% rename from .github/ISSUE_TEMPLATE/dns.md rename to .github/ISSUE_TEMPLATE/2dns.md index d0942c54d..ac13523f8 100644 --- a/.github/ISSUE_TEMPLATE/dns.md +++ b/.github/ISSUE_TEMPLATE/2dns.md @@ -1,12 +1,12 @@ --- name: DNS Provider Apply -about: 请求支持新的域名提供商 +about: 域名提供商申请支持 title: "[DNS] " labels: feature --- -> 感谢您支持certd,请按如下规范提交issue +> 感谢您支持certd,请按如下规范提交issue > 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues) # 新域名提供商支持申请 @@ -14,23 +14,23 @@ labels: feature ## 1. 基本信息 请填写如下内容: -1. 域名提供商名称: +1. 域名提供商名称: 2. 管理页面地址: -3. 是否有API接口,接口地址: +3. 是否有API接口,接口地址: -4. 如果没有API接口,网页登录是否有验证码: +4. 如果没有API接口,网页登录是否有验证码: 5. 是否可以提供测试账号?(如果可以请留下联系方式或者加作者好友) -## 2. 截图 +## 2. 截图 `域名管理页面截图` diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/3bug.md similarity index 86% rename from .github/ISSUE_TEMPLATE/bug.md rename to .github/ISSUE_TEMPLATE/3bug.md index 4cc6c94d5..fbbf5b17a 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/3bug.md @@ -1,12 +1,12 @@ --- name: Bug Report -about: 报告一个错误或问题 +about: 错误或问题报告 title: "[BUG] " labels: bug --- -> 感谢您支持certd,请按如下规范提交issue +> 感谢您支持certd,请按如下规范提交issue > 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues) # bug提交 diff --git a/.github/ISSUE_TEMPLATE/feature.md b/.github/ISSUE_TEMPLATE/4feature.md similarity index 68% rename from .github/ISSUE_TEMPLATE/feature.md rename to .github/ISSUE_TEMPLATE/4feature.md index f39a1ddf1..0ba1520bd 100644 --- a/.github/ISSUE_TEMPLATE/feature.md +++ b/.github/ISSUE_TEMPLATE/4feature.md @@ -1,15 +1,16 @@ --- name: Feature Request -about: 新需求、新特性 +about: 新需求、新特性申请支持 title: "[Feature] " labels: feature --- -> > 感谢您支持certd,请按如下规范提交issue +> > 感谢您支持certd,请按如下规范提交issue > 如果有条件,请尽量在[github上提交](https://github.com/certd/certd/issues) -# 新需求申请 +# 新特性申请 +>注意:这里仅供如果是要申请新的部署插件,请提交插件申请 ## 1. 需求描述,需求背景 `请在此处简要描述你所遇到的问题,必要时请贴出相关截图辅助理解` diff --git a/CHANGELOG.md b/CHANGELOG.md index fde503c72..6e02139a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,36 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +### Bug Fixes + +* 修复新部署的无法保存公共eab配置的bug ([d5dee75](https://github.com/certd/certd/commit/d5dee75df3bd635a597436e448b2de1407531f3a)) + +### Performance Improvements + +* 阿里云 FC3.0 不在要求证书加密方式为旧版, 修复支持的协议类型可以正常选择 ([a34db74](https://github.com/certd/certd/commit/a34db7449eff6ad1dda01de673bf85579fa3865a)) +* 部署到腾讯云cdn,每个域名增加3每秒延迟 ([f7d43ad](https://github.com/certd/certd/commit/f7d43ad5af4663d4be369820a80d1fd9817ca4ab)) +* 腾讯云关闭证书通知增加开关选项,在腾讯云授权里面 ([a77c777](https://github.com/certd/certd/commit/a77c777980dd38d97d983124eeed1596879bba95)) +* 证书申请任务默认不发送申请成功通知 ([0283bd2](https://github.com/certd/certd/commit/0283bd2f978dbcd13d361129135e439dd9fbc180)) + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +### Bug Fixes + +* 修复授权配置复制功能,无法复制已加密字段的问题 ([221e068](https://github.com/certd/certd/commit/221e068bac3af6cd5d1794f8cd4c2ec5c0bc3f45)) + +### Performance Improvements + +* 百度云支持上传到证书托管,支持部署到负载均衡 ([798a48a](https://github.com/certd/certd/commit/798a48aa9686fd5d11cfffb6cd93eadfc40aacb3)) +* 部署到百度cdn支持自动获取域名列表选择 ([4e432ed](https://github.com/certd/certd/commit/4e432ed03f4fb564e85a2f284ee26b58400b82f5)) +* 验证码可重试次数设置为3次 ([1bdceee](https://github.com/certd/certd/commit/1bdceeecf4b5daecdd621a05a2596b6eb45ce8ea)) +* 增加找回密码的验证码可重试次数 [@nicheng-he](https://github.com/nicheng-he) ([#496](https://github.com/certd/certd/issues/496)) ([fe03f99](https://github.com/certd/certd/commit/fe03f9942b5662fb90cad86da10782f5dc3603f5)) +* 支持阿里云API网关 ([9e1e4ee](https://github.com/certd/certd/commit/9e1e4eeec2859759ca5b07834c9d24cf88a6ad33)) +* 支持部署到金山云CDN ([dfa74a6](https://github.com/certd/certd/commit/dfa74a69f7cbb9009d3e20c7eecfa1b905a00cf0)) +* 支持更新金山云cdn证书 ([462e22a](https://github.com/certd/certd/commit/462e22a3b0a94887462fe6aa68e4671a365e0737)) +* 支持apisix证书部署 ([9b63fb4](https://github.com/certd/certd/commit/9b63fb4ee2c6b56139160c5bf63482dab0869c2b)) + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) ### Bug Fixes diff --git a/build.trigger b/build.trigger index 1af9d98f8..f5ed3ee83 100644 --- a/build.trigger +++ b/build.trigger @@ -1 +1 @@ -23:23 +23:58 diff --git a/docker/run/docker-compose.yaml b/docker/run/docker-compose.yaml index ef05998db..76e0eaae1 100644 --- a/docker/run/docker-compose.yaml +++ b/docker/run/docker-compose.yaml @@ -11,6 +11,9 @@ services: # ↓↓↓↓↓ -------------------------------------------------------- 数据库以及证书存储路径,默认存在宿主机的/data/certd/目录下,【您需要定时备份此目录,以保障数据容灾】 # 只要修改冒号前面的,冒号后面的/app/data不要动 - /data/certd:/app/data + # ↓↓↓↓↓ -------------------------------------------------------- 如果走时不准,考虑挂载localtime文件 + #- /etc/localtime:/etc/localtime + #- /etc/timezone:/etc/timezone ports: # 端口映射 # ↓↓↓↓ ---------------------------------------------------------- 如果端口有冲突,可以修改第一个7001为其他不冲突的端口号,第二个7001不要动 - "7001:7001" @@ -42,7 +45,7 @@ services: # 设置环境变量即可自定义certd配置 # 配置项见: packages/ui/certd-server/src/config/config.default.ts # 配置规则: certd_ + 配置项, 点号用_代替 -# #↓↓↓↓ ----------------------------- 如果忘记管理员密码,可以设置为true,重启之后,管理员密码将改成123456,然后请及时修改回false +# #↓↓↓↓ ----------------------------- 如果忘记管理员密码,可以设置为true,docker compose up -d 重建容器之后,管理员密码将改成123456,然后请及时修改回false - certd_system_resetAdminPasswd=false # 默认使用sqlite文件数据库,如果需要使用其他数据库,请设置以下环境变量 diff --git a/docs/guide/changelogs/CHANGELOG.md b/docs/guide/changelogs/CHANGELOG.md index fde503c72..6e02139a1 100644 --- a/docs/guide/changelogs/CHANGELOG.md +++ b/docs/guide/changelogs/CHANGELOG.md @@ -3,6 +3,36 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +### Bug Fixes + +* 修复新部署的无法保存公共eab配置的bug ([d5dee75](https://github.com/certd/certd/commit/d5dee75df3bd635a597436e448b2de1407531f3a)) + +### Performance Improvements + +* 阿里云 FC3.0 不在要求证书加密方式为旧版, 修复支持的协议类型可以正常选择 ([a34db74](https://github.com/certd/certd/commit/a34db7449eff6ad1dda01de673bf85579fa3865a)) +* 部署到腾讯云cdn,每个域名增加3每秒延迟 ([f7d43ad](https://github.com/certd/certd/commit/f7d43ad5af4663d4be369820a80d1fd9817ca4ab)) +* 腾讯云关闭证书通知增加开关选项,在腾讯云授权里面 ([a77c777](https://github.com/certd/certd/commit/a77c777980dd38d97d983124eeed1596879bba95)) +* 证书申请任务默认不发送申请成功通知 ([0283bd2](https://github.com/certd/certd/commit/0283bd2f978dbcd13d361129135e439dd9fbc180)) + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +### Bug Fixes + +* 修复授权配置复制功能,无法复制已加密字段的问题 ([221e068](https://github.com/certd/certd/commit/221e068bac3af6cd5d1794f8cd4c2ec5c0bc3f45)) + +### Performance Improvements + +* 百度云支持上传到证书托管,支持部署到负载均衡 ([798a48a](https://github.com/certd/certd/commit/798a48aa9686fd5d11cfffb6cd93eadfc40aacb3)) +* 部署到百度cdn支持自动获取域名列表选择 ([4e432ed](https://github.com/certd/certd/commit/4e432ed03f4fb564e85a2f284ee26b58400b82f5)) +* 验证码可重试次数设置为3次 ([1bdceee](https://github.com/certd/certd/commit/1bdceeecf4b5daecdd621a05a2596b6eb45ce8ea)) +* 增加找回密码的验证码可重试次数 [@nicheng-he](https://github.com/nicheng-he) ([#496](https://github.com/certd/certd/issues/496)) ([fe03f99](https://github.com/certd/certd/commit/fe03f9942b5662fb90cad86da10782f5dc3603f5)) +* 支持阿里云API网关 ([9e1e4ee](https://github.com/certd/certd/commit/9e1e4eeec2859759ca5b07834c9d24cf88a6ad33)) +* 支持部署到金山云CDN ([dfa74a6](https://github.com/certd/certd/commit/dfa74a69f7cbb9009d3e20c7eecfa1b905a00cf0)) +* 支持更新金山云cdn证书 ([462e22a](https://github.com/certd/certd/commit/462e22a3b0a94887462fe6aa68e4671a365e0737)) +* 支持apisix证书部署 ([9b63fb4](https://github.com/certd/certd/commit/9b63fb4ee2c6b56139160c5bf63482dab0869c2b)) + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) ### Bug Fixes diff --git a/docs/guide/qa/index.md b/docs/guide/qa/index.md index 7171795b4..88a367146 100644 --- a/docs/guide/qa/index.md +++ b/docs/guide/qa/index.md @@ -65,8 +65,16 @@ networks: docker logs -f --tail 200 certd ``` - - +## 6. 容器内走时不准,或者时区不对 +走时不准确,慢慢偏差越来越大 +或者整个时区都不对 +可以尝试挂载localtime文件 +```yaml + volumes: + # ↓↓↓↓↓ -------------------- 如果走时不准,请尝试挂载localtime文件 + - /etc/localtime:/etc/localtime + - /etc/timezone:/etc/timezone +``` diff --git a/lerna.json b/lerna.json index dd4210ae1..bbbdafd7b 100644 --- a/lerna.json +++ b/lerna.json @@ -9,5 +9,5 @@ } }, "npmClient": "pnpm", - "version": "1.36.15" + "version": "1.36.17" } diff --git a/package.json b/package.json index e18c98f32..e6d136153 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "init": "lerna run build", "init:dev": "lerna run build", "docs:dev": "vitepress dev docs", - "docs:build": "vitepress build docs", + "docs:build": "npm run copylogs && vitepress build docs", "docs:preview": "vitepress preview docs", "pub": "echo 1" }, diff --git a/packages/core/acme-client/.editorconfig b/packages/core/acme-client/.editorconfig index 7660a90c1..8214df12a 100644 --- a/packages/core/acme-client/.editorconfig +++ b/packages/core/acme-client/.editorconfig @@ -6,7 +6,7 @@ root = true [*] indent_style = space -indent_size = 4 +indent_size = 2 trim_trailing_whitespace = true [{*.yml,*.yaml}] diff --git a/packages/core/acme-client/CHANGELOG.md b/packages/core/acme-client/CHANGELOG.md index 495a4e96e..5a3217de1 100644 --- a/packages/core/acme-client/CHANGELOG.md +++ b/packages/core/acme-client/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/publishlab/node-acme-client/compare/v1.36.16...v1.36.17) (2025-08-17) + +**Note:** Version bump only for package @certd/acme-client + +## [1.36.16](https://github.com/publishlab/node-acme-client/compare/v1.36.15...v1.36.16) (2025-08-16) + +### Performance Improvements + +* 部署到百度cdn支持自动获取域名列表选择 ([4e432ed](https://github.com/publishlab/node-acme-client/commit/4e432ed03f4fb564e85a2f284ee26b58400b82f5)) + ## [1.36.15](https://github.com/publishlab/node-acme-client/compare/v1.36.14...v1.36.15) (2025-08-07) **Note:** Version bump only for package @certd/acme-client diff --git a/packages/core/acme-client/package.json b/packages/core/acme-client/package.json index 7c7bd5bfa..e83971a56 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.15", + "version": "1.36.17", "type": "module", "module": "scr/index.js", "main": "src/index.js", @@ -18,7 +18,7 @@ "types" ], "dependencies": { - "@certd/basic": "^1.36.15", + "@certd/basic": "^1.36.17", "@peculiar/x509": "^1.11.0", "asn1js": "^3.0.5", "axios": "^1.7.2", @@ -69,5 +69,5 @@ "bugs": { "url": "https://github.com/publishlab/node-acme-client/issues" }, - "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" + "gitHead": "831c325c6383ba0a6f2dfa7496451ec714784e93" } diff --git a/packages/core/basic/CHANGELOG.md b/packages/core/basic/CHANGELOG.md index 08900434c..12a89bdbe 100644 --- a/packages/core/basic/CHANGELOG.md +++ b/packages/core/basic/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +**Note:** Version bump only for package @certd/basic + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +**Note:** Version bump only for package @certd/basic + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) **Note:** Version bump only for package @certd/basic diff --git a/packages/core/basic/build.md b/packages/core/basic/build.md index 70bd44af2..7a6a87811 100644 --- a/packages/core/basic/build.md +++ b/packages/core/basic/build.md @@ -1 +1 @@ -23:18 +23:53 diff --git a/packages/core/basic/package.json b/packages/core/basic/package.json index 28bce9626..0fe465a68 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.15", + "version": "1.36.17", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -45,5 +45,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" + "gitHead": "831c325c6383ba0a6f2dfa7496451ec714784e93" } diff --git a/packages/core/pipeline/CHANGELOG.md b/packages/core/pipeline/CHANGELOG.md index abd83d047..fba2e62ad 100644 --- a/packages/core/pipeline/CHANGELOG.md +++ b/packages/core/pipeline/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.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +**Note:** Version bump only for package @certd/pipeline + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +### Performance Improvements + +* 百度云支持上传到证书托管,支持部署到负载均衡 ([798a48a](https://github.com/certd/certd/commit/798a48aa9686fd5d11cfffb6cd93eadfc40aacb3)) +* 支持部署到金山云CDN ([dfa74a6](https://github.com/certd/certd/commit/dfa74a69f7cbb9009d3e20c7eecfa1b905a00cf0)) + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) **Note:** Version bump only for package @certd/pipeline diff --git a/packages/core/pipeline/package.json b/packages/core/pipeline/package.json index 76b9e3ce8..9944b6a36 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.15", + "version": "1.36.17", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -17,8 +17,8 @@ "pub": "npm publish" }, "dependencies": { - "@certd/basic": "^1.36.15", - "@certd/plus-core": "^1.36.15", + "@certd/basic": "^1.36.17", + "@certd/plus-core": "^1.36.17", "dayjs": "^1.11.7", "lodash-es": "^4.17.21", "reflect-metadata": "^0.1.13" @@ -44,5 +44,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" + "gitHead": "831c325c6383ba0a6f2dfa7496451ec714784e93" } diff --git a/packages/core/pipeline/src/context/index.ts b/packages/core/pipeline/src/context/index.ts index 5679590ee..0dd6b147e 100644 --- a/packages/core/pipeline/src/context/index.ts +++ b/packages/core/pipeline/src/context/index.ts @@ -11,7 +11,6 @@ export type PageSearch = { // sortOrder?: "asc" | "desc"; }; - export type PageRes = { pageNo?: number; pageSize?: number; diff --git a/packages/core/pipeline/src/plugin/group.ts b/packages/core/pipeline/src/plugin/group.ts index bb83090f6..6a4b48d3b 100644 --- a/packages/core/pipeline/src/plugin/group.ts +++ b/packages/core/pipeline/src/plugin/group.ts @@ -27,6 +27,7 @@ export const pluginGroups = { tencent: new PluginGroup("tencent", "腾讯云", 4, "svg:icon-tencentcloud"), volcengine: new PluginGroup("volcengine", "火山引擎", 4, "svg:icon-volcengine"), jdcloud: new PluginGroup("jdcloud", "京东云", 4, "svg:icon-jdcloud"), + baidu: new PluginGroup("baidu", "百度云", 4, "ant-design:baidu-outlined"), qiniu: new PluginGroup("qiniu", "七牛云", 5, "svg:icon-qiniuyun"), aws: new PluginGroup("aws", "亚马逊云", 6, "svg:icon-aws"), other: new PluginGroup("other", "其他", 10, "clarity:plugin-line"), diff --git a/packages/libs/lib-huawei/CHANGELOG.md b/packages/libs/lib-huawei/CHANGELOG.md index 40ab6368f..5688a5362 100644 --- a/packages/libs/lib-huawei/CHANGELOG.md +++ b/packages/libs/lib-huawei/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +**Note:** Version bump only for package @certd/lib-huawei + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +**Note:** Version bump only for package @certd/lib-huawei + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) **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 bb5af44e3..3683308e8 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.15", + "version": "1.36.17", "main": "./dist/bundle.js", "module": "./dist/bundle.js", "types": "./dist/d/index.d.ts", @@ -24,5 +24,5 @@ "prettier": "^2.8.8", "tslib": "^2.8.1" }, - "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" + "gitHead": "831c325c6383ba0a6f2dfa7496451ec714784e93" } diff --git a/packages/libs/lib-iframe/CHANGELOG.md b/packages/libs/lib-iframe/CHANGELOG.md index 3deda2398..c9c107c62 100644 --- a/packages/libs/lib-iframe/CHANGELOG.md +++ b/packages/libs/lib-iframe/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +**Note:** Version bump only for package @certd/lib-iframe + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +**Note:** Version bump only for package @certd/lib-iframe + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) **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 7567f8f25..b81af813a 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.15", + "version": "1.36.17", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -31,5 +31,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" + "gitHead": "831c325c6383ba0a6f2dfa7496451ec714784e93" } diff --git a/packages/libs/lib-jdcloud/CHANGELOG.md b/packages/libs/lib-jdcloud/CHANGELOG.md index 4b435e515..e4cc75378 100644 --- a/packages/libs/lib-jdcloud/CHANGELOG.md +++ b/packages/libs/lib-jdcloud/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +**Note:** Version bump only for package @certd/jdcloud + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +**Note:** Version bump only for package @certd/jdcloud + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) **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 910228943..60233cc37 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.15", + "version": "1.36.17", "description": "jdcloud openApi sdk", "main": "./dist/bundle.js", "module": "./dist/bundle.js", @@ -61,5 +61,5 @@ "fetch" ] }, - "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" + "gitHead": "831c325c6383ba0a6f2dfa7496451ec714784e93" } diff --git a/packages/libs/lib-k8s/CHANGELOG.md b/packages/libs/lib-k8s/CHANGELOG.md index e23c4da4b..5a0149f94 100644 --- a/packages/libs/lib-k8s/CHANGELOG.md +++ b/packages/libs/lib-k8s/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +**Note:** Version bump only for package @certd/lib-k8s + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +**Note:** Version bump only for package @certd/lib-k8s + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) **Note:** Version bump only for package @certd/lib-k8s diff --git a/packages/libs/lib-k8s/package.json b/packages/libs/lib-k8s/package.json index 91bb0a4a1..7df9c6f32 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.15", + "version": "1.36.17", "type": "module", "main": "./dist/index.js", "module": "./dist/index.js", @@ -17,7 +17,7 @@ "pub": "npm publish" }, "dependencies": { - "@certd/basic": "^1.36.15", + "@certd/basic": "^1.36.17", "@kubernetes/client-node": "0.21.0" }, "devDependencies": { @@ -32,5 +32,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" + "gitHead": "831c325c6383ba0a6f2dfa7496451ec714784e93" } diff --git a/packages/libs/lib-server/CHANGELOG.md b/packages/libs/lib-server/CHANGELOG.md index e27f4cfb4..5a38b23dd 100644 --- a/packages/libs/lib-server/CHANGELOG.md +++ b/packages/libs/lib-server/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +**Note:** Version bump only for package @certd/lib-server + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +### Bug Fixes + +* 修复授权配置复制功能,无法复制已加密字段的问题 ([221e068](https://github.com/certd/certd/commit/221e068bac3af6cd5d1794f8cd4c2ec5c0bc3f45)) + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) **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 5671a003b..83ca13a24 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.15", + "version": "1.36.17", "description": "midway with flyway, sql upgrade way ", "private": false, "type": "module", @@ -27,10 +27,10 @@ ], "license": "AGPL", "dependencies": { - "@certd/acme-client": "^1.36.15", - "@certd/basic": "^1.36.15", - "@certd/pipeline": "^1.36.15", - "@certd/plus-core": "^1.36.15", + "@certd/acme-client": "^1.36.17", + "@certd/basic": "^1.36.17", + "@certd/pipeline": "^1.36.17", + "@certd/plus-core": "^1.36.17", "@midwayjs/cache": "~3.14.0", "@midwayjs/core": "~3.20.3", "@midwayjs/i18n": "~3.20.3", @@ -61,5 +61,5 @@ "typeorm": "^0.3.11", "typescript": "^5.4.2" }, - "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" + "gitHead": "831c325c6383ba0a6f2dfa7496451ec714784e93" } diff --git a/packages/libs/lib-server/src/user/access/service/access-service.ts b/packages/libs/lib-server/src/user/access/service/access-service.ts index f99f6330d..756e72d1d 100644 --- a/packages/libs/lib-server/src/user/access/service/access-service.ts +++ b/packages/libs/lib-server/src/user/access/service/access-service.ts @@ -34,7 +34,18 @@ export class AccessService extends BaseService { } async add(param) { - this.encryptSetting(param, null); + let oldEntity = null; + if (param._copyFrom){ + oldEntity = await this.info(param._copyFrom); + if (oldEntity == null) { + throw new ValidateException('该授权配置不存在,请确认是否已被删除'); + } + if (oldEntity.userId !== param.userId) { + throw new ValidateException('您无权查看该授权配置'); + } + } + delete param._copyFrom + this.encryptSetting(param, oldEntity); return await super.add(param); } diff --git a/packages/libs/midway-flyway-js/CHANGELOG.md b/packages/libs/midway-flyway-js/CHANGELOG.md index 2479d1e3b..341d1f19c 100644 --- a/packages/libs/midway-flyway-js/CHANGELOG.md +++ b/packages/libs/midway-flyway-js/CHANGELOG.md @@ -3,6 +3,14 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +**Note:** Version bump only for package @certd/midway-flyway-js + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +**Note:** Version bump only for package @certd/midway-flyway-js + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) **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 fb9d18e14..21ea147ae 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.15", + "version": "1.36.17", "description": "midway with flyway, sql upgrade way ", "private": false, "type": "module", @@ -46,5 +46,5 @@ "typeorm": "^0.3.11", "typescript": "^5.4.2" }, - "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" + "gitHead": "831c325c6383ba0a6f2dfa7496451ec714784e93" } diff --git a/packages/plugins/plugin-cert/CHANGELOG.md b/packages/plugins/plugin-cert/CHANGELOG.md index 059926fce..c68b54780 100644 --- a/packages/plugins/plugin-cert/CHANGELOG.md +++ b/packages/plugins/plugin-cert/CHANGELOG.md @@ -3,6 +3,18 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +### Performance Improvements + +* 证书申请任务默认不发送申请成功通知 ([0283bd2](https://github.com/certd/certd/commit/0283bd2f978dbcd13d361129135e439dd9fbc180)) + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +### Performance Improvements + +* 百度云支持上传到证书托管,支持部署到负载均衡 ([798a48a](https://github.com/certd/certd/commit/798a48aa9686fd5d11cfffb6cd93eadfc40aacb3)) + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) **Note:** Version bump only for package @certd/plugin-cert diff --git a/packages/plugins/plugin-cert/package.json b/packages/plugins/plugin-cert/package.json index aea6226d4..81cae2d3f 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.15", + "version": "1.36.17", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", @@ -16,10 +16,10 @@ "pub": "npm publish" }, "dependencies": { - "@certd/acme-client": "^1.36.15", - "@certd/basic": "^1.36.15", - "@certd/pipeline": "^1.36.15", - "@certd/plugin-lib": "^1.36.15", + "@certd/acme-client": "^1.36.17", + "@certd/basic": "^1.36.17", + "@certd/pipeline": "^1.36.17", + "@certd/plugin-lib": "^1.36.17", "@google-cloud/publicca": "^1.3.0", "dayjs": "^1.11.7", "jszip": "^3.10.1", @@ -43,5 +43,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" + "gitHead": "831c325c6383ba0a6f2dfa7496451ec714784e93" } diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts index 2d1e4362d..3f55a27bf 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/acme.ts @@ -48,6 +48,7 @@ export type CertInfo = { der?: string; jks?: string; one?: string; + p7b?: string; }; export type SSLProvider = "letsencrypt" | "google" | "zerossl"; export type PrivateKeyType = "rsa_1024" | "rsa_2048" | "rsa_3072" | "rsa_4096" | "ec_256" | "ec_384" | "ec_521"; 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 e25707cb1..d2725b8de 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 @@ -125,6 +125,10 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin { cert.jks = res.jks; } + if (cert.p7b == null && res.p7b) { + cert.p7b = res.p7b; + } + this.logger.info("转换证书格式成功"); } catch (e) { this.logger.error("转换证书格式失败", e); @@ -150,6 +154,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin { zip.file("intermediate.crt", cert.ic); zip.file("origin.crt", cert.oc); zip.file("one.pem", cert.one); + zip.file("cert.p7b", cert.p7b); if (cert.pfx) { zip.file("cert.pfx", Buffer.from(cert.pfx, "base64")); } diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts index cd612334f..8180fb829 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/base.ts @@ -33,7 +33,7 @@ export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin { @TaskInput({ title: "证书申请成功通知", - value: true, + value: false, component: { name: "a-switch", vModel: "checked", @@ -41,7 +41,7 @@ export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin { order: 100, helper: "证书申请成功后是否发送通知,优先使用默认通知渠道", }) - successNotify = true; + successNotify = false; // @TaskInput({ // title: "CsrInfo", 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 adcfe4684..318aa0c48 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 @@ -17,6 +17,7 @@ export type CertReaderHandleContext = { tmpIcPath?: string; tmpJksPath?: string; tmpOnePath?: string; + tmpP7bPath?: string; }; export type CertReaderHandle = (ctx: CertReaderHandleContext) => Promise; export type HandleOpts = { logger: ILogger; handle: CertReaderHandle }; @@ -124,7 +125,7 @@ export class CertReader { return domain; } - saveToFile(type: "crt" | "key" | "pfx" | "der" | "oc" | "one" | "ic" | "jks", filepath?: string) { + saveToFile(type: "crt" | "key" | "pfx" | "der" | "oc" | "one" | "ic" | "jks" | "p7b", filepath?: string) { if (!this.cert[type]) { return; } @@ -138,7 +139,7 @@ export class CertReader { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } - if (type === "crt" || type === "key" || type === "ic" || type === "oc" || type === "one") { + if (type === "crt" || type === "key" || type === "ic" || type === "oc" || type === "one" || type === "p7b") { fs.writeFileSync(filepath, this.cert[type]); } else { fs.writeFileSync(filepath, Buffer.from(this.cert[type], "base64")); @@ -157,17 +158,19 @@ export class CertReader { const tmpDerPath = this.saveToFile("der"); const tmpJksPath = this.saveToFile("jks"); const tmpOnePath = this.saveToFile("one"); + const tmpP7bPath = this.saveToFile("p7b"); logger.info("本地文件写入成功"); try { return await opts.handle({ reader: this, - tmpCrtPath: tmpCrtPath, - tmpKeyPath: tmpKeyPath, - tmpPfxPath: tmpPfxPath, - tmpDerPath: tmpDerPath, - tmpIcPath: tmpIcPath, - tmpJksPath: tmpJksPath, - tmpOcPath: tmpOcPath, + tmpCrtPath, + tmpKeyPath, + tmpPfxPath, + tmpDerPath, + tmpIcPath, + tmpJksPath, + tmpOcPath, + tmpP7bPath, tmpOnePath, }); } catch (err) { @@ -189,6 +192,7 @@ export class CertReader { removeFile(tmpIcPath); removeFile(tmpJksPath); removeFile(tmpOnePath); + removeFile(tmpP7bPath); } } @@ -211,4 +215,8 @@ export class CertReader { } return name + "_" + dayjs().format("YYYYMMDDHHmmssSSS"); } + + static buildCertName(cert: any) { + return new CertReader(cert).buildCertName(); + } } diff --git a/packages/plugins/plugin-cert/src/plugin/cert-plugin/convert.ts b/packages/plugins/plugin-cert/src/plugin/cert-plugin/convert.ts index 59fec30c5..dfb4cafae 100644 --- a/packages/plugins/plugin-cert/src/plugin/cert-plugin/convert.ts +++ b/packages/plugins/plugin-cert/src/plugin/cert-plugin/convert.ts @@ -18,11 +18,13 @@ export class CertConverter { pfx: string; der: string; jks: string; + p7b: string; }> { const certReader = new CertReader(opts.cert); let pfx: string; let der: string; let jks: string; + let p7b: string; const handle = async (ctx: CertReaderHandleContext) => { // 调用openssl 转pfx pfx = await this.convertPfx(ctx, opts.pfxPassword, opts.pfxArgs); @@ -31,6 +33,8 @@ export class CertConverter { der = await this.convertDer(ctx); jks = await this.convertJks(ctx, opts.pfxPassword); + + p7b = await this.convertP7b(ctx); }; await certReader.readCertFile({ logger: this.logger, handle }); @@ -39,6 +43,7 @@ export class CertConverter { pfx, der, jks, + p7b, }; } @@ -95,6 +100,23 @@ export class CertConverter { return derCert; } + async convertP7b(opts: CertReaderHandleContext) { + const { tmpCrtPath } = opts; + const p7bPath = path.join(os.tmpdir(), "/certd/tmp/", Math.floor(Math.random() * 1000000) + `_cert.p7b`); + const dir = path.dirname(p7bPath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + //openssl crl2pkcs7 -nocrl \ + // -certfile your_domain.crt \ + // -certfile intermediate.crt \ + // -out chain.p7b + await this.exec(`openssl crl2pkcs7 -nocrl -certfile ${tmpCrtPath} -out ${p7bPath}`); + const fileBuffer = fs.readFileSync(p7bPath); + const p7bCert = fileBuffer.toString(); + fs.unlinkSync(p7bPath); + return p7bCert; + } async convertJks(opts: CertReaderHandleContext, pfxPassword = "") { const jksPassword = pfxPassword || "123456"; try { @@ -113,9 +135,7 @@ export class CertConverter { if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } - await this.exec( - `keytool -importkeystore -srckeystore ${p12Path} -srcstoretype PKCS12 -srcstorepass "${jksPassword}" -destkeystore ${jksPath} -deststoretype PKCS12 -deststorepass "${jksPassword}" ` - ); + await this.exec(`keytool -importkeystore -srckeystore ${p12Path} -srcstoretype PKCS12 -srcstorepass "${jksPassword}" -destkeystore ${jksPath} -deststoretype PKCS12 -deststorepass "${jksPassword}" `); fs.unlinkSync(p12Path); const fileBuffer = fs.readFileSync(jksPath); diff --git a/packages/plugins/plugin-lib/CHANGELOG.md b/packages/plugins/plugin-lib/CHANGELOG.md index 9428e1545..82b40351a 100644 --- a/packages/plugins/plugin-lib/CHANGELOG.md +++ b/packages/plugins/plugin-lib/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +### Performance Improvements + +* 腾讯云关闭证书通知增加开关选项,在腾讯云授权里面 ([a77c777](https://github.com/certd/certd/commit/a77c777980dd38d97d983124eeed1596879bba95)) + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +**Note:** Version bump only for package @certd/plugin-lib + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) ### Performance Improvements diff --git a/packages/plugins/plugin-lib/package.json b/packages/plugins/plugin-lib/package.json index 2fbe8cce8..e02c69283 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.15", + "version": "1.36.17", "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.15", - "@certd/pipeline": "^1.36.15", + "@certd/basic": "^1.36.17", + "@certd/pipeline": "^1.36.17", "@kubernetes/client-node": "0.21.0", "ali-oss": "^6.22.0", "basic-ftp": "^5.0.5", @@ -53,5 +53,5 @@ "tslib": "^2.8.1", "typescript": "^5.4.2" }, - "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" + "gitHead": "831c325c6383ba0a6f2dfa7496451ec714784e93" } diff --git a/packages/plugins/plugin-lib/src/ctyun/access/ctyun-access.ts b/packages/plugins/plugin-lib/src/ctyun/access/ctyun-access.ts index aa7ec53c6..04875b898 100644 --- a/packages/plugins/plugin-lib/src/ctyun/access/ctyun-access.ts +++ b/packages/plugins/plugin-lib/src/ctyun/access/ctyun-access.ts @@ -5,6 +5,7 @@ import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline"; title: "天翼云授权", desc: "", icon: "ant-design:aliyun-outlined", + order: 2, }) export class CtyunAccess extends BaseAccess { @AccessInput({ diff --git a/packages/plugins/plugin-lib/src/qiniu/access.ts b/packages/plugins/plugin-lib/src/qiniu/access.ts index d760abbf5..7a4e757ff 100644 --- a/packages/plugins/plugin-lib/src/qiniu/access.ts +++ b/packages/plugins/plugin-lib/src/qiniu/access.ts @@ -6,6 +6,7 @@ import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline"; desc: "", icon: "svg:icon-qiniuyun", input: {}, + order: 2, }) export class QiniuAccess extends BaseAccess { @AccessInput({ diff --git a/packages/plugins/plugin-lib/src/ssh/ssh-access.ts b/packages/plugins/plugin-lib/src/ssh/ssh-access.ts index 22f6956fe..984a435cc 100644 --- a/packages/plugins/plugin-lib/src/ssh/ssh-access.ts +++ b/packages/plugins/plugin-lib/src/ssh/ssh-access.ts @@ -5,6 +5,7 @@ import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline"; desc: "", icon: "clarity:host-line", input: {}, + order: 0, }) export class SshAccess extends BaseAccess { @AccessInput({ diff --git a/packages/plugins/plugin-lib/src/ssh/ssh.ts b/packages/plugins/plugin-lib/src/ssh/ssh.ts index e80079a4b..d0aa74e3c 100644 --- a/packages/plugins/plugin-lib/src/ssh/ssh.ts +++ b/packages/plugins/plugin-lib/src/ssh/ssh.ts @@ -247,6 +247,9 @@ export class AsyncSsh2Client { const err = this.convert(iconv, ret); stdErr += err; hasErrorLog = true; + if (err.includes("sudo: a password is required")) { + this.logger.warn("请配置sudo免密,否则命令无法执行"); + } this.logger.error(`[${this.connConf.host}][error]: ` + err.trimEnd()); }); }); diff --git a/packages/plugins/plugin-lib/src/tencent/access.ts b/packages/plugins/plugin-lib/src/tencent/access.ts index 6e0f03b5f..aaa1c150a 100644 --- a/packages/plugins/plugin-lib/src/tencent/access.ts +++ b/packages/plugins/plugin-lib/src/tencent/access.ts @@ -4,6 +4,7 @@ import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline"; name: "tencent", title: "腾讯云", icon: "svg:icon-tencentcloud", + order: 0, }) export class TencentAccess extends BaseAccess { @AccessInput({ @@ -46,7 +47,21 @@ export class TencentAccess extends BaseAccess { }) accountType: string; + @AccessInput({ + title: "关闭证书过期通知", + value: true, + component: { + name: "a-switch", + vModel: "checked", + }, + }) + closeExpiresNotify: boolean = true; + isIntl() { return this.accountType === "intl"; } + + intlDomain() { + return this.isIntl() ? "intl." : ""; + } } diff --git a/packages/plugins/plugin-lib/src/tencent/lib/cos-client.js b/packages/plugins/plugin-lib/src/tencent/lib/cos-client.js new file mode 100644 index 000000000..a6d6af0a0 --- /dev/null +++ b/packages/plugins/plugin-lib/src/tencent/lib/cos-client.js @@ -0,0 +1,183 @@ +"use strict"; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.TencentCosClient = void 0; +var basic_1 = require("@certd/basic"); +var fs_1 = require("fs"); +var TencentCosClient = /** @class */ (function () { + function TencentCosClient(opts) { + this.access = opts.access; + this.logger = opts.logger; + this.bucket = opts.bucket; + this.region = opts.region; + } + TencentCosClient.prototype.getCosClient = function () { + return __awaiter(this, void 0, void 0, function () { + var sdk, clientConfig; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, Promise.resolve().then(function () { return require("cos-nodejs-sdk-v5"); })]; + case 1: + sdk = _a.sent(); + clientConfig = { + SecretId: this.access.secretId, + SecretKey: this.access.secretKey, + }; + return [2 /*return*/, new sdk.default(clientConfig)]; + } + }); + }); + }; + TencentCosClient.prototype.uploadFile = function (key, file) { + return __awaiter(this, void 0, void 0, function () { + var cos; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.getCosClient()]; + case 1: + cos = _a.sent(); + return [2 /*return*/, (0, basic_1.safePromise)(function (resolve, reject) { + var readableStream = file; + if (typeof file === "string") { + readableStream = fs_1.default.createReadStream(file); + } + cos.putObject({ + Bucket: _this.bucket /* 必须 */, + Region: _this.region /* 必须 */, + Key: key /* 必须 */, + Body: readableStream, // 上传文件对象 + onProgress: function (progressData) { + console.log(JSON.stringify(progressData)); + }, + }, function (err, data) { + if (err) { + reject(err); + return; + } + resolve(data); + }); + })]; + } + }); + }); + }; + TencentCosClient.prototype.removeFile = function (key) { + return __awaiter(this, void 0, void 0, function () { + var cos; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.getCosClient()]; + case 1: + cos = _a.sent(); + return [2 /*return*/, (0, basic_1.safePromise)(function (resolve, reject) { + cos.deleteObject({ + Bucket: _this.bucket, + Region: _this.region, + Key: key, + }, function (err, data) { + if (err) { + reject(err); + return; + } + resolve(data); + }); + })]; + } + }); + }); + }; + TencentCosClient.prototype.downloadFile = function (key, savePath) { + return __awaiter(this, void 0, void 0, function () { + var cos, writeStream; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.getCosClient()]; + case 1: + cos = _a.sent(); + writeStream = fs_1.default.createWriteStream(savePath); + return [2 /*return*/, (0, basic_1.safePromise)(function (resolve, reject) { + cos.getObject({ + Bucket: _this.bucket, + Region: _this.region, + Key: key, + Output: writeStream, + }, function (err, data) { + if (err) { + reject(err); + return; + } + resolve(data); + }); + })]; + } + }); + }); + }; + TencentCosClient.prototype.listDir = function (dirKey) { + return __awaiter(this, void 0, void 0, function () { + var cos; + var _this = this; + return __generator(this, function (_a) { + switch (_a.label) { + case 0: return [4 /*yield*/, this.getCosClient()]; + case 1: + cos = _a.sent(); + return [2 /*return*/, (0, basic_1.safePromise)(function (resolve, reject) { + cos.getBucket({ + Bucket: _this.bucket, + Region: _this.region, + Prefix: dirKey, + MaxKeys: 1000, + }, function (err, data) { + if (err) { + reject(err); + return; + } + resolve(data.Contents); + }); + })]; + } + }); + }); + }; + return TencentCosClient; +}()); +exports.TencentCosClient = TencentCosClient; diff --git a/packages/plugins/plugin-lib/src/tencent/lib/ssl-client.ts b/packages/plugins/plugin-lib/src/tencent/lib/ssl-client.ts index 8b1c56dfd..4846ee1ae 100644 --- a/packages/plugins/plugin-lib/src/tencent/lib/ssl-client.ts +++ b/packages/plugins/plugin-lib/src/tencent/lib/ssl-client.ts @@ -26,7 +26,7 @@ export class TencentSslClient { region: this.region, profile: { httpProfile: { - endpoint: "ssl.tencentcloudapi.com", + endpoint: this.access.isIntl() ? "ssl.intl.tencentcloudapi.com" : "ssl.tencentcloudapi.com", }, }, }; @@ -50,7 +50,10 @@ export class TencentSslClient { const ret = await client.UploadCertificate(params); this.checkRet(ret); this.logger.info(`证书[${opts.certName}]上传成功:tencentCertId=`, ret.CertificateId); - await this.switchCertNotify([ret.CertificateId], true); + if (this.access.closeExpiresNotify) { + await this.switchCertNotify([ret.CertificateId], true); + } + return ret.CertificateId; } diff --git a/packages/ui/certd-client/CHANGELOG.md b/packages/ui/certd-client/CHANGELOG.md index f7b5d11d4..d4a11f67c 100644 --- a/packages/ui/certd-client/CHANGELOG.md +++ b/packages/ui/certd-client/CHANGELOG.md @@ -3,6 +3,22 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +**Note:** Version bump only for package @certd/ui-client + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +### Bug Fixes + +* 修复授权配置复制功能,无法复制已加密字段的问题 ([221e068](https://github.com/certd/certd/commit/221e068bac3af6cd5d1794f8cd4c2ec5c0bc3f45)) + +### Performance Improvements + +* 增加找回密码的验证码可重试次数 [@nicheng-he](https://github.com/nicheng-he) ([#496](https://github.com/certd/certd/issues/496)) ([fe03f99](https://github.com/certd/certd/commit/fe03f9942b5662fb90cad86da10782f5dc3603f5)) +* 支持阿里云API网关 ([9e1e4ee](https://github.com/certd/certd/commit/9e1e4eeec2859759ca5b07834c9d24cf88a6ad33)) +* 支持部署到金山云CDN ([dfa74a6](https://github.com/certd/certd/commit/dfa74a69f7cbb9009d3e20c7eecfa1b905a00cf0)) + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) ### Performance Improvements diff --git a/packages/ui/certd-client/package.json b/packages/ui/certd-client/package.json index ea2b3827a..3d5127f02 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.15", + "version": "1.36.17", "private": true, "scripts": { "dev": "vite --open", @@ -43,7 +43,7 @@ "@tailwindcss/typography": "^0.5.16", "@tanstack/vue-store": "^0.7.0", "@vee-validate/zod": "^4.15.0", - "@vue-js-cron/light": "^4.0.5", + "@certd/vue-js-cron-light": "^4.0.14", "@vue/shared": "^3.5.13", "@vueuse/core": "^10.11.0", "ant-design-vue": "^4.2.6", @@ -103,8 +103,8 @@ "zod-defaults": "^0.1.3" }, "devDependencies": { - "@certd/lib-iframe": "^1.36.15", - "@certd/pipeline": "^1.36.15", + "@certd/lib-iframe": "^1.36.17", + "@certd/pipeline": "^1.36.17", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@types/chai": "^4.3.12", @@ -120,7 +120,7 @@ "@vue/compiler-sfc": "^3.4.21", "@vue/eslint-config-typescript": "^13.0.0", "@vue/test-utils": "^2.4.6", - "autoprefixer": "^10.4.20", + "autoprefixer": "^10.4.21", "caller-path": "^4.0.0", "chai": "^5.1.0", "dependency-cruiser": "^16.2.3", diff --git a/packages/ui/certd-client/public/static/icons/demo_index.html b/packages/ui/certd-client/public/static/icons/demo_index.html index 4f3dde4d0..14e926579 100644 --- a/packages/ui/certd-client/public/static/icons/demo_index.html +++ b/packages/ui/certd-client/public/static/icons/demo_index.html @@ -54,6 +54,36 @@
    +
  • + +
    social-foursquare
    +
    &#xe8fb;
    +
  • + +
  • + +
    ksyun-logo
    +
    &#xe65a;
    +
  • + +
  • + +
    雨-copy
    +
    &#xe608;
    +
  • + +
  • + +
    网宿
    +
    &#xe655;
    +
  • + +
  • + +
    ai客服
    +
    &#xe727;
    +
  • +
  • cdn
    @@ -198,7 +228,7 @@
    @font-face {
       font-family: 'iconfont';
    -  src: url('iconfont.svg?t=1743267254898#iconfont') format('svg');
    +  src: url('iconfont.svg?t=1754884110189#iconfont') format('svg');
     }
     

    第二步:定义使用 iconfont 的样式

    @@ -224,6 +254,51 @@
      +
    • + +
      + social-foursquare +
      +
      .icon-four +
      +
    • + +
    • + +
      + ksyun-logo +
      +
      .icon-ksyun +
      +
    • + +
    • + +
      + 雨-copy +
      +
      .icon-rainyun +
      +
    • + +
    • + +
      + 网宿 +
      +
      .icon-wangsu +
      +
    • + +
    • + +
      + ai客服 +
      +
      .icon-aikefu +
      +
    • +
    • @@ -440,6 +515,46 @@
        +
      • + +
        social-foursquare
        +
        #icon-four
        +
      • + +
      • + +
        ksyun-logo
        +
        #icon-ksyun
        +
      • + +
      • + +
        雨-copy
        +
        #icon-rainyun
        +
      • + +
      • + +
        网宿
        +
        #icon-wangsu
        +
      • + +
      • + +
        ai客服
        +
        #icon-aikefu
        +
      • +
      • - - - - {{ t("certd.saveButton") }} diff --git a/packages/ui/certd-client/src/views/sys/settings/tabs/payment.vue b/packages/ui/certd-client/src/views/sys/settings/tabs/payment.vue index 2f69007f0..d50e53da5 100644 --- a/packages/ui/certd-client/src/views/sys/settings/tabs/payment.vue +++ b/packages/ui/certd-client/src/views/sys/settings/tabs/payment.vue @@ -8,7 +8,7 @@ @@ -17,7 +17,7 @@ -
        需要开通电脑网站支付, 支付宝配置帮助文档
        +
        需要开通电脑网站支付, 支付宝配置帮助文档
        @@ -25,7 +25,7 @@ -
        需要开通Native支付, 微信配置帮助文档
        +
        需要开通Native支付, 微信配置帮助文档
        diff --git a/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue b/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue index 45a468e9a..2272ae8fd 100644 --- a/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue +++ b/packages/ui/certd-client/src/views/sys/settings/tabs/register.vue @@ -11,6 +11,9 @@ + + +
        diff --git a/packages/ui/certd-server/CHANGELOG.md b/packages/ui/certd-server/CHANGELOG.md index 72b8a9be7..f89486c2c 100644 --- a/packages/ui/certd-server/CHANGELOG.md +++ b/packages/ui/certd-server/CHANGELOG.md @@ -3,6 +3,29 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.36.17](https://github.com/certd/certd/compare/v1.36.16...v1.36.17) (2025-08-17) + +### Bug Fixes + +* 修复新部署的无法保存公共eab配置的bug ([d5dee75](https://github.com/certd/certd/commit/d5dee75df3bd635a597436e448b2de1407531f3a)) + +### Performance Improvements + +* 阿里云 FC3.0 不在要求证书加密方式为旧版, 修复支持的协议类型可以正常选择 ([a34db74](https://github.com/certd/certd/commit/a34db7449eff6ad1dda01de673bf85579fa3865a)) +* 部署到腾讯云cdn,每个域名增加3每秒延迟 ([f7d43ad](https://github.com/certd/certd/commit/f7d43ad5af4663d4be369820a80d1fd9817ca4ab)) + +## [1.36.16](https://github.com/certd/certd/compare/v1.36.15...v1.36.16) (2025-08-16) + +### Performance Improvements + +* 百度云支持上传到证书托管,支持部署到负载均衡 ([798a48a](https://github.com/certd/certd/commit/798a48aa9686fd5d11cfffb6cd93eadfc40aacb3)) +* 验证码可重试次数设置为3次 ([1bdceee](https://github.com/certd/certd/commit/1bdceeecf4b5daecdd621a05a2596b6eb45ce8ea)) +* 增加找回密码的验证码可重试次数 [@nicheng-he](https://github.com/nicheng-he) ([#496](https://github.com/certd/certd/issues/496)) ([fe03f99](https://github.com/certd/certd/commit/fe03f9942b5662fb90cad86da10782f5dc3603f5)) +* 支持阿里云API网关 ([9e1e4ee](https://github.com/certd/certd/commit/9e1e4eeec2859759ca5b07834c9d24cf88a6ad33)) +* 支持部署到金山云CDN ([dfa74a6](https://github.com/certd/certd/commit/dfa74a69f7cbb9009d3e20c7eecfa1b905a00cf0)) +* 支持更新金山云cdn证书 ([462e22a](https://github.com/certd/certd/commit/462e22a3b0a94887462fe6aa68e4671a365e0737)) +* 支持apisix证书部署 ([9b63fb4](https://github.com/certd/certd/commit/9b63fb4ee2c6b56139160c5bf63482dab0869c2b)) + ## [1.36.15](https://github.com/certd/certd/compare/v1.36.14...v1.36.15) (2025-08-07) ### Bug Fixes diff --git a/packages/ui/certd-server/README.md b/packages/ui/certd-server/README.md index 97e32f251..0745defec 100755 --- a/packages/ui/certd-server/README.md +++ b/packages/ui/certd-server/README.md @@ -10,6 +10,7 @@ ``` + ```shell npm run heap ``` diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json index ed51dc51c..531f8c391 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.15", + "version": "1.36.17", "description": "fast-server base midway", "private": true, "type": "module", @@ -42,20 +42,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.15", - "@certd/basic": "^1.36.15", - "@certd/commercial-core": "^1.36.15", + "@certd/acme-client": "^1.36.17", + "@certd/basic": "^1.36.17", + "@certd/commercial-core": "^1.36.17", "@certd/cv4pve-api-javascript": "^8.4.1", - "@certd/jdcloud": "^1.36.15", - "@certd/lib-huawei": "^1.36.15", - "@certd/lib-k8s": "^1.36.15", - "@certd/lib-server": "^1.36.15", - "@certd/midway-flyway-js": "^1.36.15", - "@certd/pipeline": "^1.36.15", - "@certd/plugin-cert": "^1.36.15", - "@certd/plugin-lib": "^1.36.15", - "@certd/plugin-plus": "^1.36.15", - "@certd/plus-core": "^1.36.15", + "@certd/jdcloud": "^1.36.17", + "@certd/lib-huawei": "^1.36.17", + "@certd/lib-k8s": "^1.36.17", + "@certd/lib-server": "^1.36.17", + "@certd/midway-flyway-js": "^1.36.17", + "@certd/pipeline": "^1.36.17", + "@certd/plugin-cert": "^1.36.17", + "@certd/plugin-lib": "^1.36.17", + "@certd/plugin-plus": "^1.36.17", + "@certd/plus-core": "^1.36.17", "@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120", "@huaweicloud/huaweicloud-sdk-core": "^3.1.120", "@koa/cors": "^5.0.0", @@ -82,6 +82,7 @@ "cross-env": "^7.0.3", "crypto-js": "^4.2.0", "dayjs": "^1.11.7", + "esdk-obs-nodejs": "^3.25.6", "form-data": "^4.0.0", "glob": "^11.0.0", "https-proxy-agent": "^7.0.5", 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 fb0eedc29..bb7c0f6e9 100644 --- a/packages/ui/certd-server/src/controller/basic/code-controller.ts +++ b/packages/ui/certd-server/src/controller/basic/code-controller.ts @@ -16,6 +16,9 @@ export class SmsCodeReq { @Rule(RuleType.string().required().max(4)) imgCode: string; + + @Rule(RuleType.string()) + verificationType: string; } export class EmailCodeReq { @@ -32,6 +35,9 @@ export class EmailCodeReq { verificationType: string; } +// 找回密码的验证码有效期 +const FORGOT_PASSWORD_CODE_DURATION = 3 + /** */ @Provide() @@ -48,8 +54,18 @@ export class BasicController extends BaseController { @Body(ALL) body: SmsCodeReq ) { + const opts = { + verificationType: body.verificationType, + verificationCodeLength: undefined, + duration: undefined, + }; + if(body?.verificationType === 'forgotPassword') { + opts.duration = FORGOT_PASSWORD_CODE_DURATION; + // opts.verificationCodeLength = 6; //部分厂商这里会设置参数长度这里就不改了 + } + await this.codeService.checkCaptcha(body.randomStr, body.imgCode); - await this.codeService.sendSmsCode(body.phoneCode, body.mobile, body.randomStr); + await this.codeService.sendSmsCode(body.phoneCode, body.mobile, body.randomStr, opts); return this.ok(null); } @@ -60,6 +76,7 @@ export class BasicController extends BaseController { ) { const opts = { verificationType: body.verificationType, + verificationCodeLength: undefined, title: undefined, content: undefined, duration: undefined, @@ -67,7 +84,8 @@ export class BasicController extends BaseController { if(body?.verificationType === 'forgotPassword') { opts.title = '找回密码'; opts.content = '验证码:${code}。您正在找回密码,请输入验证码并完成操作。如非本人操作请忽略'; - opts.duration = 3; + opts.duration = FORGOT_PASSWORD_CODE_DURATION; + opts.verificationCodeLength = 6; } await this.codeService.checkCaptcha(body.randomStr, body.imgCode); 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 a497504ba..4c60d394c 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 @@ -28,6 +28,8 @@ export class LoginController extends BaseController { if(!sysSettings.selfServicePasswordRetrievalEnabled) { throw new CommonException('暂未开启自助找回'); } + // 找回密码的验证码允许错误次数 + const errorNum = 5; if(body.type === 'email') { this.codeService.checkEmailCode({ @@ -35,6 +37,7 @@ export class LoginController extends BaseController { email: body.input, randomStr: body.randomStr, validateCode: body.validateCode, + errorNum, throwError: true, }); } else if(body.type === 'mobile') { @@ -44,6 +47,7 @@ export class LoginController extends BaseController { randomStr: body.randomStr, phoneCode: body.phoneCode, smsCode: body.validateCode, + errorNum, throwError: true, }); } else { 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 62d108cad..93445b7d4 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 @@ -63,7 +63,8 @@ export class CodeService { randomStr: string, opts?: { duration?: number, - verificationType?: string + verificationType?: string, + verificationCodeLength?: number, }, ) { if (!mobile) { @@ -73,7 +74,8 @@ export class CodeService { throw new Error('randomStr不能为空'); } - const duration = Math.max(Math.floor(Math.min(opts?.duration || 5, 15)), 1); + 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)); const sysSettings = await this.sysSettingsService.getPrivateSettings(); if (!sysSettings.sms?.config?.accessId) { @@ -87,7 +89,7 @@ export class CodeService { accessService: accessGetter, config: smsConfig, }); - const smsCode = randomNumber(4); + const smsCode = randomNumber(verificationCodeLength); await sender.sendSmsCode({ mobile, code: smsCode, @@ -114,7 +116,8 @@ export class CodeService { title?: string, content?: string, duration?: number, - verificationType?: string + verificationType?: string, + verificationCodeLength?: number, }, ) { if (!email) { @@ -132,8 +135,10 @@ export class CodeService { } } - const code = randomNumber(4); - const duration = Math.max(Math.floor(Math.min(opts?.duration || 5, 15)), 1); + 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)); + + const code = randomNumber(verificationCodeLength); const title = `【${siteTitle}】${!!opts?.title ? opts.title : '验证码'}`; const content = !!opts.content ? this.compile(opts.content)({code, duration}) : `您的验证码是${code},请勿泄露`; @@ -154,12 +159,12 @@ export class CodeService { /** * checkSms */ - async checkSmsCode(opts: { mobile: string; phoneCode: string; smsCode: string; randomStr: string; verificationType?: string; throwError: boolean }) { + 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()) { return true; } - return this.checkValidateCode(key, opts.smsCode, opts.throwError); + return this.checkValidateCode(key, opts.smsCode, opts.throwError, opts.errorNum); } buildSmsCodeKey(phoneCode: string, mobile: string, randomStr: string, verificationType?: string) { @@ -169,22 +174,38 @@ export class CodeService { buildEmailCodeKey(email: string, randomStr: string, verificationType?: string) { return ['email', verificationType, email, randomStr].filter(item => !!item).join(':'); } - checkValidateCode(key: string, userCode: string, throwError = true) { + 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) { + const err_num = cache.get(err_num_key) || 0 + if(err_num >= errorNum - 1) { + maxRetryCount = true; + cache.delete(key); + cache.delete(err_num_key); + } else { + cache.set(err_num_key, err_num + 1, { + ttl: 30 * 60 * 1000 + }); + } + } if (throwError) { - throw new CodeErrorException('验证码错误'); + throw new CodeErrorException(!maxRetryCount ? '验证码错误': '验证码错误请获取新的验证码'); } return false; } cache.delete(key); + cache.delete(err_num_key); return true; } - checkEmailCode(opts: { randomStr: string; validateCode: string; email: string; verificationType?: string; throwError: boolean }) { + 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); + return this.checkValidateCode(key, opts.validateCode, opts.throwError, opts.errorNum); } compile(templateString: string) { diff --git a/packages/ui/certd-server/src/plugins/index.ts b/packages/ui/certd-server/src/plugins/index.ts index 50e343e0d..15ca89258 100644 --- a/packages/ui/certd-server/src/plugins/index.ts +++ b/packages/ui/certd-server/src/plugins/index.ts @@ -31,3 +31,5 @@ export * from './plugin-namesilo/index.js' export * from './plugin-proxmox/index.js' export * from './plugin-wangsu/index.js' export * from './plugin-admin/index.js' +export * from './plugin-ksyun/index.js' +export * from './plugin-apisix/index.js' diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apigateway/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apigateway/index.ts new file mode 100644 index 000000000..6bd45e4a3 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apigateway/index.ts @@ -0,0 +1,228 @@ +import {AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput} from '@certd/pipeline'; +import {AliyunAccess, createCertDomainGetterInputDefine, createRemoteSelectInputDefine} from "@certd/plugin-lib"; +import {CertApplyPluginNames, CertInfo} from '@certd/plugin-cert'; +import {optionsUtils} from "@certd/basic/dist/utils/util.options.js"; + +@IsTaskPlugin({ + name: 'DeployCertToAliyunApiGateway', + title: '阿里云-部署证书至API网关', + icon: 'svg:icon-aliyun', + group: pluginGroups.aliyun.key, + desc: '自动部署域名证书至阿里云API网关(APIGateway)', + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, + }, + }, +}) +export class DeployCertToAliyunApiGateway extends AbstractTaskPlugin { + @TaskInput({ + title: '域名证书', + helper: '请选择前置任务输出的域名证书', + component: { + name: 'output-selector', + from: [...CertApplyPluginNames], + }, + required: true, + }) + cert!: CertInfo; + + @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) + certDomains!: string[]; + + @TaskInput({ + title: 'Access授权', + helper: '阿里云授权AccessKeyId、AccessKeySecret', + component: { + name: 'access-selector', + type: 'aliyun', + }, + required: true, + }) + accessId!: string; + + + @TaskInput({ + title: '证书名称', + helper: '上传后将以此名称作为前缀备注', + }) + certName!: string; + + @TaskInput( + createRemoteSelectInputDefine({ + title: '区域', + helper: '请选择区域', + action: DeployCertToAliyunApiGateway.prototype.onGetRegionList.name, + watches: ['certDomains', 'accessId'], + required: true, + component:{ + name:"remote-auto-complete" + } + }) + ) + regionEndpoint!: string; + + @TaskInput( + createRemoteSelectInputDefine({ + title: 'API分组', + helper: '请选择API分组', + action: DeployCertToAliyunApiGateway.prototype.onGetGroupList.name, + watches: ['regionEndpoint', 'accessId'], + required: true, + component:{ + name:"remote-auto-complete" + } + }) + ) + groupId!: string; + + + @TaskInput( + createRemoteSelectInputDefine({ + title: '绑定域名', + helper: '在API分组上配置的绑定域名', + action: DeployCertToAliyunApiGateway.prototype.onGetDomainList.name, + watches: ['groupId','regionEndpoint', 'accessId'], + required: true, + }) + ) + customDomains!: string[]; + + + async onInstance() {} + async execute(): Promise { + this.logger.info('开始部署证书到阿里云Api网关'); + if(!this.customDomains){ + throw new Error('您还未选择域名'); + } + const access = await this.getAccess(this.accessId); + const client = access.getClient(this.regionEndpoint) + + for (const domainName of this.customDomains ) { + this.logger.info(`[${domainName}]开始部署`) + await this.updateCert(client, domainName); + this.logger.info(`[${domainName}]部署成功`) + } + + this.logger.info('部署完成'); + } + + + async updateCert(client: any, domainName: string) { + const ret = await client.doRequest({ + // 接口名称 + action: "SetDomainCertificate", + // 接口版本 + version: "2016-07-14", + data:{ + query:{ + GroupId: this.groupId, + DomainName: domainName, + CertificateName: this.buildCertName(domainName), + CertificateBody: this.cert.crt, + CertificatePrivateKey: this.cert.key + } + } + }) + this.logger.info(`设置${domainName}证书成功:`, ret.RequestId); + } + + + async onGetGroupList(data: any) { + if (!this.accessId) { + throw new Error('请选择Access授权'); + } + if (!this.regionEndpoint) { + throw new Error('请选择区域'); + } + const access = await this.getAccess(this.accessId); + const client = access.getClient(this.regionEndpoint) + const res =await client.doRequest({ + // 接口名称 + action: "DescribeApiGroups", + // 接口版本 + version: "2016-07-14", + data:{} + }) + const list = res?.ApiGroupAttributes?.ApiGroupAttribute; + if (!list || list.length === 0) { + throw new Error('没有数据,您可以手动输入API网关ID'); + } + return list.map((item: any) => { + return { + value: item.GroupId, + label: `${item.GroupName}<${item.GroupId}>`, + }; + }); + } + + async onGetDomainList(data: any) { + if (!this.accessId) { + throw new Error('请选择Access授权'); + } + if (!this.regionEndpoint) { + throw new Error('请选择区域'); + } + if (!this.groupId) { + throw new Error('请选择分组'); + } + const access = await this.getAccess(this.accessId); + + const client = access.getClient(this.regionEndpoint) + + const res =await client.doRequest({ + // 接口名称 + action: "DescribeApiGroup", + // 接口版本 + version: "2016-07-14", + data:{ + query:{ + GroupId: this.groupId + } + } + }) + const list = res?.CustomDomains?.DomainItem; + if (!list || list.length === 0) { + throw new Error('没有数据,您可以手动输入'); + } + const options = list.map((item: any) => { + return { + value: item.DomainName, + label: `${item.DomainName}<${item.CertificateName}>`, + domain: item.DomainName, + }; + }); + return optionsUtils.buildGroupOptions(options, this.certDomains); + } + + + async onGetRegionList(data: any) { + if (!this.accessId) { + throw new Error('请选择Access授权'); + } + const access = await this.getAccess(this.accessId); + + const client = access.getClient("apigateway.cn-hangzhou.aliyuncs.com") + + const res =await client.doRequest({ + // 接口名称 + action: "DescribeRegions", + // 接口版本 + version: "2016-07-14", + data:{} + }) + const list = res.Regions.Region ; + if (!list || list.length === 0) { + throw new Error('没有数据,您可以手动输入'); + } + return list.map((item: any) => { + return { + value: item.RegionEndpoint, + label: item.LocalName, + endpoint: item.RegionEndpoint, + regionId: item.RegionId + }; + }); + } +} +new DeployCertToAliyunApiGateway(); diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts index 9d8c8a3c6..47d8d0164 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts @@ -79,10 +79,10 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin { this.domainName = [this.domainName]; } for (const domainName of this.domainName ) { - this.logger.info(`[${this.domainName}]开始部署`) + this.logger.info(`[${domainName}]开始部署`) const params = await this.buildParams(domainName); await this.doRequest(client, params); - this.logger.info(`[${this.domainName}]部署成功`) + this.logger.info(`[${domainName}]部署成功`) } this.logger.info('部署完成'); diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-fc/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-fc/index.ts index 15cf6f4d2..14286d2aa 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-fc/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-fc/index.ts @@ -1,13 +1,17 @@ import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; import { CertApplyPluginNames, CertInfo, CertReader } from "@certd/plugin-cert"; import { AliyunAccess, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; +import fs from "fs"; +import path from "path"; +import { tmpdir } from "node:os"; +import { sp } from "@certd/basic"; @IsTaskPlugin({ name: 'AliyunDeployCertToFC', title: '阿里云-部署至阿里云FC(3.0)', icon: 'svg:icon-aliyun', group: pluginGroups.aliyun.key, - desc: '部署证书到阿里云函数计算(FC3.0),【注意】证书的加密算法必须选择【pkcs1旧版】', + desc: '部署证书到阿里云函数计算(FC3.0)', needPlus: false, default: { strategy: { @@ -89,7 +93,7 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin { @TaskInput( createRemoteSelectInputDefine({ title: 'FC域名', - helper: "请选择要部署证书的域名\n【注意】证书的加密算法必须选择【pkcs1旧版】(否则会报'private key' has to be in PEM format错误)", + helper: "请选择要部署证书的域名", typeName: 'AliyunDeployCertToFC', action: AliyunDeployCertToFC.prototype.onGetDomainList.name, watches: ['accessId', 'regionId'], @@ -99,9 +103,10 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin { @TaskInput({ title: '域名支持的协议类型', + value: '', component: { name: 'a-select', - value: '', + vModel:"value", options: [ { value: '', label: '保持原样(适用于原来已经开启了HTTPS)' }, { value: 'HTTPS', label: '仅HTTPS' }, @@ -113,6 +118,13 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin { async onInstance() {} + async exec(cmd: string) { + process.env.LANG = "zh_CN.GBK"; + await sp.spawn({ + cmd: cmd, + logger: this.logger, + }); + } async execute(): Promise { this.logger.info('开始部署证书到阿里云'); const access = await this.getAccess(this.accessId); @@ -121,6 +133,32 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin { const $Util = await import('@alicloud/tea-util'); const $OpenApi = await import('@alicloud/openapi-client'); + + + let privateKey = this.cert.key + try{ + // openssl rsa -in private_key.pem -out private_key_pkcs1.pem + const tempDir = path.join(tmpdir(), "certd"); + if (!fs.existsSync(tempDir)) { + fs.mkdirSync(tempDir, { recursive: true }); + } + const keyFileName = this.ctx.utils.id.randomNumber(10); + const tempPem = `${tempDir}/${keyFileName}.pem`; + const tempPkcs1Pem =`${tempDir}/${keyFileName}_pkcs1.pem`; + fs.writeFileSync(tempPem, this.cert.key); + const oldPfxCmd = `openssl rsa -in ${tempPem} -traditional -out ${tempPkcs1Pem}`; + await this.exec(oldPfxCmd); + const fileBuffer = fs.readFileSync(tempPkcs1Pem); + privateKey = fileBuffer.toString(); + fs.unlinkSync(tempPem); + fs.unlinkSync(tempPkcs1Pem); + }catch (e) { + this.logger.warn("私钥转换为PKCS#1格式失败",e); + } + + + + for (const domainName of this.fcDomains) { const params = new $OpenApi.Params({ // 接口名称 @@ -147,7 +185,7 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin { certConfig: { certName: certName, certificate: this.cert.crt, - privateKey: this.cert.key, + privateKey: privateKey, }, }; if (this.protocol) { diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/index.ts index d011be829..87e353bc9 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/index.ts @@ -9,3 +9,4 @@ export * from './deploy-to-slb/index.js'; export * from './deploy-to-fc/index.js'; export * from './deploy-to-esa/index.js'; export * from './deploy-to-vod/index.js'; +export * from './deploy-to-apigateway/index.js'; diff --git a/packages/ui/certd-server/src/plugins/plugin-apisix/access.ts b/packages/ui/certd-server/src/plugins/plugin-apisix/access.ts new file mode 100644 index 000000000..3b9177066 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-apisix/access.ts @@ -0,0 +1,104 @@ +import {AccessInput, BaseAccess, IsAccess} from "@certd/pipeline"; +import {HttpRequestConfig} from "@certd/basic"; +import {CertInfo, CertReader} from "@certd/plugin-cert"; + +/** + */ +@IsAccess({ + name: "apisix", + title: "APISIX授权", + desc: "", + icon: "svg:icon-ksyun" +}) +export class ApisixAccess extends BaseAccess { + + @AccessInput({ + title: "Apisix管理地址", + component: { + placeholder: "http://192.168.11.11:9180", + }, + required: true, + }) + endpoint = ''; + + @AccessInput({ + title: 'ApiKey', + component: { + placeholder: 'ApiKey', + }, + helper: "[参考文档](https://apisix.apache.org/docs/apisix/admin-api/#using-environment-variables)在config中配置admin apiKey", + required: true, + encrypt: true, + }) + apiKey = ''; + + + @AccessInput({ + title: "测试", + component: { + name: "api-test", + action: "TestRequest" + }, + helper: "点击测试接口是否正常" + }) + testRequest = true; + + async onTestRequest() { + await this.getCertList(); + return "ok" + } + + async getCertList(){ + const req = { + url :"/apisix/admin/ssls", + method: "get", + } + return await this.doRequest(req); + } + + async createCert(opts:{cert:CertInfo}){ + const certReader = new CertReader(opts.cert) + const req = { + url :"/apisix/admin/ssls", + method: "post", + data:{ + cert: opts.cert.crt, + key: opts.cert.key, + snis: certReader.getAllDomains() + } + } + return await this.doRequest(req); + } + + async updateCert (opts:{cert:CertInfo,id:string}){ + const certReader = new CertReader(opts.cert) + const req = { + url :`/apisix/admin/ssls/${opts.id}`, + method: "put", + data:{ + cert: opts.cert.crt, + key: opts.cert.key, + snis: certReader.getAllDomains() + } + } + return await this.doRequest(req); + } + + async doRequest(req: HttpRequestConfig){ + const headers = { + "X-API-KEY": this.apiKey, + ...req.headers + }; + return await this.ctx.http.request({ + headers, + baseURL: this.endpoint, + ...req, + logRes: true, + }); + } + + +} + + +new ApisixAccess(); diff --git a/packages/ui/certd-server/src/plugins/plugin-apisix/index.ts b/packages/ui/certd-server/src/plugins/plugin-apisix/index.ts new file mode 100644 index 000000000..02dc3945d --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-apisix/index.ts @@ -0,0 +1,2 @@ +export * from "./plugins/index.js"; +export * from "./access.js"; diff --git a/packages/ui/certd-server/src/plugins/plugin-apisix/plugins/index.ts b/packages/ui/certd-server/src/plugins/plugin-apisix/plugins/index.ts new file mode 100644 index 000000000..705339de6 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-apisix/plugins/index.ts @@ -0,0 +1 @@ +import "./plugin-refresh-cert.js" diff --git a/packages/ui/certd-server/src/plugins/plugin-apisix/plugins/plugin-refresh-cert.ts b/packages/ui/certd-server/src/plugins/plugin-apisix/plugins/plugin-refresh-cert.ts new file mode 100644 index 000000000..66c10e200 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-apisix/plugins/plugin-refresh-cert.ts @@ -0,0 +1,115 @@ +import {IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput} from "@certd/pipeline"; +import {CertApplyPluginNames, CertInfo} from "@certd/plugin-cert"; +import {createCertDomainGetterInputDefine, createRemoteSelectInputDefine} from "@certd/plugin-lib"; +import {ApisixAccess} from "../access.js"; +import {AbstractPlusTaskPlugin} from "@certd/plugin-plus"; + +@IsTaskPlugin({ + //命名规范,插件类型+功能(就是目录plugin-demo中的demo),大写字母开头,驼峰命名 + name: "ApisixRefreshCert", + title: "APISIX-更新证书", + desc: "自动更新APISIX证书", + icon: "svg:icon-lucky", + //插件分组 + group: pluginGroups.cdn.key, + needPlus: true, + default: { + //默认值配置照抄即可 + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed + } + } +}) +//类名规范,跟上面插件名称(name)一致 +export class ApisixRefreshCDNCert extends AbstractPlusTaskPlugin { + //证书选择,此项必须要有 + @TaskInput({ + title: "域名证书", + helper: "请选择前置任务输出的域名证书", + component: { + name: "output-selector", + from: [...CertApplyPluginNames] + } + // required: true, // 必填 + }) + cert!: CertInfo; + + @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) + certDomains!: string[]; + + //授权选择框 + @TaskInput({ + title: "Apisix授权", + component: { + name: "access-selector", + type: "apisix" //固定授权类型 + }, + required: true //必填 + }) + accessId!: string; + // + + @TaskInput( + createRemoteSelectInputDefine({ + title: "证书Id", + helper: "要更新的证书id,如果这里没有,请先给手动绑定一次证书", + action: ApisixRefreshCDNCert.prototype.onGetCertList.name, + pager: false, + search: false + }) + ) + certList!: string[]; + + //插件实例化时执行的方法 + async onInstance() { + } + + //插件执行方法 + async execute(): Promise { + const access = await this.getAccess(this.accessId); + + // await access.createCert({cert:this.cert}) + + for (const certId of this.certList) { + this.logger.info(`----------- 开始更新证书:${certId}`); + + await access.updateCert({ + id: certId, + cert: this.cert + }); + this.logger.info(`----------- 更新证书${certId}成功`); + } + + this.logger.info("部署完成"); + } + + async onGetCertList(data: PageSearch = {}) { + const access = await this.getAccess(this.accessId); + + const res = await access.getCertList() + const list = res.list + if (!list || list.length === 0) { + throw new Error("没有找到证书,你可以直接手动输入id,如果id不存在将自动创建"); + } + + + /** + * certificate-id + * name + * dns-names + */ + const options = list.map((item: any) => { + return { + label: `${item.value.snis[0]}<${item.value.id}>`, + value: item.value.id, + domain: item.value.snis + }; + }); + return { + list: this.ctx.utils.options.buildGroupOptions(options, this.certDomains), + }; + } +} + +//实例化一下,注册插件 +new ApisixRefreshCDNCert(); diff --git a/packages/ui/certd-server/src/plugins/plugin-demo/dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-demo/dns-provider.ts index fb1ccd96b..de14f46b9 100644 --- a/packages/ui/certd-server/src/plugins/plugin-demo/dns-provider.ts +++ b/packages/ui/certd-server/src/plugins/plugin-demo/dns-provider.ts @@ -16,6 +16,7 @@ type DemoRecord = { icon: 'clarity:plugin-line', // 这里是对应的云平台的access类型名称 accessType: 'demo', + order:99, }) export class DemoDnsProvider extends AbstractDnsProvider { access!: DemoAccess; diff --git a/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts b/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts index e35bf5dde..8aea00cbd 100644 --- a/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-host/plugin/upload-to-host/index.ts @@ -39,6 +39,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { { value: 'der', label: 'der,一般用于Apache' }, { value: 'jks', label: 'jks,一般用于JAVA应用' }, { value: 'one', label: '证书私钥一体,crt+key简单合并为一个pem文件' }, + { value: 'p7b', label: 'p7b格式' }, ], }, required: true, @@ -71,7 +72,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { mergeScript: ` return { show: ctx.compute(({form})=>{ - return form.certType === 'pem'; + return form.certType === 'pem' || form.certType === 'p7b' ; }) } `, @@ -169,6 +170,24 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { }) onePath!: string; + @TaskInput({ + title: 'p7b证书保存路径', + helper: '填写应用原本的证书保存路径,路径要包含证书文件名,例如:/tmp/domain_cert.p7b', + component: { + placeholder: '/root/deploy/app/domain_cert.p7b', + }, + mergeScript: ` + return { + show: ctx.compute(({form})=>{ + return form.certType === 'p7b'; + }) + } + `, + required: true, + rules: [{ type: 'filepath' }], + }) + p7bPath!: string; + @TaskInput({ title: '主机登录配置', helper: 'access授权', @@ -277,12 +296,17 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { }) hostOnePath!: string; + @TaskOutput({ + title: 'p7b证书保存路径', + }) + hostP7bPath!: string; + async onInstance() {} async execute(): Promise { const { cert, accessId } = this; - let { crtPath, keyPath, icPath, pfxPath, derPath, jksPath, onePath } = this; + let { crtPath, keyPath, icPath, pfxPath, derPath, jksPath, onePath,p7bPath } = this; const certReader = new CertReader(cert); const executeCmd = async ( script:string)=> { @@ -308,6 +332,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { env['HOST_DER_PATH'] = this.hostDerPath || ''; env['HOST_JKS_PATH'] = this.hostJksPath || ''; env['HOST_ONE_PATH'] = this.hostOnePath || ''; + env['HOST_P7B_PATH'] = this.hostOnePath || ''; } const scripts = script.split('\n'); @@ -320,7 +345,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { } const handle = async (opts: CertReaderHandleContext) => { - const { tmpCrtPath, tmpKeyPath, tmpDerPath, tmpJksPath, tmpPfxPath, tmpIcPath, tmpOnePath } = opts; + const { tmpCrtPath, tmpKeyPath, tmpDerPath, tmpJksPath, tmpPfxPath, tmpIcPath, tmpOnePath ,tmpP7bPath} = opts; if (accessId == null) { this.logger.error('复制到当前主机功能已迁移到 “复制到本机”插件,请换成复制到本机插件'); @@ -392,6 +417,14 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { remotePath: this.onePath, }); } + if (this.p7bPath) { + this.logger.info(`上传p7b证书到主机:${this.p7bPath}`); + p7bPath = this.p7bPath.trim(); + transports.push({ + localPath: tmpP7bPath, + remotePath: this.p7bPath, + }); + } this.logger.info('开始上传文件到服务器'); await sshClient.uploadFiles({ @@ -410,6 +443,7 @@ export class UploadCertToHostPlugin extends AbstractTaskPlugin { this.hostDerPath = derPath; this.hostJksPath = jksPath; this.hostOnePath = onePath; + this.hostP7bPath = p7bPath; }; //执行前置命令 diff --git a/packages/ui/certd-server/src/plugins/plugin-huawei/access/huawei-access.ts b/packages/ui/certd-server/src/plugins/plugin-huawei/access/huawei-access.ts index 13683a568..eeaaa5c62 100644 --- a/packages/ui/certd-server/src/plugins/plugin-huawei/access/huawei-access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-huawei/access/huawei-access.ts @@ -5,6 +5,7 @@ import { IsAccess, AccessInput, BaseAccess } from '@certd/pipeline'; title: '华为云授权', desc: '', icon: 'svg:icon-huawei', + order: 0, }) export class HuaweiAccess extends BaseAccess { @AccessInput({ diff --git a/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/deploy-to-obs/index.ts b/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/deploy-to-obs/index.ts new file mode 100644 index 000000000..02f596fc6 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/deploy-to-obs/index.ts @@ -0,0 +1,185 @@ +import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; +import { HuaweiAccess } from "../../access/index.js"; +import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert"; +import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib"; + +@IsTaskPlugin({ + name: 'HauweiDeployCertToOBS', + title: '华为云-部署证书至OBS', + icon: 'svg:icon-huawei', + group: pluginGroups.huawei.key, + desc: '', + default: { + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed, + }, + }, +}) +export class HauweiDeployCertToOBS extends AbstractTaskPlugin { + @TaskInput({ + title: '域名证书', + helper: '请选择前置任务输出的域名证书\n如果你选择使用ccm证书ID,则需要在[域名管理页面右上角开启SCM授权](https://console.huaweicloud.com/cdn/#/cdn/domain)', + component: { + name: 'output-selector', + from: [...CertApplyPluginNames,'HauweiUploadToCCM'], + }, + required: true, + }) + cert!: CertInfo | string; + + @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) + certDomains!: string[]; + + @TaskInput({ + title: 'Access授权', + helper: '华为云授权AccessKeyId、AccessKeySecret', + component: { + name: 'access-selector', + type: 'huawei', + }, + required: true, + }) + accessId!: string; + + + @TaskInput( + createRemoteSelectInputDefine({ + title: '存储桶', + helper: '请选择存储桶', + action: HauweiDeployCertToOBS.prototype.onGetBucketList.name, + }) + ) + bucketList!: string[]; + + @TaskInput( + createRemoteSelectInputDefine({ + title: '自定义域名', + helper: '请选择自定义域名', + action: HauweiDeployCertToOBS.prototype.onGetDomainList.name, + watches: ['bucketList'], + }) + ) + domainList!: string[]; + + + + async execute(): Promise { + if (!this.cert) { + throw new Error('域名证书不能为空'); + } + this.logger.info('开始部署证书到华为云obs'); + + for (const domainStr of this.domainList) { + const [location, bucket,domain] = domainStr.split('_'); + + await this.setDomainCert({ + location, + bucket, + domain, + cert: this.cert + }); + } + + this.logger.info('部署证书到华为云cdn完成'); + } + + checkRet(ret: any){ + if (ret?.CommonMsg?.Status>300){ + + throw new Error(`【${ret?.CommonMsg?.Code}】${ret?.CommonMsg?.Message}`); + } + } + + + async getObsClient(opts:{region?:string,bucket?:string} = {}) { + const { region,bucket } = opts; + const regionStr = region? `${region}.`: 'cn-north-4.'; + const bucketStr = bucket? `${bucket}.` : ''; + const access = await this.getAccess(this.accessId); + const sdk = await import('esdk-obs-nodejs'); + const obsClient = new sdk.default({ + // 推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险 + // 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html + access_key_id: access.accessKeyId, + secret_access_key: access.accessKeySecret, + // 【可选】如果使用临时AK/SK和SecurityToken访问OBS,同样建议您尽量避免使用硬编码,以降低信息泄露风险。您可以通过环境变量获取访问密钥AK/SK,也可以使用其他外部引入方式传入 + // security_token: process.env.SECURITY_TOKEN, + // endpoint填写Bucket对应的Endpoint, 这里以华北-北京四为例,其他地区请按实际情况填写 + server: `https://${bucketStr}obs.${regionStr}myhuaweicloud.com`, + }); + return obsClient + } + + async onGetBucketList(data: any) { + const obsClient = await this.getObsClient(); + const res = await obsClient.listBuckets({ + QueryLocation:true + }) + + this.checkRet(res) + + const list = res.InterfaceResult?.Buckets + + if (!list || list.length === 0) { + return [] + } + + return list.map(item => { + return { + value: `${item.Location}_${item.BucketName}`, + label: `${item.BucketName}<${item.Location}>`, + }; + }); + } + + async onGetDomainList(data:any) { + if (!this.bucketList || this.bucketList.length === 0) { + return [] + } + const optionList = [] + for (const item of this.bucketList) { + const [location,bucket] = item.split('_') + + const obsClient = await this.getObsClient({region:location}); + const res = await obsClient.getBucketCustomDomain({ + Bucket: bucket, + }) + this.checkRet(res) + + const list = res.InterfaceResult?.Domains + + if (!list || list.length === 0) { + continue + } + const options= list.map(item => { + return { + value: `${location}_${bucket}_${item.DomainName}`, + label: `${item.DomainName}<${bucket}_${location}>`, + domain: item.DomainName, + }; + }); + optionList.push(...options) + } + + return this.ctx.utils.options.buildGroupOptions( optionList,this.certDomains) + } + + async setDomainCert(opts:{location:string,bucket:string,domain:string,cert:string|CertInfo}){ + const {location,bucket,domain,cert} = opts + const obsClient = await this.getObsClient({region:location}); + const params:any = { + Bucket: bucket, + DomainName: domain, + Name: this.buildCertName( domain) + }; + if (typeof cert === 'string'){ + params.CertificateId= cert + }else{ + params.Certificate= cert.crt + params.PrivateKey = cert.key + } + const res = await obsClient.setBucketCustomDomain(params) + this.checkRet(res) + } +} +new HauweiDeployCertToOBS(); diff --git a/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/index.ts b/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/index.ts index 3043939b5..fdf6ded9d 100644 --- a/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-huawei/plugins/index.ts @@ -1,2 +1,3 @@ export * from './deploy-to-cdn/index.js' export * from './upload-to-ccm/index.js' +export * from './deploy-to-obs/index.js' diff --git a/packages/ui/certd-server/src/plugins/plugin-jdcloud/access.ts b/packages/ui/certd-server/src/plugins/plugin-jdcloud/access.ts index ab8231086..ecba2bceb 100644 --- a/packages/ui/certd-server/src/plugins/plugin-jdcloud/access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-jdcloud/access.ts @@ -9,6 +9,7 @@ import {AccessInput, BaseAccess, IsAccess} from '@certd/pipeline'; title: '京东云', desc: '', icon: 'svg:icon-jdcloud', + order: 1, }) export class JDCloudAccess extends BaseAccess { diff --git a/packages/ui/certd-server/src/plugins/plugin-jdcloud/dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-jdcloud/dns-provider.ts index f4fd7d3d1..597cdbada 100644 --- a/packages/ui/certd-server/src/plugins/plugin-jdcloud/dns-provider.ts +++ b/packages/ui/certd-server/src/plugins/plugin-jdcloud/dns-provider.ts @@ -6,7 +6,8 @@ import { JDCloudAccess } from "./access.js"; title: "京东云", desc: "京东云DNS解析提供商", accessType: "jdcloud", - icon: "svg:icon-jdcloud" + icon: "svg:icon-jdcloud", + order:3, }) export class JDCloudDnsProvider extends AbstractDnsProvider { access!: JDCloudAccess; diff --git a/packages/ui/certd-server/src/plugins/plugin-ksyun/access.ts b/packages/ui/certd-server/src/plugins/plugin-ksyun/access.ts new file mode 100644 index 000000000..20c5d5d70 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-ksyun/access.ts @@ -0,0 +1,128 @@ +import {AccessInput, BaseAccess, IsAccess} from "@certd/pipeline"; +import {KsyunClient} from './client.js' +import {CertInfo} from "@certd/plugin-cert"; + +/** + */ +@IsAccess({ + name: "ksyun", + title: "金山云授权", + desc: "", + icon: "svg:icon-ksyun" +}) +export class KsyunAccess extends BaseAccess { + + @AccessInput({ + title: 'AccessKeyID', + component: { + placeholder: 'AccessKeyID', + }, + helper: "[获取密钥](https://uc.console.ksyun.com/pro/iam/#/set/keyManage)", + required: true, + }) + accessKeyId = ''; + @AccessInput({ + title: 'AccessKeySecret', + component: { + placeholder: 'AccessKeySecret', + }, + required: true, + encrypt: true, + }) + accessKeySecret = ''; + + + @AccessInput({ + title: "测试", + component: { + name: "api-test", + action: "TestRequest" + }, + helper: "点击测试接口是否正常" + }) + testRequest = true; + + async onTestRequest() { + const client = await this.getCdnClient() + await this.getCertList({client}) + return "ok" + } + + + async getCertList(opts?:{client:KsyunClient,pageNo?:number;pageSize?:number}) { + const res = await opts.client.doRequest({ + action: "GetCertificates", + version: "2016-09-01", + method:"POST", + url:"/2016-09-01/cert/GetCertificates", + data:{ + PageNum:opts?.pageNo || 1, + PageSize: opts?.pageSize || 30 + } + }) + this.ctx.logger.info(res) + return res + } + + /** + * CertificateId 是 string 证书对应的唯一ID + * CertificateName 是 String 安全证书名称 + * ServerCertificate 是 String 域名对应的安全证书内容 + * PrivateKey + * @param opts + */ + async updateCert(opts:{ + client:KsyunClient, + certId:string, + certName:string, + cert:CertInfo + }){ + const res = await opts.client.doRequest({ + action: "SetCertificate", + version: "2016-09-01", + method:"POST", + url:"/2016-09-01/cert/SetCertificate", + data:{ + CertificateId: opts.certId, + CertificateName: opts.certName, + ServerCertificate: opts.cert.crt, + PrivateKey: opts.cert.key + } + }) + this.ctx.logger.info(res) + return res + } + + async getCert(opts:{client:KsyunClient,certId:string}){ + const res = await opts.client.doRequest({ + action: "GetCertificates", + version: "2016-09-01", + method:"POST", + url:"/2016-09-01/cert/GetCertificates", + data:{ + CertificateId: opts.certId, + } + }) + this.ctx.logger.info(res) + const list = res.Certificates + if (list.length > 0) { + return list[0] + } + throw new Error(`未找到证书:${opts.certId}`) + } + + async getCdnClient() { + return new KsyunClient({ + accessKeyId: this.accessKeyId, + secretAccessKey: this.accessKeySecret, + region: 'cn-beijing-6', + service: 'cdn', + endpoint: 'cdn.api.ksyun.com', + logger: this.ctx.logger, + http: this.ctx.http + }) + } +} + + +new KsyunAccess(); diff --git a/packages/ui/certd-server/src/plugins/plugin-ksyun/client.ts b/packages/ui/certd-server/src/plugins/plugin-ksyun/client.ts new file mode 100644 index 000000000..27bf7952f --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-ksyun/client.ts @@ -0,0 +1,357 @@ +import crypto from 'crypto'; +import querystring from 'querystring' +import {HttpClient, HttpRequestConfig, ILogger} from "@certd/basic"; + +export class KsyunClient { + + accessKeyId: string; + secretAccessKey: string; + region: string; + service: string; + endpoint: string; + logger: ILogger; + http: HttpClient + constructor(opts:{accessKeyId:string; secretAccessKey:string; region?:string; service :string;endpoint :string,logger:ILogger,http:HttpClient}) { + this.accessKeyId = opts.accessKeyId; + this.secretAccessKey = opts.secretAccessKey; + this.region = opts.region || 'cn-beijing-6'; + this.service = opts.service; + this.endpoint =opts.endpoint + this.logger = opts.logger; + this.http = opts.http; + } + + + async doRequest(opts: {action:string;version:string} &HttpRequestConfig){ + const config = this.signRequest({ + method: opts.method || 'GET', + url: opts.url || '/2016-09-01/domain/GetCdnDomains', + baseURL: `https://${this.endpoint}`, + params: opts.params, + headers: { + 'X-Action': opts.action, + 'X-Version': opts.version + }, + data: opts.data + }); + + try{ + return await this.http.request({ + ...config, + data: opts.data + }) + }catch (e) { + this.logger.error(e.request) + if (e.response?.data?.Error?.Message){ + throw new Error(e.response?.data?.Error?.Message) + } + throw e + } + + } + + /** + * 签名请求 + * @param {Object} config Axios 请求配置 + * @returns {Object} 签名后的请求配置 + */ + signRequest(config) { + // 确保有必要的配置 + if (!this.accessKeyId || !this.secretAccessKey) { + throw new Error('AccessKeyId and SecretAccessKey are required'); + } + + // 设置默认值 + config.method = config.method || 'GET'; + config.headers = config.headers || {}; + + // 获取当前时间并设置 X-Amz-Date + const requestDate = this.getRequestDate(); + config.headers['x-amz-date'] = requestDate; + + // 处理不同的请求方法 + let canonicalQueryString = ''; + let hashedPayload = this.hashPayload(config.data || ''); + + if (config.method.toUpperCase() === 'GET') { + // GET 请求 - 参数在 URL 中 + const urlParts = config.url.split('?'); + const path = urlParts[0]; + const query = urlParts[1] || ''; + + // 合并现有查询参数和额外参数 + const queryParams = { + ...querystring.parse(query), + ...(config.params || {}) + }; + + // 生成规范查询字符串 + canonicalQueryString = this.createCanonicalQueryString(queryParams); + config.url = `${path}?${canonicalQueryString}`; + config.params = {}; // 清空 params,因为已经合并到 URL 中 + } else { + // POST/PUT 等请求 - 参数在 body 中 + canonicalQueryString = ''; + if (config.data && typeof config.data === 'object') { + // 如果 data 是对象,转换为 JSON 字符串 + config.data = JSON.stringify(config.data); + hashedPayload = this.hashPayload(config.data); + } + } + + // 生成规范请求 + const canonicalRequest = this.createCanonicalRequest( + config.method, + config.url, + canonicalQueryString, + config.headers, + hashedPayload + ); + + // 生成签名字符串 + const credentialScope = this.createCredentialScope(requestDate); + const stringToSign = this.createStringToSign(requestDate, credentialScope, canonicalRequest); + + // 计算签名 + const signature = this.calculateSignature(requestDate, stringToSign); + + // 生成 Authorization 头 + const signedHeaders = this.getSignedHeaders(config.headers); + const authorizationHeader = this.createAuthorizationHeader( + credentialScope, + signedHeaders, + signature + ); + + // 添加 Authorization 头 + config.headers.Authorization = authorizationHeader; + + return config; + } + + /** + * 获取当前时间 (格式: YYYYMMDD'T'HHMMSS'Z') + * @returns {string} 格式化后的时间字符串 + */ + getRequestDate() { + const now = new Date(); + const year = now.getUTCFullYear(); + const month = String(now.getUTCMonth() + 1).padStart(2, '0'); + const day = String(now.getUTCDate()).padStart(2, '0'); + const hours = String(now.getUTCHours()).padStart(2, '0'); + const minutes = String(now.getUTCMinutes()).padStart(2, '0'); + const seconds = String(now.getUTCSeconds()).padStart(2, '0'); + + return `${year}${month}${day}T${hours}${minutes}${seconds}Z`; + } + + /** + * 哈希 payload + * @param {string} payload 请求体内容 + * @returns {string} 哈希后的16进制字符串 + */ + hashPayload(payload) { + if (typeof payload !== 'string') { + payload = ''; + } + return crypto.createHash('sha256').update(payload).digest('hex').toLowerCase(); + } + + /** + * 创建规范查询字符串 + * @param {Object} params 查询参数对象 + * @returns {string} 规范化的查询字符串 + */ + createCanonicalQueryString(params) { + // 对参数名和值进行 URI 编码 + const encodedParams = {}; + for (const key in params) { + if (params.hasOwnProperty(key)) { + const encodedKey = this.uriEncode(key); + const encodedValue = this.uriEncode(params[key].toString()); + encodedParams[encodedKey] = encodedValue; + } + } + + // 按 ASCII 顺序排序 + const sortedKeys = Object.keys(encodedParams).sort(); + + // 构建查询字符串 + return sortedKeys.map(key => `${key}=${encodedParams[key]}`).join('&'); + } + + /** + * URI 编码 (符合 AWS 规范) + * @param {string} str 要编码的字符串 + * @returns {string} 编码后的字符串 + */ + uriEncode(str) { + return encodeURIComponent(str) + .replace(/[^A-Za-z0-9\-_.~]/g, c => + '%' + c.charCodeAt(0).toString(16).toUpperCase()); + } + + /** + * 创建规范请求 + * @param {string} method HTTP 方法 + * @param {string} url 请求 URL + * @param {string} queryString 查询字符串 + * @param {Object} headers 请求头 + * @param {string} hashedPayload 哈希后的 payload + * @returns {string} 规范化的请求字符串 + */ + createCanonicalRequest(method, url, queryString, headers, hashedPayload) { + // 获取规范 URI + const urlObj = new URL(url, 'http://dummy.com'); // 使用虚拟基础 URL 来解析路径 + const canonicalUri = this.uriEncodePath(urlObj.pathname) || '/'; + + // 获取规范 headers 和 signed headers + const { canonicalHeaders, signedHeaders } = this.createCanonicalHeaders(headers); + + return [ + method.toUpperCase(), + canonicalUri, + queryString, + canonicalHeaders, + signedHeaders, + hashedPayload + ].join('\n'); + } + + /** + * URI 编码路径部分 + * @param {string} path 路径 + * @returns {string} 编码后的路径 + */ + uriEncodePath(path) { + // 分割路径为各个部分,分别编码 + return path.split('/').map(part => this.uriEncode(part)).join('/'); + } + + /** + * 创建规范 headers 和 signed headers + * @param {Object} headers 原始请求头 + * @returns {Object} { canonicalHeaders: string, signedHeaders: string } + */ + createCanonicalHeaders(headers) { + // 处理 headers + const headerMap:any = {}; + + // 标准化 headers + for (const key in headers) { + if (headers.hasOwnProperty(key)) { + const lowerKey = key.toLowerCase(); + let value = headers[key] + if (value) { + value = value.toString().replace(/\s+/g, ' ').trim(); + headerMap[lowerKey] = value; + } + } + } + + // 确保 host 和 x-amz-date 存在 + if (!headerMap.host) { + const url = headers.host ||this.endpoint || 'cdn.api.ksyun.com'; // 默认值 + headerMap.host = url.replace(/^https?:\/\//, '').split('/')[0]; + } + + // 按 header 名称排序 + const sortedHeaderNames = Object.keys(headerMap).sort(); + + // 构建规范 headers + let canonicalHeaders = ''; + for (const name of sortedHeaderNames) { + canonicalHeaders += `${name}:${headerMap[name]}\n`; + } + + // 构建 signed headers + const signedHeaders = sortedHeaderNames.join(';'); + + return { canonicalHeaders, signedHeaders }; + } + + /** + * 获取 signed headers + * @param {Object} headers 请求头 + * @returns {string} signed headers 字符串 + */ + getSignedHeaders(headers) { + const { signedHeaders } = this.createCanonicalHeaders(headers); + return signedHeaders; + } + + /** + * 创建信任状范围 + * @param {string} requestDate 请求日期 (YYYYMMDDTHHMMSSZ) + * @returns {string} 信任状范围字符串 + */ + createCredentialScope(requestDate) { + const date = requestDate.split('T')[0]; + return `${date}/${this.region}/${this.service}/aws4_request`; + } + + /** + * 创建签名字符串 + * @param {string} requestDate 请求日期 + * @param {string} credentialScope 信任状范围 + * @param {string} canonicalRequest 规范请求 + * @returns {string} 签名字符串 + */ + createStringToSign(requestDate, credentialScope, canonicalRequest) { + const algorithm = 'AWS4-HMAC-SHA256'; + const hashedCanonicalRequest = crypto.createHash('sha256') + .update(canonicalRequest) + .digest('hex') + .toLowerCase(); + + return [ + algorithm, + requestDate, + credentialScope, + hashedCanonicalRequest + ].join('\n'); + } + + /** + * 计算签名 + * @param {string} requestDate 请求日期 + * @param {string} stringToSign 签名字符串 + * @returns {string} 签名值 + */ + calculateSignature(requestDate, stringToSign) { + const date = requestDate.split('T')[0]; + const kDate = this.hmac(`AWS4${this.secretAccessKey}`, date); + const kRegion = this.hmac(kDate, this.region); + const kService = this.hmac(kRegion, this.service); + const kSigning = this.hmac(kService, 'aws4_request'); + + return this.hmac(kSigning, stringToSign, 'hex'); + } + + /** + * HMAC-SHA256 计算 + * @param {string|Buffer} key 密钥 + * @param {string} data 数据 + * @param {string} [encoding] 输出编码 + * @returns {string|Buffer} HMAC 结果 + */ + hmac(key, data, encoding = null) { + const hmac = crypto.createHmac('sha256', key); + hmac.update(data); + return encoding ? hmac.digest(encoding) : hmac.digest(); + } + + /** + * 创建 Authorization 头 + * @param {string} credentialScope 信任状范围 + * @param {string} signedHeaders signed headers + * @param {string} signature 签名值 + * @returns {string} Authorization 头值 + */ + createAuthorizationHeader(credentialScope, signedHeaders, signature) { + return `AWS4-HMAC-SHA256 Credential=${this.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`; + } +} + + + diff --git a/packages/ui/certd-server/src/plugins/plugin-ksyun/index.ts b/packages/ui/certd-server/src/plugins/plugin-ksyun/index.ts new file mode 100644 index 000000000..02dc3945d --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-ksyun/index.ts @@ -0,0 +1,2 @@ +export * from "./plugins/index.js"; +export * from "./access.js"; diff --git a/packages/ui/certd-server/src/plugins/plugin-ksyun/plugins/index.ts b/packages/ui/certd-server/src/plugins/plugin-ksyun/plugins/index.ts new file mode 100644 index 000000000..705339de6 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-ksyun/plugins/index.ts @@ -0,0 +1 @@ +import "./plugin-refresh-cert.js" diff --git a/packages/ui/certd-server/src/plugins/plugin-ksyun/plugins/plugin-refresh-cert.ts b/packages/ui/certd-server/src/plugins/plugin-ksyun/plugins/plugin-refresh-cert.ts new file mode 100644 index 000000000..05e906172 --- /dev/null +++ b/packages/ui/certd-server/src/plugins/plugin-ksyun/plugins/plugin-refresh-cert.ts @@ -0,0 +1,137 @@ +import { + AbstractTaskPlugin, + IsTaskPlugin, + Pager, + PageSearch, + pluginGroups, + RunStrategy, + TaskInput +} from "@certd/pipeline"; +import {CertApplyPluginNames, CertInfo} from "@certd/plugin-cert"; +import {createCertDomainGetterInputDefine, createRemoteSelectInputDefine} from "@certd/plugin-lib"; +import {KsyunAccess} from "../access.js"; + +@IsTaskPlugin({ + //命名规范,插件类型+功能(就是目录plugin-demo中的demo),大写字母开头,驼峰命名 + name: "KsyunRefreshCert", + title: "金山云-更新CDN证书", + desc: "金山云自动更新CDN证书", + icon: "svg:icon-lucky", + //插件分组 + group: pluginGroups.cdn.key, + needPlus: false, + default: { + //默认值配置照抄即可 + strategy: { + runStrategy: RunStrategy.SkipWhenSucceed + } + } +}) +//类名规范,跟上面插件名称(name)一致 +export class KsyunRefreshCDNCert extends AbstractTaskPlugin { + //证书选择,此项必须要有 + @TaskInput({ + title: "域名证书", + helper: "请选择前置任务输出的域名证书", + component: { + name: "output-selector", + from: [...CertApplyPluginNames] + } + // required: true, // 必填 + }) + cert!: CertInfo; + + @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) + certDomains!: string[]; + + //授权选择框 + @TaskInput({ + title: "金山云授权", + component: { + name: "access-selector", + type: "ksyun" //固定授权类型 + }, + required: true //必填 + }) + accessId!: string; + // + + @TaskInput( + createRemoteSelectInputDefine({ + title: "证书Id", + helper: "要更新的金山云CDN证书id,如果这里没有,请先给cdn域名手动绑定一次证书", + action: KsyunRefreshCDNCert.prototype.onGetCertList.name, + pager: false, + search: false + }) + ) + certList!: string[]; + + //插件实例化时执行的方法 + async onInstance() { + } + + //插件执行方法 + async execute(): Promise { + const access = await this.getAccess(this.accessId); + + const client = await access.getCdnClient(); + for (const certId of this.certList) { + this.logger.info(`----------- 开始更新证书:${certId}`); + + const oldCert = await access.getCert({ + client, + certId:certId + }) + + await access.updateCert({ + client, + certId: certId, + certName: oldCert.CertificateName, + cert: this.cert + }); + this.logger.info(`----------- 更新证书${certId}成功`); + } + + this.logger.info("部署完成"); + } + + async onGetCertList(data: PageSearch = {}) { + const access = await this.getAccess(this.accessId); + + const client = await access.getCdnClient(); + const pager = new Pager(data) + const res = await access.getCertList({client, + pageNo: pager.pageNo , + pageSize: pager.pageSize + }) + const list = res.Certificates + if (!list || list.length === 0) { + throw new Error("没有找到证书,请先在控制台手动上传一次证书"); + } + + const total = res.TotalCount + + /** + * certificate-id + * name + * dns-names + */ + const options = list.map((item: any) => { + return { + label: `${item.CertificateName}<${item.CertificateId}-${item.ConfigDomainNames}>`, + value: item.CertificateId, + domain: item.ConfigDomainNames + }; + }); + return { + list: this.ctx.utils.options.buildGroupOptions(options, this.certDomains), + total: total, + pageNo: pager.pageNo, + pageSize: pager.pageSize + }; + } +} + +//实例化一下,注册插件 +new KsyunRefreshCDNCert(); diff --git a/packages/ui/certd-server/src/plugins/plugin-rainyun/access.ts b/packages/ui/certd-server/src/plugins/plugin-rainyun/access.ts index b02c7d010..a9d080174 100644 --- a/packages/ui/certd-server/src/plugins/plugin-rainyun/access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-rainyun/access.ts @@ -9,7 +9,8 @@ import { CertInfo } from "@certd/plugin-cert"; name: "rainyun", title: "雨云授权", desc: "https://app.rainyun.com/", - icon: "svg:icon-lucky" + icon: "svg:icon-lucky", + order: 100 }) export class RainyunAccess extends BaseAccess { diff --git a/packages/ui/certd-server/src/plugins/plugin-rainyun/dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-rainyun/dns-provider.ts index c37800efe..1ed0c69c5 100644 --- a/packages/ui/certd-server/src/plugins/plugin-rainyun/dns-provider.ts +++ b/packages/ui/certd-server/src/plugins/plugin-rainyun/dns-provider.ts @@ -7,7 +7,6 @@ import { RainyunAccess } from "./access.js"; desc: "雨云DNS解析提供商", accessType: "rainyun", icon: "svg:icon-lucky", - order: 0 }) export class RainyunDnsProvider extends AbstractDnsProvider { diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-all/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-all/index.ts index ce6adc477..4ef96c2f8 100644 --- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-all/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-all/index.ts @@ -122,7 +122,7 @@ export class DeployCertToTencentAll extends AbstractTaskPlugin { region: this.region, profile: { httpProfile: { - endpoint: 'ssl.tencentcloudapi.com', + endpoint: `ssl.${access.intlDomain()}tencentcloudapi.com`, }, }, }); diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-cdn-v2/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-cdn-v2/index.ts index 25ef29876..ab96b5ca8 100644 --- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-cdn-v2/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-cdn-v2/index.ts @@ -80,6 +80,8 @@ export class TencentDeployCertToCDNv2 extends AbstractTaskPlugin { InstanceIdList: this.domains, }); + await this.ctx.utils.sleep(3000) + this.logger.info('部署成功', res); } @@ -102,7 +104,7 @@ export class TencentDeployCertToCDNv2 extends AbstractTaskPlugin { region: '', profile: { httpProfile: { - endpoint: 'cdn.tencentcloudapi.com', + endpoint: `cdn.${accessProvider.intlDomain()}tencentcloudapi.com`, }, }, }; diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-cdn/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-cdn/index.ts index eb68b3c67..6a5b156b7 100644 --- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-cdn/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-cdn/index.ts @@ -80,7 +80,7 @@ export class DeployToCdnPlugin extends AbstractTaskPlugin { region: '', profile: { httpProfile: { - endpoint: 'cdn.tencentcloudapi.com', + endpoint: `cdn.${accessProvider.intlDomain()}tencentcloudapi.com`, }, }, }; diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts index 1abb57d33..b53acf4e3 100644 --- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts @@ -119,7 +119,7 @@ export class DeployCertToTencentCLB extends AbstractTaskPlugin { region: region, profile: { httpProfile: { - endpoint: 'clb.tencentcloudapi.com', + endpoint: `clb.${accessProvider.intlDomain()}tencentcloudapi.com`, }, }, }; diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-eo/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-eo/index.ts index df3c1074b..a35f981bc 100644 --- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-eo/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-eo/index.ts @@ -1,5 +1,11 @@ -import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline"; -import { TencentAccess } from "@certd/plugin-lib"; +import {AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput} from "@certd/pipeline"; +import { + createCertDomainGetterInputDefine, + createRemoteSelectInputDefine, + TencentAccess, + TencentSslClient +} from "@certd/plugin-lib"; +import {CertApplyPluginNames, CertInfo, CertReader} from "@certd/plugin-cert"; @IsTaskPlugin({ name: 'DeployCertToTencentEO', @@ -14,16 +20,21 @@ import { TencentAccess } from "@certd/plugin-lib"; }, }) export class DeployCertToTencentEO extends AbstractTaskPlugin { + @TaskInput({ - title: '已上传证书ID', - helper: '请选择前置任务上传到腾讯云的证书', + title: '域名证书', + helper: '请选择前置任务输出的域名证书,或者选择前置任务“上传证书到腾讯云”任务的证书ID', component: { name: 'output-selector', - from: 'UploadCertToTencent', + from: [...CertApplyPluginNames, 'UploadCertToTencent'], }, required: true, }) - certId!: string; + cert!: CertInfo | string; + + + @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) + certDomains!: string[]; @TaskInput({ title: 'Access提供者', @@ -36,31 +47,35 @@ export class DeployCertToTencentEO extends AbstractTaskPlugin { }) accessId!: string; - @TaskInput({ + @TaskInput(createRemoteSelectInputDefine({ title: '站点ID', helper: '类似于zone-xxxx的字符串,在站点概览页面左上角,或者,站点列表页面站点名称下方', + action: DeployCertToTencentEO.prototype.onGetZoneList.name, + watches: ['certDomains', 'accessId'], required: true, - }) + component:{ + name:"remote-auto-complete" + } + })) zoneId!: string; + @TaskInput( + createRemoteSelectInputDefine({ + title: '加速域名', + helper: '请选择域名或输入域名', + action: DeployCertToTencentEO.prototype.onGetDomainList.name, + }) + ) + domainNames!: string[]; + + @TaskInput({ title: '证书名称', helper: '证书上传后将以此参数作为名称前缀', }) certName!: string; - @TaskInput({ - title: 'cdn加速域名', - component: { - name: 'a-select', - vModel: 'value', - mode: 'tags', - open: false, - }, - helper: '支持多个域名', - rules: [{ required: true, message: '该项必填' }], - }) - domainNames!: string[]; + // @TaskInput({ // title: "CDN接口", @@ -80,16 +95,41 @@ export class DeployCertToTencentEO extends AbstractTaskPlugin { } async execute(): Promise { - const accessProvider: TencentAccess = (await this.getAccess(this.accessId)) as TencentAccess; + const accessProvider = await this.getAccess(this.accessId) const client = this.getClient(accessProvider); - const params = this.buildParams(); + + const sslClient = new TencentSslClient({ + access:accessProvider, + logger: this.logger, + }); + + let tencentCertId = this.cert as string; + if (typeof this.cert !== 'string') { + const certReader = new CertReader(this.cert); + tencentCertId = await sslClient.uploadToTencent({ + certName: certReader.buildCertName(), + cert: this.cert, + }); + } + + const params:any = { + ZoneId: this.zoneId, + Hosts: this.domainNames, + Mode: 'sslcert', + ServerCertInfo: [ + { + CertId: tencentCertId, + }, + ], + }; await this.doRequest(client, params); } getClient(accessProvider: TencentAccess) { const TeoClient = this.Client; - const endpoint = accessProvider.isIntl()?"teo.intl.tencentcloudapi.com": "teo.tencentcloudapi.com"; + //teo.intl.tencentcloudapi.com + const endpoint = `teo.${accessProvider.intlDomain()}tencentcloudapi.com`; const clientConfig = { credential: { secretId: accessProvider.secretId, @@ -106,18 +146,6 @@ export class DeployCertToTencentEO extends AbstractTaskPlugin { return new TeoClient(clientConfig); } - buildParams() { - return { - ZoneId: this.zoneId, - Hosts: this.domainNames, - Mode: 'sslcert', - ServerCertInfo: [ - { - CertId: this.certId, - }, - ], - }; - } async doRequest(client: any, params: any) { const ret = await client.ModifyHostsCertificate(params); @@ -131,6 +159,57 @@ export class DeployCertToTencentEO extends AbstractTaskPlugin { throw new Error('执行失败:' + ret.Error.Code + ',' + ret.Error.Message); } } + + + + + + async onGetZoneList(data: any) { + if (!this.accessId){ + throw new Error('请选择授权'); + } + const access: TencentAccess = await this.getAccess(this.accessId); + const client = await this.getClient(access); + const res = await client.DescribeZones({ + Limit: 100, + }); + this.checkRet(res); + const list = res.Zones; + if (!list || list.length === 0) { + return []; + } + return list.map((item: any) => { + return { + label: `${item.ZoneName}<${item.ZoneId}>`, + value: item.ZoneId, + }; + }); + } + + async onGetDomainList(data: any) { + if (!this.accessId){ + throw new Error('请选择授权'); + } + const access: TencentAccess = await this.getAccess(this.accessId); + const client = await this.getClient(access); + const res = await client.DescribeAccelerationDomains({ + Limit: 200, + ZoneId: this.zoneId, + }); + this.checkRet(res); + const list = res.AccelerationDomains + if (!list || list.length === 0) { + return []; + } + const options = list.map((item: any) => { + return { + label: item.DomainName, + value: item.DomainName, + domain: item.DomainName + }; + }); + return this.ctx.utils.options.buildGroupOptions(options, this.certDomains); + } } new DeployCertToTencentEO(); diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-live/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-live/index.ts index 92b0f6267..0bd4a63c7 100644 --- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-live/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-live/index.ts @@ -101,7 +101,7 @@ export class TencentDeployCertToLive extends AbstractTaskPlugin { region: '', profile: { httpProfile: { - endpoint: 'live.tencentcloudapi.com', + endpoint: `live.${accessProvider.intlDomain()}tencentcloudapi.com`, }, }, }; diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-tke-ingress/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-tke-ingress/index.ts index 6578a7453..05ad7e932 100644 --- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-tke-ingress/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-tke-ingress/index.ts @@ -210,7 +210,7 @@ export class DeployCertToTencentTKEIngressPlugin extends AbstractTaskPlugin { region, profile: { httpProfile: { - endpoint: "tke.tencentcloudapi.com" + endpoint: `tke.${accessProvider.intlDomain()}tencentcloudapi.com` } } }; diff --git a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/start-instances/index.ts b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/start-instances/index.ts index e4de138e0..5487a2161 100644 --- a/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/start-instances/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/start-instances/index.ts @@ -151,7 +151,7 @@ export class TencentActionInstancesPlugin extends AbstractTaskPlugin { region: this.region, profile: { httpProfile: { - endpoint: 'cvm.tencentcloudapi.com', + endpoint: `cvm.${accessProvider.intlDomain()}tencentcloudapi.com`, }, }, }; diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/access.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/access.ts index e89b4ec95..2a8598357 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/access.ts @@ -9,6 +9,7 @@ import {AccessInput, BaseAccess, IsAccess} from '@certd/pipeline'; title: '火山引擎', desc: '', icon: 'svg:icon-volcengine', + order: 1, }) export class VolcengineAccess extends BaseAccess { diff --git a/packages/ui/certd-server/src/plugins/plugin-volcengine/volcengine-dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-volcengine/volcengine-dns-provider.ts index eb10ec6fd..f3ef15b77 100644 --- a/packages/ui/certd-server/src/plugins/plugin-volcengine/volcengine-dns-provider.ts +++ b/packages/ui/certd-server/src/plugins/plugin-volcengine/volcengine-dns-provider.ts @@ -8,7 +8,8 @@ import { VolcengineAccess } from "./access.js"; title: "火山引擎", desc: "火山引擎DNS解析提供商", accessType: "volcengine", - icon: "svg:icon-volcengine" + icon: "svg:icon-volcengine", + order:2, }) export class VolcengineDnsProvider extends AbstractDnsProvider { client: VolcengineDnsClient; diff --git a/packages/ui/certd-server/src/plugins/plugin-woai/plugins/plugin-deploy-to-cdn.ts b/packages/ui/certd-server/src/plugins/plugin-woai/plugins/plugin-deploy-to-cdn.ts index e0db45a91..0aa82bc98 100644 --- a/packages/ui/certd-server/src/plugins/plugin-woai/plugins/plugin-deploy-to-cdn.ts +++ b/packages/ui/certd-server/src/plugins/plugin-woai/plugins/plugin-deploy-to-cdn.ts @@ -17,14 +17,14 @@ import { CertApplyPluginNames} from '@certd/plugin-cert'; export class WoaiCdnPlugin extends AbstractTaskPlugin { @TaskInput({ title: '接口地址(可留空)', - helper: '请填写我爱云的地址, 默认为 [API](https://console.edeg.sxhjgy.cn) 末尾请不要携带`/`', + helper: '请填写我爱云的地址, 默认为 [API](https://console.edge.ttzi.cn) 末尾请不要携带`/`', component: { name: 'a-input' }, required: false, }) baseApi?: string; @TaskInput({ title: '证书ID', - helper: '请填写 [证书列表](https://console.edge.sxhjgy.cn/site/certificate) 中的证书的ID', + helper: '请填写 [证书列表](https://console.edge.ttzi.cn/site/certificate) 中的证书的ID', component: { name: 'a-input' }, required: true, }) @@ -73,9 +73,9 @@ export class WoaiCdnPlugin extends AbstractTaskPlugin { const { baseApi, certId, cert, accessId } = this; const access = (await this.getAccess(accessId)) as WoaiAccess; // 使用默认值或用户输入的值 - const apiBase = baseApi || 'https://console.edeg.sxhjgy.cn'; + const apiBase = baseApi || 'https://console.edge.ttzi.cn'; // 登录获取token - const loginResponse = await this.doRequestApi(`${apiBase}/account/login`, { + const loginResponse = await this.doRequestApi(`${apiBase}/login`, { username: access.username, password: access.password, }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7cb8f023..3c778db0e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,7 +49,7 @@ importers: packages/core/acme-client: dependencies: '@certd/basic': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../basic '@peculiar/x509': specifier: ^1.11.0 @@ -210,10 +210,10 @@ importers: packages/core/pipeline: dependencies: '@certd/basic': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../basic '@certd/plus-core': - specifier: ^1.36.10 + specifier: ^1.36.17 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.10 + specifier: ^1.36.17 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.10 + specifier: ^1.36.17 version: link:../../core/acme-client '@certd/basic': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../core/basic '@certd/pipeline': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../core/pipeline '@certd/plus-core': - specifier: ^1.36.10 + specifier: ^1.36.17 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.10 + specifier: ^1.36.17 version: link:../../core/acme-client '@certd/basic': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../core/basic '@certd/pipeline': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../core/pipeline '@certd/plugin-lib': - specifier: ^1.36.10 + specifier: ^1.36.17 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.10 + specifier: ^1.36.17 version: link:../../core/basic '@certd/pipeline': - specifier: ^1.36.10 + specifier: ^1.36.17 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.10 + specifier: ^1.36.17 version: link:../../core/basic '@certd/lib-server': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../libs/lib-server '@certd/pipeline': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../core/pipeline '@certd/plugin-plus': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../plugin-plus '@certd/plus-core': - specifier: ^1.36.10 + specifier: ^1.36.17 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.10 + specifier: ^1.36.17 version: link:../../core/basic '@certd/lib-k8s': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../libs/lib-k8s '@certd/pipeline': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../core/pipeline '@certd/plugin-cert': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../plugins/plugin-cert '@certd/plugin-lib': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../plugins/plugin-lib '@certd/plus-core': - specifier: ^1.36.10 + specifier: ^1.36.17 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.10 + specifier: ^1.36.17 version: link:../../core/basic dayjs: specifier: ^1.11.7 @@ -1082,6 +1082,9 @@ importers: '@aws-sdk/s3-request-presigner': specifier: ^3.535.0 version: 3.810.0 + '@certd/vue-js-cron-light': + specifier: ^4.0.14 + version: 4.0.14 '@ctrl/tinycolor': specifier: ^4.1.0 version: 4.1.0 @@ -1121,9 +1124,6 @@ importers: '@vee-validate/zod': specifier: ^4.15.0 version: 4.15.0(vue@3.5.14(typescript@5.8.3))(zod@3.24.4) - '@vue-js-cron/light': - specifier: ^4.0.5 - version: 4.0.10 '@vue/shared': specifier: ^3.5.13 version: 3.5.14 @@ -1297,10 +1297,10 @@ importers: version: 0.1.3(zod@3.24.4) devDependencies: '@certd/lib-iframe': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../libs/lib-iframe '@certd/pipeline': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../core/pipeline '@rollup/plugin-commonjs': specifier: ^25.0.7 @@ -1348,7 +1348,7 @@ importers: specifier: ^2.4.6 version: 2.4.6 autoprefixer: - specifier: ^10.4.20 + specifier: ^10.4.21 version: 10.4.21(postcss@8.5.6) caller-path: specifier: ^4.0.0 @@ -1390,7 +1390,7 @@ importers: specifier: ^12.2.0 version: 12.3.0(less@4.3.0) postcss: - specifier: ^8.5.6 + specifier: ^8.4.35 version: 8.5.6 prettier: specifier: 3.3.3 @@ -1414,7 +1414,7 @@ importers: specifier: ^6.0.4 version: 6.0.4(stylelint@15.11.0(typescript@5.8.3)) tailwindcss: - specifier: ^3.4.17 + specifier: ^3.4.14 version: 3.4.17(ts-node@10.9.2(@types/node@18.19.100)(typescript@5.8.3)) terser: specifier: ^5.29.2 @@ -1432,7 +1432,7 @@ importers: specifier: ^1.4.2 version: 1.5.5(vue@3.5.14(typescript@5.8.3)) vite: - specifier: ^5.4.19 + specifier: ^5.3.1 version: 5.4.19(@types/node@18.19.100)(less@4.3.0)(terser@5.39.1) vite-plugin-compression: specifier: ^0.5.1 @@ -1483,46 +1483,46 @@ importers: specifier: ^3.705.0 version: 3.810.0(aws-crt@1.26.2) '@certd/acme-client': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../core/acme-client '@certd/basic': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../core/basic '@certd/commercial-core': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../pro/commercial-core '@certd/cv4pve-api-javascript': specifier: ^8.4.1 version: 8.4.1 '@certd/jdcloud': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../libs/lib-jdcloud '@certd/lib-huawei': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../libs/lib-huawei '@certd/lib-k8s': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../libs/lib-k8s '@certd/lib-server': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../libs/lib-server '@certd/midway-flyway-js': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../libs/midway-flyway-js '@certd/pipeline': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../core/pipeline '@certd/plugin-cert': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../plugins/plugin-cert '@certd/plugin-lib': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../plugins/plugin-lib '@certd/plugin-plus': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../pro/plugin-plus '@certd/plus-core': - specifier: ^1.36.10 + specifier: ^1.36.17 version: link:../../pro/plus-core '@huaweicloud/huaweicloud-sdk-cdn': specifier: ^3.1.120 @@ -1602,6 +1602,9 @@ importers: dayjs: specifier: ^1.11.7 version: 1.11.13 + esdk-obs-nodejs: + specifier: ^3.25.6 + version: 3.25.6 form-data: specifier: ^4.0.0 version: 4.0.2 @@ -2766,6 +2769,12 @@ packages: '@certd/cv4pve-api-javascript@8.4.1': resolution: {integrity: sha512-jxlRieJmCA0Z9LnwX6Ra6ZekRGJEu8o8RGYoKU0Jjkhc9jm6ChEbVyfE7Iw49/hlpA+2yaHdAXb46au/afCISg==} + '@certd/vue-js-cron-core@6.0.3': + resolution: {integrity: sha512-kqzoAMhYz9j6FGNWEODRYtt4NpUEUwjpkU89z5WVg2tCtOcI5VhwyUGOd8AxiBCRfd6PtXvzuqw85PaOps9wrQ==} + + '@certd/vue-js-cron-light@4.0.14': + resolution: {integrity: sha512-Ebng/VvrN/WpzCMnt0MrsPNgPY3BP6jNYLSUlV5eES6dqWXFpCyIXbNI+qep8fWdn7rNXDW310uUsn3bCPvvEw==} + '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -5159,12 +5168,6 @@ packages: resolution: {integrity: sha512-W8R6+7UIhx06s2kpsJ0KF80IKAiIad71HUKssonMChyI37h5oFnPf6zONcX2ikXc5M8xdOiKFRxoCO8ChEob0g==} engines: {node: '>=12'} - '@vue-js-cron/core@5.4.2': - resolution: {integrity: sha512-y5HrgLJ/0JMXUliOdJZQbB+9UHs7+b7/spe8GFt/a1iCM1ILGKxAoTfbeapoSbF9LxDgw9SsXTgdkmMKWum58A==} - - '@vue-js-cron/light@4.0.10': - resolution: {integrity: sha512-ri8Or3fOkA5pSAihdTcgqrQKzCIa9QcvfilyAqEfEhkK6bTFxQVqJ9LW6tLiNaepHlU8IE68xBLM/kms67le2g==} - '@vue-macros/common@1.16.1': resolution: {integrity: sha512-Pn/AWMTjoMYuquepLZP813BIcq8DTZiNCoaceuNlvaYuOTd8DqBZWc5u0uOMQZMInwME1mdSmmBAcTluiV9Jtg==} engines: {node: '>=16.14.0'} @@ -7387,6 +7390,10 @@ packages: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} + esdk-obs-nodejs@3.25.6: + resolution: {integrity: sha512-bDEznGBoSjqmFNjkL0PvkMzF6o50wa+1PSKQ1tT5CtBP/yw7Egx0c/kIVsu5Raqcip1SjKu7muzslG4xo/skew==} + engines: {node: '>=0.12.7'} + eslint-config-prettier@8.10.0: resolution: {integrity: sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==} hasBin: true @@ -15447,6 +15454,14 @@ snapshots: transitivePeerDependencies: - supports-color + '@certd/vue-js-cron-core@6.0.3': + dependencies: + mustache: 4.2.0 + + '@certd/vue-js-cron-light@4.0.14': + dependencies: + '@certd/vue-js-cron-core': 6.0.3 + '@colors/colors@1.5.0': optional: true @@ -18516,14 +18531,6 @@ snapshots: - buffer - supports-color - '@vue-js-cron/core@5.4.2': - dependencies: - mustache: 4.2.0 - - '@vue-js-cron/light@4.0.10': - dependencies: - '@vue-js-cron/core': 5.4.2 - '@vue-macros/common@1.16.1(vue@3.5.14(typescript@5.8.3))': dependencies: '@vue/compiler-sfc': 3.5.14 @@ -21380,6 +21387,13 @@ snapshots: escape-string-regexp@5.0.0: {} + esdk-obs-nodejs@3.25.6: + dependencies: + fast-xml-parser: 4.5.0 + log4js: 6.9.1 + transitivePeerDependencies: + - supports-color + eslint-config-prettier@8.10.0(eslint@7.32.0): dependencies: eslint: 7.32.0 @@ -21521,13 +21535,13 @@ snapshots: resolve: 1.22.10 semver: 6.3.1 - eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8): + eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8): dependencies: eslint: 7.32.0 prettier: 2.8.8 prettier-linter-helpers: 1.0.0 optionalDependencies: - eslint-config-prettier: 8.10.0(eslint@8.57.0) + eslint-config-prettier: 8.10.0(eslint@7.32.0) eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8): dependencies: @@ -24235,7 +24249,7 @@ snapshots: eslint: 7.32.0 eslint-config-prettier: 8.10.0(eslint@7.32.0) eslint-plugin-node: 11.1.0(eslint@7.32.0) - eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8) + eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8) execa: 5.1.1 inquirer: 7.3.3 json5: 2.2.3