Compare commits

...
27 Commits
Author SHA1 Message Date
Aomsir b99e78f245 fix(tencent): 腾讯云删除即将过期证书失败时不再误报成功 (#820) @aomsir
* fix: 修复腾讯云证书清理失败误报成功

* fix: 完善腾讯云证书清理任务响应校验

* fix: 腾讯云证书清理汇总提交阶段失败并保留证书ID
2026-10-22 22:38:09 +08:00
xiaojunnuo 289028859c build: publish 2026-10-22 15:43:26 +08:00
xiaojunnuo e2da200ddb build: trigger build image 2026-10-22 15:43:16 +08:00
xiaojunnuo 7d285840f9 build: release 2026-10-22 15:37:12 +08:00
xiaojunnuo 5e3958f67a v1.45.5 2026-10-22 15:30:12 +08:00
xiaojunnuo 0898e2bad9 build: prepare to build 2026-10-22 15:24:34 +08:00
xiaojunnuo 4539c10404 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-10-22 15:24:19 +08:00
xiaojunnuo e01a84cc7f chore: 1 2026-10-22 15:24:13 +08:00
xiaojunnuo 32f65bce9f chore: 修改角色删除的限制条件和提示信息 2026-10-22 15:17:49 +08:00
xiaojunnuo 29201d1cf3 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-10-22 15:14:32 +08:00
xiaojunnuo 5326d7dff3 fix(authority): 修复权限校验时角色值错误的问题
将无有效项目ID时的角色值从0改为3,修正权限判断逻辑
2026-10-22 15:14:19 +08:00
xiaojunnuo cc1c9c7fe7 chore: 降低api token的权限 2026-10-22 15:13:07 +08:00
xiaojunnuo b601b503e2 chore: 企业版鉴权时校验projectId非空 2026-10-22 15:09:01 +08:00
xiaojunnuo f50651ef91 fix: 非企业模式下禁止API密钥指定项目ID并校验权限【安全加强】 2026-10-22 15:08:26 +08:00
xiaojunnuo 9f18453d2b v1.45.4 2026-10-22 14:15:31 +08:00
xiaojunnuo 9eb22d3150 build: prepare to build 2026-10-22 14:09:50 +08:00
Zephyr Jin 7ac9101266 fix(pipeline): 修复定时触发器cron归一化截取位置错误导致日期字段被吃掉的问题 (#813)
registerCron 中 "* * " 前缀长4个字符却使用 substring(5),多跳的字符
吃掉了日期字段首字符:* * 15 * * 被归一化为 0 0 5 * *(15号变5号),
* * 1 * * 等则产出4字段非法cron,被解析器抛错后静默跳过注册,定时
任务不创建。改用 substring(4),与同函数第二分支及客户端 cron-editor
的字段替换语义保持一致,并补充回归测试。
2026-10-22 14:03:35 +08:00
Aomsir 113c96b866 fix(docker): 将构建和运行镜像的 Node.js 固定到 22.23.4 安全版本 (#817) 2026-10-22 14:03:21 +08:00
xiaojunnuo 3f118d9c21 build: release 2026-10-19 10:38:58 +08:00
xiaojunnuo 646a009832 build: publish 2026-10-19 10:35:20 +08:00
xiaojunnuo bacb141b3b build: trigger build image 2026-10-19 10:35:09 +08:00
xiaojunnuo 9b1cf353b9 v1.45.3 2026-10-19 10:34:11 +08:00
xiaojunnuo 2b66faef90 build: prepare to build 2026-10-19 10:19:13 +08:00
xiaojunnuo 28e4f70e49 fix: 修复修改密码时校验表单字段的错误
修复了修改密码功能中,访问表单实例的属性路径错误,导致无法正确校验确认新密码字段的问题。
2026-10-19 10:13:10 +08:00
xiaojunnuo 93031e7f53 build: release 2026-10-18 21:42:30 +08:00
xiaojunnuo d60635f766 build: publish 2026-10-18 21:20:24 +08:00
xiaojunnuo f5df69fbc9 build: trigger build image 2026-10-18 21:20:13 +08:00
41 changed files with 1011 additions and 104 deletions
+20
View File
@@ -3,6 +3,26 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
### Bug Fixes
* 非企业模式下禁止API密钥指定项目ID并校验权限【安全加强】 ([f50651e](https://github.com/certd/certd/commit/f50651ef91813b529b8498ddefe0f66b8b7946da))
* **authority:** 修复权限校验时角色值错误的问题 ([5326d7d](https://github.com/certd/certd/commit/5326d7dff3967974d70090b45f8580a155c1a032))
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
### Bug Fixes
* **docker:** 将构建和运行镜像的 Node.js 固定到 22.23.4 安全版本 ([#817](https://github.com/certd/certd/issues/817)) ([113c96b](https://github.com/certd/certd/commit/113c96b866e2ec5c85e37e42150445ed8b0eefbe))
* **pipeline:** 修复定时触发器cron归一化截取位置错误导致日期字段被吃掉的问题 ([#813](https://github.com/certd/certd/issues/813)) ([7ac9101](https://github.com/certd/certd/commit/7ac9101266f2821fb13ff68ea6d36969c9485675))
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
### Bug Fixes
* 修复修改密码时校验表单字段的错误 ([28e4f70](https://github.com/certd/certd/commit/28e4f70e494f7c56202a8ca4708718c836fff702))
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
### Bug Fixes
+33
View File
@@ -3,6 +3,39 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
### Bug Fixes
* 非企业模式下禁止API密钥指定项目ID并校验权限【安全加强】 ([f50651e](https://github.com/certd/certd/commit/f50651ef91813b529b8498ddefe0f66b8b7946da))
* **authority:** 修复权限校验时角色值错误的问题 ([5326d7d](https://github.com/certd/certd/commit/5326d7dff3967974d70090b45f8580a155c1a032))
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
### Bug Fixes
* **docker:** 将构建和运行镜像的 Node.js 固定到 22.23.4 安全版本 ([#817](https://github.com/certd/certd/issues/817)) ([113c96b](https://github.com/certd/certd/commit/113c96b866e2ec5c85e37e42150445ed8b0eefbe))
* **pipeline:** 修复定时触发器cron归一化截取位置错误导致日期字段被吃掉的问题 ([#813](https://github.com/certd/certd/issues/813)) ([7ac9101](https://github.com/certd/certd/commit/7ac9101266f2821fb13ff68ea6d36969c9485675))
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
### Bug Fixes
* 修复修改密码时校验表单字段的错误 ([28e4f70](https://github.com/certd/certd/commit/28e4f70e494f7c56202a8ca4708718c836fff702))
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
### Bug Fixes
* 修复腾讯云CLB部署证书逻辑 ([a67d5bc](https://github.com/certd/certd/commit/a67d5bca2dd12e157e45ec1b8abff54efcbdfa84))
### Performance Improvements
* 为PEM输入组件添加文件拖拽上传功能 ([cfd3844](https://github.com/certd/certd/commit/cfd384485f212cdea0b08e4b35a3f19a1acfe026))
* 新增更新金山云KCM SLB证书插件 ([62751be](https://github.com/certd/certd/commit/62751bee7a5e00245bb4dd840a5f4ea35565616a))
* 支持ikuai部署证书 ([5f83693](https://github.com/certd/certd/commit/5f83693423ec5ccd08d7bb03e4fb8315dbb8b931))
* **baota:** 宝塔站点全自动部署插件支持部署docker站点 ([61ef971](https://github.com/certd/certd/commit/61ef971518eeccbc31b09c06ec1b48589929d6bb))
## [1.45.1](https://github.com/certd/certd/compare/v1.45.0...v1.45.1) (2026-10-14)
### Bug Fixes
+1 -1
View File
@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.45.2"
"version": "1.45.5"
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/publishlab/node-acme-client/compare/v1.45.4...v1.45.5) (2026-10-22)
**Note:** Version bump only for package @certd/acme-client
## [1.45.4](https://github.com/publishlab/node-acme-client/compare/v1.45.3...v1.45.4) (2026-10-22)
**Note:** Version bump only for package @certd/acme-client
## [1.45.3](https://github.com/publishlab/node-acme-client/compare/v1.45.2...v1.45.3) (2026-10-19)
**Note:** Version bump only for package @certd/acme-client
## [1.45.2](https://github.com/publishlab/node-acme-client/compare/v1.45.1...v1.45.2) (2026-10-18)
**Note:** Version bump only for package @certd/acme-client
+3 -3
View File
@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.45.2",
"version": "1.45.5",
"type": "module",
"module": "./dist/index.js",
"main": "./dist/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.45.2",
"@certd/basic": "^1.45.5",
"@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5",
"axios": "^1.9.0",
@@ -77,5 +77,5 @@
"bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues"
},
"gitHead": "e9b9839d79532e4961dc0de5c097f81235e557f6"
"gitHead": "5e3958f67a1249b372ebebf45540a655a12a4824"
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
**Note:** Version bump only for package @certd/basic
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
**Note:** Version bump only for package @certd/basic
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
**Note:** Version bump only for package @certd/basic
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
### Performance Improvements
+1 -1
View File
@@ -1 +1 @@
21:07
15:24
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.45.2",
"version": "1.45.5",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -54,5 +54,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "e9b9839d79532e4961dc0de5c097f81235e557f6"
"gitHead": "5e3958f67a1249b372ebebf45540a655a12a4824"
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
**Note:** Version bump only for package @certd/pipeline
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
**Note:** Version bump only for package @certd/pipeline
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
**Note:** Version bump only for package @certd/pipeline
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
### Performance Improvements
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.45.2",
"version": "1.45.5",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -21,8 +21,8 @@
"lint": "eslint --fix --ext .ts src"
},
"dependencies": {
"@certd/basic": "^1.45.2",
"@certd/plus-core": "^1.45.2",
"@certd/basic": "^1.45.5",
"@certd/plus-core": "^1.45.5",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"reflect-metadata": "^0.2.2"
@@ -51,5 +51,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "e9b9839d79532e4961dc0de5c097f81235e557f6"
"gitHead": "5e3958f67a1249b372ebebf45540a655a12a4824"
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
**Note:** Version bump only for package @certd/lib-huawei
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
**Note:** Version bump only for package @certd/lib-huawei
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
**Note:** Version bump only for package @certd/lib-huawei
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
**Note:** Version bump only for package @certd/lib-huawei
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.45.2",
"version": "1.45.5",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -31,5 +31,5 @@
"prettier": "3.3.3",
"tslib": "^2.8.1"
},
"gitHead": "e9b9839d79532e4961dc0de5c097f81235e557f6"
"gitHead": "5e3958f67a1249b372ebebf45540a655a12a4824"
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
**Note:** Version bump only for package @certd/lib-iframe
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
**Note:** Version bump only for package @certd/lib-iframe
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
**Note:** Version bump only for package @certd/lib-iframe
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
**Note:** Version bump only for package @certd/lib-iframe
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.45.2",
"version": "1.45.5",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -37,5 +37,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "e9b9839d79532e4961dc0de5c097f81235e557f6"
"gitHead": "5e3958f67a1249b372ebebf45540a655a12a4824"
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
**Note:** Version bump only for package @certd/jdcloud
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
**Note:** Version bump only for package @certd/jdcloud
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
**Note:** Version bump only for package @certd/jdcloud
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
**Note:** Version bump only for package @certd/jdcloud
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/jdcloud",
"version": "1.45.2",
"version": "1.45.5",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
@@ -63,5 +63,5 @@
"fetch"
]
},
"gitHead": "e9b9839d79532e4961dc0de5c097f81235e557f6"
"gitHead": "5e3958f67a1249b372ebebf45540a655a12a4824"
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
**Note:** Version bump only for package @certd/lib-k8s
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
**Note:** Version bump only for package @certd/lib-k8s
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
**Note:** Version bump only for package @certd/lib-k8s
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
**Note:** Version bump only for package @certd/lib-k8s
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-k8s",
"private": false,
"version": "1.45.2",
"version": "1.45.5",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -21,7 +21,7 @@
"lint": "eslint --fix --ext .ts src"
},
"dependencies": {
"@certd/basic": "^1.45.2",
"@certd/basic": "^1.45.5",
"@kubernetes/client-node": "0.21.0"
},
"devDependencies": {
@@ -38,5 +38,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "e9b9839d79532e4961dc0de5c097f81235e557f6"
"gitHead": "5e3958f67a1249b372ebebf45540a655a12a4824"
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
**Note:** Version bump only for package @certd/lib-server
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
**Note:** Version bump only for package @certd/lib-server
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
**Note:** Version bump only for package @certd/lib-server
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
**Note:** Version bump only for package @certd/lib-server
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.45.2",
"version": "1.45.5",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -29,11 +29,11 @@
],
"license": "AGPL",
"dependencies": {
"@certd/acme-client": "^1.45.2",
"@certd/basic": "^1.45.2",
"@certd/pipeline": "^1.45.2",
"@certd/plugin-lib": "^1.45.2",
"@certd/plus-core": "^1.45.2",
"@certd/acme-client": "^1.45.5",
"@certd/basic": "^1.45.5",
"@certd/pipeline": "^1.45.5",
"@certd/plugin-lib": "^1.45.5",
"@certd/plus-core": "^1.45.5",
"@midwayjs/cache": "3.14.0",
"@midwayjs/core": "3.20.11",
"@midwayjs/i18n": "3.20.13",
@@ -69,5 +69,5 @@
"typeorm": "^0.3.20",
"typescript": "^5.4.2"
},
"gitHead": "e9b9839d79532e4961dc0de5c097f81235e557f6"
"gitHead": "5e3958f67a1249b372ebebf45540a655a12a4824"
}
@@ -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.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
**Note:** Version bump only for package @certd/midway-flyway-js
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
"version": "1.45.2",
"version": "1.45.5",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -52,5 +52,5 @@
"typeorm": "^0.3.20",
"typescript": "^5.4.2"
},
"gitHead": "e9b9839d79532e4961dc0de5c097f81235e557f6"
"gitHead": "5e3958f67a1249b372ebebf45540a655a12a4824"
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
**Note:** Version bump only for package @certd/plugin-cert
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
**Note:** Version bump only for package @certd/plugin-cert
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
**Note:** Version bump only for package @certd/plugin-cert
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
**Note:** Version bump only for package @certd/plugin-cert
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.45.2",
"version": "1.45.5",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -20,7 +20,7 @@
"lint": "eslint --fix --ext .ts src"
},
"dependencies": {
"@certd/plugin-lib": "^1.45.2"
"@certd/plugin-lib": "^1.45.5"
},
"devDependencies": {
"@types/chai": "^4.3.12",
@@ -38,5 +38,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "e9b9839d79532e4961dc0de5c097f81235e557f6"
"gitHead": "5e3958f67a1249b372ebebf45540a655a12a4824"
}
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
**Note:** Version bump only for package @certd/plugin-lib
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
**Note:** Version bump only for package @certd/plugin-lib
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
**Note:** Version bump only for package @certd/plugin-lib
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
**Note:** Version bump only for package @certd/plugin-lib
+5 -5
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
"version": "1.45.2",
"version": "1.45.5",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -17,9 +17,9 @@
"lint": "eslint --fix --ext .ts src"
},
"dependencies": {
"@certd/acme-client": "^1.45.2",
"@certd/basic": "^1.45.2",
"@certd/pipeline": "^1.45.2",
"@certd/acme-client": "^1.45.5",
"@certd/basic": "^1.45.5",
"@certd/pipeline": "^1.45.5",
"dayjs": "^1.11.7",
"jszip": "^3.10.1",
"lodash-es": "^4.17.21",
@@ -45,5 +45,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "e9b9839d79532e4961dc0de5c097f81235e557f6"
"gitHead": "5e3958f67a1249b372ebebf45540a655a12a4824"
}
+6 -6
View File
@@ -1,13 +1,13 @@
ARG base_type=alpine
# 根据 base_type 参数选择基础镜像系列
FROM --platform=linux/amd64 node:22-alpine AS base-amd64-alpine
FROM --platform=linux/arm64 node:22-alpine AS base-arm64-alpine
FROM --platform=linux/arm/v7 node:22-alpine AS base-arm-v7-alpine
FROM --platform=linux/amd64 node:22.23.4-alpine AS base-amd64-alpine
FROM --platform=linux/arm64 node:22.23.4-alpine AS base-arm64-alpine
FROM --platform=linux/arm/v7 node:22.23.4-alpine AS base-arm-v7-alpine
FROM --platform=linux/amd64 node:22-trixie-slim AS base-amd64-slim
FROM --platform=linux/arm64 node:22-trixie-slim AS base-arm64-slim
FROM --platform=linux/arm/v7 node:22-alpine AS base-arm-v7-slim
FROM --platform=linux/amd64 node:22.23.4-trixie-slim AS base-amd64-slim
FROM --platform=linux/arm64 node:22.23.4-trixie-slim AS base-arm64-slim
FROM --platform=linux/arm/v7 node:22.23.4-alpine AS base-arm-v7-slim
FROM base-${TARGETARCH}${TARGETVARIANT:+-}${TARGETVARIANT}-${base_type} AS builder
+14
View File
@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
**Note:** Version bump only for package @certd/ui-client
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
**Note:** Version bump only for package @certd/ui-client
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
### Bug Fixes
* 修复修改密码时校验表单字段的错误 ([28e4f70](https://github.com/certd/certd/commit/28e4f70e494f7c56202a8ca4708718c836fff702))
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
### Bug Fixes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/ui-client",
"version": "1.45.2",
"version": "1.45.5",
"private": true,
"scripts": {
"dev": "vite --open",
@@ -104,8 +104,8 @@
"zod-defaults": "^0.1.3"
},
"devDependencies": {
"@certd/lib-iframe": "^1.45.2",
"@certd/pipeline": "^1.45.2",
"@certd/lib-iframe": "^1.45.5",
"@certd/pipeline": "^1.45.5",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12",
@@ -35,7 +35,7 @@ const validatePass1 = async (rule: any, value: any) => {
}
const formData = passwordFormRef.value.getFormData();
if (formData.confirmNewPassword !== "") {
passwordFormRef.value.formRef.formRef.validateFields(["confirmNewPassword"]);
passwordFormRef.value.formRef.value.formRef.validateFields(["confirmNewPassword"]);
}
if (formData.password === formData.newPassword) {
throw new Error(t("certd.auth.newPasswordNotSameOld"));
+17
View File
@@ -3,6 +3,23 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.45.5](https://github.com/certd/certd/compare/v1.45.4...v1.45.5) (2026-10-22)
### Bug Fixes
* 非企业模式下禁止API密钥指定项目ID并校验权限【安全加强】 ([f50651e](https://github.com/certd/certd/commit/f50651ef91813b529b8498ddefe0f66b8b7946da))
* **authority:** 修复权限校验时角色值错误的问题 ([5326d7d](https://github.com/certd/certd/commit/5326d7dff3967974d70090b45f8580a155c1a032))
## [1.45.4](https://github.com/certd/certd/compare/v1.45.3...v1.45.4) (2026-10-22)
### Bug Fixes
* **pipeline:** 修复定时触发器cron归一化截取位置错误导致日期字段被吃掉的问题 ([#813](https://github.com/certd/certd/issues/813)) ([7ac9101](https://github.com/certd/certd/commit/7ac9101266f2821fb13ff68ea6d36969c9485675))
## [1.45.3](https://github.com/certd/certd/compare/v1.45.2...v1.45.3) (2026-10-19)
**Note:** Version bump only for package @certd/ui-server
## [1.45.2](https://github.com/certd/certd/compare/v1.45.1...v1.45.2) (2026-10-18)
### Bug Fixes
+14 -14
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/ui-server",
"version": "1.45.2",
"version": "1.45.5",
"description": "fast-server base midway",
"private": true,
"type": "module",
@@ -43,20 +43,20 @@
"lint1": "eslint --fix --ext .ts src"
},
"dependencies": {
"@certd/acme-client": "^1.45.2",
"@certd/basic": "^1.45.2",
"@certd/commercial-core": "^1.45.2",
"@certd/acme-client": "^1.45.5",
"@certd/basic": "^1.45.5",
"@certd/commercial-core": "^1.45.5",
"@certd/cv4pve-api-javascript": "^8.4.2",
"@certd/jdcloud": "^1.45.2",
"@certd/lib-huawei": "^1.45.2",
"@certd/lib-k8s": "^1.45.2",
"@certd/lib-server": "^1.45.2",
"@certd/midway-flyway-js": "^1.45.2",
"@certd/pipeline": "^1.45.2",
"@certd/plugin-cert": "^1.45.2",
"@certd/plugin-lib": "^1.45.2",
"@certd/plugin-plus": "^1.45.2",
"@certd/plus-core": "^1.45.2",
"@certd/jdcloud": "^1.45.5",
"@certd/lib-huawei": "^1.45.5",
"@certd/lib-k8s": "^1.45.5",
"@certd/lib-server": "^1.45.5",
"@certd/midway-flyway-js": "^1.45.5",
"@certd/pipeline": "^1.45.5",
"@certd/plugin-cert": "^1.45.5",
"@certd/plugin-lib": "^1.45.5",
"@certd/plugin-plus": "^1.45.5",
"@certd/plus-core": "^1.45.5",
"@koa/cors": "^5.0.0",
"@midwayjs/bootstrap": "3.20.11",
"@midwayjs/cache": "3.14.0",
@@ -62,8 +62,8 @@ export class RoleController extends CrudController<RoleService> {
@Query("id")
id: number
) {
if (id === 1) {
throw new Error("不能删除默认的管理员角色");
if (id <= 3) {
throw new Error("不能删除系统默认角色");
}
const res = await super.delete(id);
await this.auditLog({
@@ -1,5 +1,5 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core";
import { Constants, CrudController } from "@certd/lib-server";
import { Constants, CrudController, isEnterprise } from "@certd/lib-server";
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
import { OpenKeyService } from "../../../modules/open/service/open-key-service.js";
import { ApiTags } from "@midwayjs/swagger";
@@ -60,6 +60,10 @@ export class OpenKeyController extends CrudController<OpenKeyService> {
const { projectId, userId } = await this.getProjectUserIdRead();
body.projectId = projectId;
body.userId = userId;
if (!isEnterprise()) {
//如果是非企业模式,禁止指定projectId
delete body.projectId;
}
const res = await this.service.add(body);
this.auditLog({
content: `新增了API密钥(ID:${res.id}, scope:${body.scope}, remark:${body.remark})`,
@@ -1,7 +1,7 @@
import { Init, Inject, MidwayWebRouterService, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { IMidwayKoaContext, IWebMiddleware, NextFunction } from "@midwayjs/koa";
import jwt from "jsonwebtoken";
import { Constants, SysPrivateSettings, SysSettingsService } from "@certd/lib-server";
import { Constants, isEnterprise, SysPrivateSettings, SysSettingsService } from "@certd/lib-server";
import { logger } from "@certd/basic";
import { AuthService } from "../modules/sys/authority/service/auth-service.js";
import { OpenKeyService } from "../modules/open/service/open-key-service.js";
@@ -183,11 +183,18 @@ export class AuthorityMiddleware implements IWebMiddleware {
if (!openKey) {
return null;
}
//校验 openKey
const openKeyRes = await this.openKeyService.verifyOpenKey(openKey);
let roles = [1];
if (!openKeyRes.projectId || openKeyRes.projectId <= 0) {
let roles = []
if (isEnterprise()) {
if (!openKeyRes.projectId || openKeyRes.projectId <= 0) {
throw new Error("projectId不能为空");
}
roles = [3]
}else {
if (openKeyRes.projectId !=null) {
throw new Error("对不起,您无权进行此操作");
}
roles = await this.roleService.getRoleIdsByUserId(openKeyRes.userId);
}
ctx.user = { id: openKeyRes.userId, roles, projectId: openKeyRes.projectId };
@@ -83,4 +83,50 @@ describe("PipelineService", () => {
};
assert.doesNotThrow(() => (service as any).checkTriggers(pipeline));
});
describe("registerCron normalizes every-minute cron like the client editor does", () => {
function createService() {
const service = new PipelineService();
const registered: any[] = [];
service.cron = {
remove() {},
register(req: any) {
registered.push(req);
},
getTaskSize() {
return registered.length;
},
} as any;
return { service, registered };
}
it('normalizes "* * 15 * *" to "0 0 15 * *" without eating the first digit of the day field', () => {
//与客户端 cron-editor 的归一化结果保持一致:分钟与小时均为 * 时,置为 0 0,其余字段原样保留
const { service, registered } = createService();
service.registerCron(1, 1, { id: "t1", type: "timer", props: { cron: "* * 15 * *" } });
assert.equal(registered.length, 1);
assert.equal(registered[0].cron, "0 0 15 * *");
});
it('keeps registration for "* * 1 * *" (single-digit day must not be dropped)', () => {
const { service, registered } = createService();
service.registerCron(1, 1, { id: "t1", type: "timer", props: { cron: "* * 1 * *" } });
assert.equal(registered.length, 1);
assert.equal(registered[0].cron, "0 0 1 * *");
});
it('normalizes "* * * * *" to "0 0 * * *"', () => {
const { service, registered } = createService();
service.registerCron(1, 1, { id: "t1", type: "timer", props: { cron: "* * * * *" } });
assert.equal(registered.length, 1);
assert.equal(registered[0].cron, "0 0 * * *");
});
it('keeps the existing correct normalization for "* 12 * * *"', () => {
const { service, registered } = createService();
service.registerCron(1, 1, { id: "t1", type: "timer", props: { cron: "* 12 * * *" } });
assert.equal(registered.length, 1);
assert.equal(registered[0].cron, "0 12 * * *");
});
});
});
@@ -590,7 +590,8 @@ export class PipelineService extends BaseService<PipelineEntity> {
}
cron = cron.trim();
if (cron.startsWith("* * ")) {
cron = "0 0 " + cron.substring(5);
// "* * " 为4个字符,需要跳过整个前缀,否则会吃掉第3个字段(日)的首字符
cron = "0 0 " + cron.substring(4);
}
if (cron.startsWith("* ")) {
cron = "0 " + cron.substring(2);
@@ -0,0 +1,544 @@
import "reflect-metadata";
import assert from "node:assert/strict";
import esmock from "esmock";
import { AbstractTaskPlugin, ConcurrencyStrategy, Executor, pluginRegistry, ResultType, RunStrategy } from "@certd/pipeline";
import { utils } from "@certd/basic";
import { tmpdir } from "node:os";
const pluginName = "TencentDeleteExpiringCert";
const originalNow = Date.now;
const originalSleep = utils.sleep;
let originalRegistration: any;
async function createFixture(statuses: Array<number | string | undefined> = [1]) {
const certificates = statuses.map((status, index) => ({
CertificateId: `cert-${index}`,
CertEndTime: "2000-01-01 00:00:00",
Alias: "测试证书",
Domain: "example.invalid",
}));
const taskIds = certificates.map((cert, index) => ({ TaskId: `task-${index}` }));
const results = statuses.map((status, index) => ({ TaskId: `task-${index}`, CertId: `cert-${index}`, Status: status }));
const state: any = {
now: originalNow(),
certificates,
deleteResponse: { CertTaskIds: taskIds },
responses: [{ DeleteTaskResult: results }],
pollCount: 0,
failAction: "",
requestError: new Error("模拟接口异常"),
};
const logs: string[] = [];
const calls: Array<{ action: string; params: any }> = [];
const sleeps: number[] = [];
const controller = new AbortController();
const recordLog = (...args: any[]) => {
const parts = args.map(String);
const line = parts.join(" ");
logs.push(line);
};
const logger = { info: recordLog, error: recordLog, warn: recordLog, debug() {} };
const sleep = async (ms: number) => {
sleeps.push(ms);
state.now += ms;
};
class MockSslClient {
async DescribeCertificates(params: any) {
const copiedParams = structuredClone(params);
calls.push({ action: "DescribeCertificates", params: copiedParams });
if (state.failAction === "DescribeCertificates") {
throw state.requestError;
}
return { Certificates: state.certificates };
}
async doRequest(action: string, params: any) {
const copiedParams = structuredClone(params);
calls.push({ action, params: copiedParams });
if (state.failAction === action) {
throw state.requestError;
}
if (action === "DeleteCertificates") {
return state.deleteResponse;
}
assert.equal(action, "DescribeDeleteCertificatesTaskResult");
state.now += 1000;
const index = Math.min(state.pollCount, state.responses.length - 1);
state.pollCount++;
assert.ok(state.pollCount <= 100, "轮询必须有界");
return structuredClone(state.responses[index]);
}
}
const loaded = await esmock("./index.js", {
"@certd/plugin-plus": { AbstractPlusTaskPlugin: AbstractTaskPlugin },
"../../../plugin-lib/tencent/index.js": { TencentSslClient: MockSslClient },
});
const Plugin = loaded.TencentDeleteExpiringCert;
Plugin.prototype.getAccess = async () => ({});
Date.now = () => state.now;
utils.sleep = sleep;
const plugin = new Plugin();
Object.assign(plugin, { accessId: "mock-access", maxCount: 100, expiringDays: 18, checkTimeout: 1, logger });
plugin.ctx = { logger, utils: { sleep }, signal: controller.signal };
return { plugin, Plugin, state, logs, calls, sleeps, signal: controller, sleep };
}
async function runPipelineFixture() {
const sent: any[] = [];
const finished: any[] = [];
const writes: Array<{ key: string; value: string }> = [];
const pipeline: any = {
id: 1,
title: "腾讯云清理回归",
userId: 1,
stages: [
{
id: "stage-1",
title: "部署",
concurrency: ConcurrencyStrategy.Serial,
tasks: [
{
id: "task-1",
title: "清理",
steps: [
{
id: "step-1",
title: "删除旧证书",
type: pluginName,
strategy: { runStrategy: RunStrategy.AlwaysRun },
input: { accessId: "mock-access", maxCount: 100, expiringDays: 18, checkTimeout: 1 },
},
],
},
],
},
],
notifications: [
{ id: "error-notice", title: "失败通知", type: "other", when: ["error"], notificationId: 1 },
{ id: "success-notice", title: "成功通知", type: "other", when: ["success"], notificationId: 2 },
],
};
const executor = new Executor({
pipeline,
storage: {
get: async () => null,
set: async (_scope, _namespace, _version, key, value) => {
writes.push({ key, value });
},
remove: async () => {},
},
onChanged: async () => {},
onFinished: async history => {
const snapshot = structuredClone(history.pipeline);
finished.push(snapshot);
},
accessService: {} as any,
emailService: {} as any,
notificationService: {
send: async request => {
sent.push(request);
},
} as any,
cnameProxyService: {} as any,
pluginConfigService: { getPluginConfig: async () => ({ sysSetting: { input: {} } }) } as any,
urlService: { getPipelineDetailUrl: async () => "https://example.invalid/pipeline/1" } as any,
user: { id: 1, role: "user" },
serviceGetter: {} as any,
fileRootDir: tmpdir(),
});
const result = await executor.run(1, "manual");
return { executor, result, sent, finished, writes };
}
describe("TencentDeleteExpiringCert", () => {
beforeEach(() => {
const storage = pluginRegistry.getStorage();
originalRegistration = storage[pluginName];
});
afterEach(() => {
Date.now = originalNow;
utils.sleep = originalSleep;
if (originalRegistration) {
pluginRegistry.register(pluginName, originalRegistration);
} else {
pluginRegistry.unRegister(pluginName);
}
});
for (const [status, reason] of [
[2, /失败/],
[3, /SSL_QCSLinkedRoleInReplaceLoadCertificate/],
[4, /未解绑/],
[5, /查询关联.*超时/],
[99, /未知状态.*99/],
[undefined, /未知状态.*undefined/],
["1", /未知状态.*1/],
] as Array<[number | string | undefined, RegExp]>) {
it(`状态 ${String(status)} 必须传播失败`, async () => {
const fixture = await createFixture([status]);
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
assert.match(error.message, reason);
assert.match(error.message, /task-0/);
assert.match(error.message, /cert-0/);
return true;
}
);
});
}
it("全部成功保持成功且删除参数不变", async () => {
const fixture = await createFixture([1, 1]);
await fixture.plugin.execute();
const request = fixture.calls.find(call => call.action === "DeleteCertificates");
assert.deepEqual(request?.params, { CertificateIds: ["cert-0", "cert-1"], IsSync: true });
});
it("跨轮询保留失败而不是被后续成功覆盖", async () => {
const fixture = await createFixture([4, 1]);
fixture.state.responses = [
{
DeleteTaskResult: [
{ TaskId: "task-0", CertId: "cert-0", Status: 4 },
{ TaskId: "task-1", CertId: "cert-1", Status: 0 },
],
},
{ DeleteTaskResult: [{ TaskId: "task-1", CertId: "cert-1", Status: 1 }] },
];
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
assert.match(error.message, /成功[::]\s*1/);
assert.match(error.message, /失败[::]\s*1/);
assert.match(error.message, /task-0/);
assert.match(error.message, /未解绑/);
return true;
}
);
assert.equal(fixture.state.pollCount, 2);
});
for (const response of [{ DeleteTaskResult: [{ TaskId: "task-0", CertId: "cert-0", Status: 0 }] }, { DeleteTaskResult: [] }, {}]) {
it("未完成或无有效查询结果必须超时失败", async () => {
const fixture = await createFixture([0]);
fixture.state.responses = [response];
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
assert.match(error.message, /超时/);
assert.match(error.message, /task-0/);
assert.match(error.message, /未完成[::]\s*1/);
return true;
}
);
});
}
it("混合成功、失败和进行中时超时摘要保留全部结果", async () => {
const fixture = await createFixture([1, 4, 0]);
fixture.state.responses.push({ DeleteTaskResult: [{ TaskId: "task-2", CertId: "cert-2", Status: 0 }] });
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
for (const pattern of [/超时/, /成功[::]\s*1/, /失败[::]\s*1/, /未完成[::]\s*1/, /task-1/, /未解绑/, /task-2/]) {
assert.match(error.message, pattern);
}
return true;
}
);
});
it("同批次部分成功不能掩盖失败", async () => {
const fixture = await createFixture([1, 4]);
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
assert.match(error.message, /成功[::]\s*1/);
assert.match(error.message, /失败[::]\s*1/);
assert.match(error.message, /task-1/);
assert.match(error.message, /未解绑/);
return true;
}
);
assert.equal(fixture.state.pollCount, 1);
});
for (const status of [2, 3, 4, 5, 99]) {
it(`状态 ${status} 通过真实执行链产生失败历史及失败通知`, async () => {
await createFixture([status]);
const run = await runPipelineFixture();
assert.equal(run.result, ResultType.error);
const pipeline = run.executor.pipeline;
const stage = pipeline.stages[0];
const task = stage.tasks[0];
const step = task.steps[0];
for (const runnable of [step, task, stage, pipeline]) {
assert.equal(runnable.status?.status, ResultType.error);
assert.equal(runnable.status?.result, ResultType.error);
}
assert.equal(run.finished.length, 1);
assert.equal(run.finished[0].status.status, ResultType.error);
const saved = run.writes.find(write => write.key === "lastRuntime");
assert.ok(saved);
const storedValue = JSON.parse(saved.value);
const history = storedValue.value;
assert.equal(history.pipeline.status.status, ResultType.error);
const sentIds = run.sent.map(request => request.id);
assert.deepEqual(sentIds, [1]);
assert.match(run.sent[0].body.content, /task-0/);
});
}
it("成功对照通过真实执行链只发送成功通知", async () => {
await createFixture([1]);
const run = await runPipelineFixture();
assert.equal(run.result, ResultType.success);
const pipeline = run.executor.pipeline;
const stage = pipeline.stages[0];
const task = stage.tasks[0];
for (const runnable of [task.steps[0], task, stage, pipeline]) {
assert.equal(runnable.status?.status, ResultType.success);
}
const sentIds = run.sent.map(request => request.id);
assert.deepEqual(sentIds, [2]);
});
for (const response of [undefined, null, {}, { CertTaskIds: [] }, { CertTaskIds: {} }, { CertTaskIds: [{}] }, { CertTaskIds: [{ TaskId: " " }] }, { CertTaskIds: [{ TaskId: "task-0" }, {}] }]) {
it("提交结果不完整时不允许误判成功或依赖偶然TypeError", async () => {
const fixture = await createFixture([1]);
fixture.state.deleteResponse = response;
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
assert.notEqual(error.name, "TypeError");
assert.match(error.message, /删除任务提交结果异常/);
return true;
}
);
const queries = fixture.calls.filter(call => call.action === "DescribeDeleteCertificatesTaskResult");
assert.equal(queries.length, 0);
});
}
it("重复、无关和缺少TaskId的结果不污染计数", async () => {
const fixture = await createFixture([1, 4]);
fixture.state.responses = [
{
DeleteTaskResult: [
{ TaskId: "task-0", CertId: "cert-0", Status: 1 },
{ TaskId: "task-0", CertId: "cert-0", Status: 1 },
{ TaskId: "other-task", CertId: "other-cert", Status: 4 },
{ Status: 4 },
{ TaskId: "task-1", CertId: "cert-1", Status: 0 },
],
},
{
DeleteTaskResult: [
{ TaskId: "task-0", CertId: "cert-0", Status: 1 },
{ TaskId: "task-1", CertId: "cert-1", Status: 4 },
],
},
];
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
assert.match(error.message, /成功[::]\s*1/);
assert.match(error.message, /失败[::]\s*1/);
assert.doesNotMatch(error.message, /other-task/);
return true;
}
);
assert.equal(fixture.state.pollCount, 2);
const queries = fixture.calls.filter(call => call.action === "DescribeDeleteCertificatesTaskResult");
assert.deepEqual(queries[1].params.TaskIds, ["task-1"]);
});
it("重复的已提交任务ID仅处理一次", async () => {
const fixture = await createFixture([1]);
fixture.state.deleteResponse = { CertTaskIds: [{ TaskId: "task-0" }, { TaskId: "task-0" }] };
await fixture.plugin.execute();
const query = fixture.calls.find(call => call.action === "DescribeDeleteCertificatesTaskResult");
assert.deepEqual(query?.params.TaskIds, ["task-0"]);
});
for (const response of [{ DeleteTaskResult: [] }, {}]) {
it("无有效查询结果时等待而不是忙等", async () => {
const fixture = await createFixture([0]);
fixture.state.responses = [response];
await assert.rejects(() => fixture.plugin.execute(), /超时/);
assert.ok(fixture.sleeps.length > 0);
const allWaitsUseExistingInterval = fixture.sleeps.every(ms => ms === 10000);
assert.ok(allWaitsUseExistingInterval);
assert.ok(fixture.state.pollCount < 20);
});
}
for (const noCandidates of ["empty", "not-expiring"]) {
it(`没有符合条件的证书时正常结束:${noCandidates}`, async () => {
const fixture = await createFixture([1]);
if (noCandidates === "empty") {
fixture.state.certificates = [];
} else {
fixture.state.certificates[0].CertEndTime = "2999-01-01 00:00:00";
}
await fixture.plugin.execute();
const deleteWasRequested = fixture.calls.some(call => call.action === "DeleteCertificates");
assert.equal(deleteWasRequested, false);
});
}
for (const action of ["DescribeCertificates", "DeleteCertificates", "DescribeDeleteCertificatesTaskResult"]) {
it(`接口异常原样传播:${action}`, async () => {
const fixture = await createFixture([1]);
fixture.state.failAction = action;
await assert.rejects(
() => fixture.plugin.execute(),
error => error === fixture.state.requestError
);
});
}
it("取消信号不被转换为成功或普通超时", async () => {
const fixture = await createFixture([0]);
fixture.signal.abort();
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
// CancelError 未设置 this.name,依赖侧 .name 仍为 Error;用构造名确认取消语义
assert.equal(error.constructor.name, "CancelError");
assert.match(error.message, /取消/);
assert.doesNotMatch(error.message, /超时/);
return true;
}
);
});
it("超时通过真实执行链产生失败历史及失败通知", async () => {
const fixture = await createFixture([0]);
const run = await runPipelineFixture();
assert.equal(run.result, ResultType.error);
const pipeline = run.executor.pipeline;
const stage = pipeline.stages[0];
const task = stage.tasks[0];
for (const runnable of [task.steps[0], task, stage, pipeline]) {
assert.equal(runnable.status?.status, ResultType.error);
}
assert.equal(run.finished[0].status.status, ResultType.error);
const sentIds = run.sent.map(request => request.id);
assert.deepEqual(sentIds, [1]);
assert.match(run.sent[0].body.content, /超时/);
assert.ok(fixture.sleeps.length > 0);
});
it("提交响应Fail与任务成功并存时整批失败并保留失败证书明细", async () => {
const fixture = await createFixture([1, 1]);
fixture.state.deleteResponse = {
Fail: [{ CertId: "cert-0", Msg: "证书仍被资源绑定" }],
CertTaskIds: [{ CertId: "cert-1", TaskId: "task-1" }],
};
fixture.state.responses = [{ DeleteTaskResult: [{ TaskId: "task-1", CertId: "cert-1", Status: 1 }] }];
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
assert.match(error.message, /删除证书任务失败/);
assert.match(error.message, /成功[::]\s*1/);
assert.match(error.message, /失败[::]\s*1/);
assert.match(error.message, /cert-0/);
assert.match(error.message, /证书仍被资源绑定/);
return true;
}
);
});
it("CertTaskIds为空且Fail非空时抛删除证书任务失败并含Fail明细", async () => {
const fixture = await createFixture([1]);
fixture.state.deleteResponse = {
Fail: [{ CertId: "cert-0", Msg: "证书不存在或已删除" }],
CertTaskIds: [],
};
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
assert.match(error.message, /删除证书任务失败/);
assert.doesNotMatch(error.message, /删除任务提交结果异常/);
assert.match(error.message, /cert-0/);
assert.match(error.message, /证书不存在或已删除/);
return true;
}
);
const queries = fixture.calls.filter(call => call.action === "DescribeDeleteCertificatesTaskResult");
assert.equal(queries.length, 0);
});
it("提交响应Fail通过真实执行链产生失败历史及失败通知", async () => {
const fixture = await createFixture([1, 1]);
fixture.state.deleteResponse = {
Fail: [{ CertId: "cert-0", Msg: "证书仍被资源绑定" }],
CertTaskIds: [{ CertId: "cert-1", TaskId: "task-1" }],
};
fixture.state.responses = [{ DeleteTaskResult: [{ TaskId: "task-1", CertId: "cert-1", Status: 1 }] }];
const run = await runPipelineFixture();
assert.equal(run.result, ResultType.error);
const pipeline = run.executor.pipeline;
const stage = pipeline.stages[0];
const task = stage.tasks[0];
for (const runnable of [task.steps[0], task, stage, pipeline]) {
assert.equal(runnable.status?.status, ResultType.error);
}
const sentIds = run.sent.map(request => request.id);
assert.deepEqual(sentIds, [1]);
assert.match(run.sent[0].body.content, /cert-0/);
assert.match(run.sent[0].body.content, /证书仍被资源绑定/);
});
it("提交含CertId映射且查询持续为空时超时摘要保留CertId", async () => {
const fixture = await createFixture([0]);
fixture.state.deleteResponse = { CertTaskIds: [{ CertId: "cert-0", TaskId: "task-0" }] };
fixture.state.responses = [{ DeleteTaskResult: [] }];
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
assert.match(error.message, /超时/);
assert.match(error.message, /task-0/);
assert.match(error.message, /cert-0/);
assert.match(error.message, /未完成[::]\s*1/);
return true;
}
);
});
it("轮询结果丢失CertId时失败摘要保留此前已知CertId", async () => {
const fixture = await createFixture([0]);
fixture.state.deleteResponse = { CertTaskIds: [{ TaskId: "task-0" }] };
fixture.state.responses = [{ DeleteTaskResult: [{ TaskId: "task-0", CertId: "cert-0", Status: 0 }] }, { DeleteTaskResult: [{ TaskId: "task-0", Status: 4 }] }];
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
assert.match(error.message, /删除证书任务失败/);
assert.match(error.message, /task-0/);
assert.match(error.message, /cert-0/);
assert.match(error.message, /未解绑/);
return true;
}
);
});
it("提交映射有CertId且轮询全程不带CertId时摘要仍含CertId", async () => {
const fixture = await createFixture([0]);
fixture.state.deleteResponse = { CertTaskIds: [{ CertId: "cert-0", TaskId: "task-0" }] };
fixture.state.responses = [{ DeleteTaskResult: [{ TaskId: "task-0", Status: 4 }] }];
await assert.rejects(
() => fixture.plugin.execute(),
(error: Error) => {
assert.match(error.message, /删除证书任务失败/);
assert.match(error.message, /task-0/);
assert.match(error.message, /cert-0/);
assert.match(error.message, /未解绑/);
return true;
}
);
});
});
@@ -127,15 +127,8 @@ export class TencentDeleteExpiringCert extends AbstractPlusTaskPlugin {
IsSync: true,
});
this.logger.info("删除任务已提交: ", JSON.stringify(deleteRes));
const ids = deleteRes?.CertTaskIds;
if (!ids && !ids.length) {
this.logger.error("没有找到任务ID");
return;
}
const taskIds = ids.map((id: any) => id.TaskId);
const startTime = Date.now();
const results = {};
const results: Record<string, { certId?: string; status: unknown; reason: string }> = {};
const statusCount = {
success: 0,
failed: 0,
@@ -143,6 +136,67 @@ export class TencentDeleteExpiringCert extends AbstractPlusTaskPlugin {
unbind: 0,
timeout: 0,
};
const failList = deleteRes?.Fail;
if (failList != null && !Array.isArray(failList)) {
throw new Error("删除证书任务失败;提交阶段失败明细格式异常");
}
if (Array.isArray(failList)) {
for (let i = 0; i < failList.length; i++) {
const failItem = failList[i];
let failCertId = failItem?.CertId;
if (typeof failCertId !== "string" || failCertId.trim().length === 0) {
failCertId = `unknown-${i}`;
}
let failReason = failItem?.Msg;
if (typeof failReason !== "string" || failReason.trim().length === 0) {
failReason = "未知原因";
}
const failKey = `submit-fail:${failCertId}`;
results[failKey] = {
certId: failCertId,
status: "submit-fail",
reason: failReason,
};
statusCount.failed++;
this.logger.error(`提交阶段删除失败 证书${failCertId}: ${failReason}`);
}
}
const submitFailCount = Array.isArray(failList) ? failList.length : 0;
const ids = deleteRes?.CertTaskIds;
if (!Array.isArray(ids) || ids.length === 0) {
if (submitFailCount > 0) {
const failSummary = `任务总数:0,成功:0,失败:${statusCount.failed},未完成:0,任务结果:${JSON.stringify(results)},未完成任务:`;
throw new Error(`删除证书任务失败;${failSummary}`);
}
throw new Error("删除任务提交结果异常:没有有效任务ID");
}
const taskIds: string[] = [];
const certIdByTaskId: Record<string, string> = {};
for (const entry of ids) {
const taskId = entry?.TaskId;
if (typeof taskId !== "string" || taskId.trim().length === 0) {
throw new Error("删除任务提交结果异常:存在无效任务ID");
}
if (!taskIds.includes(taskId)) {
taskIds.push(taskId);
}
const submitCertId = entry?.CertId;
if (typeof submitCertId === "string" && submitCertId.trim().length > 0) {
certIdByTaskId[taskId] = submitCertId;
if (!results[taskId]) {
results[taskId] = {
certId: submitCertId,
status: undefined,
reason: "待查询",
};
} else if (!results[taskId].certId) {
results[taskId].certId = submitCertId;
}
}
}
const startTime = Date.now();
const total = taskIds.length;
while (Date.now() < startTime + this.checkTimeout * 60 * 1000) {
@@ -150,44 +204,61 @@ export class TencentDeleteExpiringCert extends AbstractPlusTaskPlugin {
const taskResultRes = await sslClient.doRequest("DescribeDeleteCertificatesTaskResult", {
TaskIds: taskIds,
});
const result = taskResultRes.DeleteTaskResult;
if (!result || result.length === 0) {
const result = taskResultRes?.DeleteTaskResult;
if (!Array.isArray(result) || result.length === 0) {
this.logger.info("暂未获取到有效的任务结果");
await this.ctx.utils.sleep(10000);
continue;
}
for (const item of result) {
//遍历结果
const status = item.Status;
if (status !== 0) {
remove(taskIds, id => id === item.TaskId);
const taskId = item?.TaskId;
if (!taskIds.includes(taskId)) {
continue;
}
// Status : 0表示任务进行中、 1表示任务成功、 2表示任务失败、3表示未授权服务角色导致任务失败、4表示有未解绑的云资源导致任务失败、5表示查询关联云资源超时导致任务失败
const status = item.Status;
let reason = "";
if (status === 0) {
this.logger.info(`任务${item.TaskId}<${item.CertId}>: 进行中`);
reason = "进行中";
} else if (status === 1) {
this.logger.info(`任务${item.TaskId}<${item.CertId}>: 成功`);
results[item.TaskId] = "成功";
reason = "成功";
statusCount.success++;
} else if (status === 2) {
this.logger.error(`任务${item.TaskId}<${item.CertId}>: 失败`);
results[item.TaskId] = "失败";
reason = "失败";
statusCount.failed++;
} else if (status === 3) {
this.logger.error(`任务${item.TaskId}<${item.CertId}>: 未授权服务角色导致任务失败`);
results[item.TaskId] = "未授权服务角色导致任务失败";
reason = "未授权服务角色导致任务失败";
statusCount.unauthorized++;
} else if (status === 4) {
this.logger.error(`任务${item.TaskId}<${item.CertId}>: 有未解绑的云资源导致任务失败`);
results[item.TaskId] = "有未解绑的云资源导致任务失败";
reason = "有未解绑的云资源导致任务失败";
statusCount.unbind++;
} else if (status === 5) {
this.logger.error(`任务${item.TaskId}<${item.CertId}>: 查询关联云资源超时导致任务失败`);
results[item.TaskId] = "查询关联云资源超时导致任务失败";
reason = "查询关联云资源超时导致任务失败";
statusCount.timeout++;
} else {
this.logger.info(`任务${item.TaskId}<${item.CertId}>: 未知状态:${status}`);
reason = `未知状态:${String(status)}`;
statusCount.failed++;
}
let certId = item.CertId;
const hasPollCertId = typeof certId === "string" && certId.trim().length > 0;
if (!hasPollCertId) {
const previousCertId = results[taskId]?.certId;
if (previousCertId) {
certId = previousCertId;
} else {
certId = certIdByTaskId[taskId];
}
}
results[taskId] = { certId, status, reason };
const message = `任务${taskId}<${certId}>: ${reason}`;
if (status === 0 || status === 1) {
this.logger.info(message);
} else {
this.logger.error(message);
}
if (status !== 0) {
remove(taskIds, id => id === taskId);
}
}
this.logger.info(
// eslint-disable-next-line max-len
@@ -195,16 +266,34 @@ export class TencentDeleteExpiringCert extends AbstractPlusTaskPlugin {
);
if (taskIds.length === 0) {
this.logger.info("任务已全部完成");
if (statusCount.unauthorized > 0) {
throw new Error("有未授权服务角色导致任务失败,需给Access授权服务角色SSL_QCSLinkedRoleInReplaceLoadCertificate");
}
return;
break;
}
await this.ctx.utils.sleep(10000);
}
this.logger.error("检查任务结果超时", JSON.stringify(results));
const failedCount = statusCount.failed + statusCount.unauthorized + statusCount.unbind + statusCount.timeout;
const resultDetails = JSON.stringify(results);
const pendingParts: string[] = [];
for (const pendingTaskId of taskIds) {
const pendingCertId = results[pendingTaskId]?.certId ?? certIdByTaskId[pendingTaskId];
if (pendingCertId) {
pendingParts.push(`${pendingTaskId}<${pendingCertId}>`);
} else {
pendingParts.push(pendingTaskId);
}
}
const pendingTaskDetails = pendingParts.join(",");
let summary = `任务总数:${total},成功:${statusCount.success},失败:${failedCount},未完成:${taskIds.length},任务结果:${resultDetails},未完成任务:${pendingTaskDetails}`;
if (statusCount.unauthorized > 0) {
summary += ";有未授权服务角色导致任务失败,需给Access授权服务角色SSL_QCSLinkedRoleInReplaceLoadCertificate";
}
if (taskIds.length > 0) {
throw new Error(`检查任务结果超时;${summary}`);
}
if (failedCount > 0) {
throw new Error(`删除证书任务失败;${summary}`);
}
this.logger.info(`删除证书任务全部成功;${summary}`);
}
}
+1 -1
View File
@@ -1 +1 @@
22:55
15:43
+1 -1
View File
@@ -1 +1 @@
23:23
15:37