mirror of https://github.com/certd/certd
				
				
				
			Merge remote-tracking branch 'origin/v2-dev' into v2-dev
						commit
						a8de2f8ae7
					
				|  | @ -0,0 +1,11 @@ | |||
| # | ||||
| # http://editorconfig.org | ||||
| # | ||||
| 
 | ||||
| root = true | ||||
| 
 | ||||
| [*] | ||||
| indent_style = space | ||||
| indent_size = 2 | ||||
| trim_trailing_whitespace = true | ||||
| 
 | ||||
							
								
								
									
										17
									
								
								CHANGELOG.md
								
								
								
								
							
							
						
						
									
										17
									
								
								CHANGELOG.md
								
								
								
								
							|  | @ -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.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 | ||||
|  |  | |||
|  | @ -1 +1 @@ | |||
| 23:23 | ||||
| 12:51 | ||||
|  |  | |||
|  | @ -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" | ||||
|  |  | |||
|  | @ -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.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 | ||||
|  |  | |||
|  | @ -65,8 +65,16 @@ networks: | |||
| docker logs -f --tail 200 certd | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## 6. 容器内走时不准,或者时区不对 | ||||
| 走时不准确,慢慢偏差越来越大     | ||||
| 或者整个时区都不对     | ||||
| 可以尝试挂载localtime文件 | ||||
| ```yaml | ||||
|     volumes: | ||||
|       #   ↓↓↓↓↓ -------------------- 如果走时不准,请尝试挂载localtime文件 | ||||
|       - /etc/localtime:/etc/localtime | ||||
|       - /etc/timezone:/etc/timezone | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,5 +9,5 @@ | |||
|     } | ||||
|   }, | ||||
|   "npmClient": "pnpm", | ||||
|   "version": "1.36.15" | ||||
|   "version": "1.36.16" | ||||
| } | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ root = true | |||
| 
 | ||||
| [*] | ||||
| indent_style = space | ||||
| indent_size = 4 | ||||
| indent_size = 2 | ||||
| trim_trailing_whitespace = true | ||||
| 
 | ||||
| [{*.yml,*.yaml}] | ||||
|  |  | |||
|  | @ -3,6 +3,12 @@ | |||
| All notable changes to this project will be documented in this file. | ||||
| See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||||
| 
 | ||||
| ## [1.36.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 | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
|     "description": "Simple and unopinionated ACME client", | ||||
|     "private": false, | ||||
|     "author": "nmorsman", | ||||
|     "version": "1.36.15", | ||||
|     "version": "1.36.16", | ||||
|     "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.16", | ||||
|         "@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": "e979e9c9fb5d4e29aa4946e9c5206c222ceb0f75" | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,10 @@ | |||
| All notable changes to this project will be documented in this file. | ||||
| See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||||
| 
 | ||||
| ## [1.36.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 | ||||
|  |  | |||
|  | @ -1 +1 @@ | |||
| 23:18 | ||||
| 12:46 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|   "name": "@certd/basic", | ||||
|   "private": false, | ||||
|   "version": "1.36.15", | ||||
|   "version": "1.36.16", | ||||
|   "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": "e979e9c9fb5d4e29aa4946e9c5206c222ceb0f75" | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,13 @@ | |||
| All notable changes to this project will be documented in this file. | ||||
| See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||||
| 
 | ||||
| ## [1.36.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 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|   "name": "@certd/pipeline", | ||||
|   "private": false, | ||||
|   "version": "1.36.15", | ||||
|   "version": "1.36.16", | ||||
|   "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.16", | ||||
|     "@certd/plus-core": "^1.36.16", | ||||
|     "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": "e979e9c9fb5d4e29aa4946e9c5206c222ceb0f75" | ||||
| } | ||||
|  |  | |||
|  | @ -11,7 +11,6 @@ export type PageSearch = { | |||
|   // sortOrder?: "asc" | "desc";
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| export type PageRes = { | ||||
|   pageNo?: number; | ||||
|   pageSize?: number; | ||||
|  |  | |||
|  | @ -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"), | ||||
|  |  | |||
|  | @ -3,6 +3,10 @@ | |||
| All notable changes to this project will be documented in this file. | ||||
| See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||||
| 
 | ||||
| ## [1.36.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 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|   "name": "@certd/lib-huawei", | ||||
|   "private": false, | ||||
|   "version": "1.36.15", | ||||
|   "version": "1.36.16", | ||||
|   "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": "e979e9c9fb5d4e29aa4946e9c5206c222ceb0f75" | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,10 @@ | |||
| All notable changes to this project will be documented in this file. | ||||
| See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||||
| 
 | ||||
| ## [1.36.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 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|   "name": "@certd/lib-iframe", | ||||
|   "private": false, | ||||
|   "version": "1.36.15", | ||||
|   "version": "1.36.16", | ||||
|   "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": "e979e9c9fb5d4e29aa4946e9c5206c222ceb0f75" | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,10 @@ | |||
| All notable changes to this project will be documented in this file. | ||||
| See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||||
| 
 | ||||
| ## [1.36.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 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| { | ||||
|   "name": "@certd/jdcloud", | ||||
|   "version": "1.36.15", | ||||
|   "version": "1.36.16", | ||||
|   "description": "jdcloud openApi sdk", | ||||
|   "main": "./dist/bundle.js", | ||||
|   "module": "./dist/bundle.js", | ||||
|  | @ -61,5 +61,5 @@ | |||
|       "fetch" | ||||
|     ] | ||||
|   }, | ||||
|   "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" | ||||
|   "gitHead": "e979e9c9fb5d4e29aa4946e9c5206c222ceb0f75" | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,10 @@ | |||
| All notable changes to this project will be documented in this file. | ||||
| See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||||
| 
 | ||||
| ## [1.36.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 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|   "name": "@certd/lib-k8s", | ||||
|   "private": false, | ||||
|   "version": "1.36.15", | ||||
|   "version": "1.36.16", | ||||
|   "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.16", | ||||
|     "@kubernetes/client-node": "0.21.0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|  | @ -32,5 +32,5 @@ | |||
|     "tslib": "^2.8.1", | ||||
|     "typescript": "^5.4.2" | ||||
|   }, | ||||
|   "gitHead": "fb7341f1f7d05d05c5439a36594665e3855d6a00" | ||||
|   "gitHead": "e979e9c9fb5d4e29aa4946e9c5206c222ceb0f75" | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,12 @@ | |||
| All notable changes to this project will be documented in this file. | ||||
| See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||||
| 
 | ||||
| ## [1.36.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 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| { | ||||
|   "name": "@certd/lib-server", | ||||
|   "version": "1.36.15", | ||||
|   "version": "1.36.16", | ||||
|   "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.16", | ||||
|     "@certd/basic": "^1.36.16", | ||||
|     "@certd/pipeline": "^1.36.16", | ||||
|     "@certd/plus-core": "^1.36.16", | ||||
|     "@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": "e979e9c9fb5d4e29aa4946e9c5206c222ceb0f75" | ||||
| } | ||||
|  |  | |||
|  | @ -34,7 +34,18 @@ export class AccessService extends BaseService<AccessEntity> { | |||
|   } | ||||
| 
 | ||||
|   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); | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,6 +3,10 @@ | |||
| All notable changes to this project will be documented in this file. | ||||
| See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||||
| 
 | ||||
| ## [1.36.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 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| { | ||||
|   "name": "@certd/midway-flyway-js", | ||||
|   "version": "1.36.15", | ||||
|   "version": "1.36.16", | ||||
|   "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": "e979e9c9fb5d4e29aa4946e9c5206c222ceb0f75" | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,12 @@ | |||
| All notable changes to this project will be documented in this file. | ||||
| See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||||
| 
 | ||||
| ## [1.36.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 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|   "name": "@certd/plugin-cert", | ||||
|   "private": false, | ||||
|   "version": "1.36.15", | ||||
|   "version": "1.36.16", | ||||
|   "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.16", | ||||
|     "@certd/basic": "^1.36.16", | ||||
|     "@certd/pipeline": "^1.36.16", | ||||
|     "@certd/plugin-lib": "^1.36.16", | ||||
|     "@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": "e979e9c9fb5d4e29aa4946e9c5206c222ceb0f75" | ||||
| } | ||||
|  |  | |||
|  | @ -211,4 +211,8 @@ export class CertReader { | |||
|     } | ||||
|     return name + "_" + dayjs().format("YYYYMMDDHHmmssSSS"); | ||||
|   } | ||||
| 
 | ||||
|   static buildCertName(cert: any) { | ||||
|     return new CertReader(cert).buildCertName(); | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,10 @@ | |||
| All notable changes to this project will be documented in this file. | ||||
| See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. | ||||
| 
 | ||||
| ## [1.36.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 | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|   "name": "@certd/plugin-lib", | ||||
|   "private": false, | ||||
|   "version": "1.36.15", | ||||
|   "version": "1.36.16", | ||||
|   "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.16", | ||||
|     "@certd/pipeline": "^1.36.16", | ||||
|     "@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": "e979e9c9fb5d4e29aa4946e9c5206c222ceb0f75" | ||||
| } | ||||
|  |  | |||
|  | @ -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; | ||||
|  | @ -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.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 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| { | ||||
|   "name": "@certd/ui-client", | ||||
|   "version": "1.36.15", | ||||
|   "version": "1.36.16", | ||||
|   "private": true, | ||||
|   "scripts": { | ||||
|     "dev": "vite --open", | ||||
|  | @ -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.16", | ||||
|     "@certd/pipeline": "^1.36.16", | ||||
|     "@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", | ||||
|  |  | |||
|  | @ -54,6 +54,36 @@ | |||
|       <div class="content unicode" style="display: block;"> | ||||
|           <ul class="icon_lists dib-box"> | ||||
|            | ||||
|             <li class="dib"> | ||||
|               <span class="icon iconfont"></span> | ||||
|                 <div class="name">social-foursquare</div> | ||||
|                 <div class="code-name">&#xe8fb;</div> | ||||
|               </li> | ||||
|            | ||||
|             <li class="dib"> | ||||
|               <span class="icon iconfont"></span> | ||||
|                 <div class="name">ksyun-logo</div> | ||||
|                 <div class="code-name">&#xe65a;</div> | ||||
|               </li> | ||||
|            | ||||
|             <li class="dib"> | ||||
|               <span class="icon iconfont"></span> | ||||
|                 <div class="name">雨-copy</div> | ||||
|                 <div class="code-name">&#xe608;</div> | ||||
|               </li> | ||||
|            | ||||
|             <li class="dib"> | ||||
|               <span class="icon iconfont"></span> | ||||
|                 <div class="name">网宿</div> | ||||
|                 <div class="code-name">&#xe655;</div> | ||||
|               </li> | ||||
|            | ||||
|             <li class="dib"> | ||||
|               <span class="icon iconfont"></span> | ||||
|                 <div class="name">ai客服</div> | ||||
|                 <div class="code-name">&#xe727;</div> | ||||
|               </li> | ||||
|            | ||||
|             <li class="dib"> | ||||
|               <span class="icon iconfont"></span> | ||||
|                 <div class="name">cdn</div> | ||||
|  | @ -198,7 +228,7 @@ | |||
| <pre><code class="language-css" | ||||
| >@font-face { | ||||
|   font-family: 'iconfont'; | ||||
|   src: url('iconfont.svg?t=1743267254898#iconfont') format('svg'); | ||||
|   src: url('iconfont.svg?t=1754884110189#iconfont') format('svg'); | ||||
| } | ||||
| </code></pre> | ||||
|           <h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3> | ||||
|  | @ -224,6 +254,51 @@ | |||
|       <div class="content font-class"> | ||||
|         <ul class="icon_lists dib-box"> | ||||
|            | ||||
|           <li class="dib"> | ||||
|             <span class="icon iconfont icon-four"></span> | ||||
|             <div class="name"> | ||||
|               social-foursquare | ||||
|             </div> | ||||
|             <div class="code-name">.icon-four | ||||
|             </div> | ||||
|           </li> | ||||
|            | ||||
|           <li class="dib"> | ||||
|             <span class="icon iconfont icon-ksyun"></span> | ||||
|             <div class="name"> | ||||
|               ksyun-logo | ||||
|             </div> | ||||
|             <div class="code-name">.icon-ksyun | ||||
|             </div> | ||||
|           </li> | ||||
|            | ||||
|           <li class="dib"> | ||||
|             <span class="icon iconfont icon-rainyun"></span> | ||||
|             <div class="name"> | ||||
|               雨-copy | ||||
|             </div> | ||||
|             <div class="code-name">.icon-rainyun | ||||
|             </div> | ||||
|           </li> | ||||
|            | ||||
|           <li class="dib"> | ||||
|             <span class="icon iconfont icon-wangsu"></span> | ||||
|             <div class="name"> | ||||
|               网宿 | ||||
|             </div> | ||||
|             <div class="code-name">.icon-wangsu | ||||
|             </div> | ||||
|           </li> | ||||
|            | ||||
|           <li class="dib"> | ||||
|             <span class="icon iconfont icon-aikefu"></span> | ||||
|             <div class="name"> | ||||
|               ai客服 | ||||
|             </div> | ||||
|             <div class="code-name">.icon-aikefu | ||||
|             </div> | ||||
|           </li> | ||||
|            | ||||
|           <li class="dib"> | ||||
|             <span class="icon iconfont icon-cdn"></span> | ||||
|             <div class="name"> | ||||
|  | @ -440,6 +515,46 @@ | |||
|       <div class="content symbol"> | ||||
|           <ul class="icon_lists dib-box"> | ||||
|            | ||||
|             <li class="dib"> | ||||
|                 <svg class="icon svg-icon" aria-hidden="true"> | ||||
|                   <use xlink:href="#icon-four"></use> | ||||
|                 </svg> | ||||
|                 <div class="name">social-foursquare</div> | ||||
|                 <div class="code-name">#icon-four</div> | ||||
|             </li> | ||||
|            | ||||
|             <li class="dib"> | ||||
|                 <svg class="icon svg-icon" aria-hidden="true"> | ||||
|                   <use xlink:href="#icon-ksyun"></use> | ||||
|                 </svg> | ||||
|                 <div class="name">ksyun-logo</div> | ||||
|                 <div class="code-name">#icon-ksyun</div> | ||||
|             </li> | ||||
|            | ||||
|             <li class="dib"> | ||||
|                 <svg class="icon svg-icon" aria-hidden="true"> | ||||
|                   <use xlink:href="#icon-rainyun"></use> | ||||
|                 </svg> | ||||
|                 <div class="name">雨-copy</div> | ||||
|                 <div class="code-name">#icon-rainyun</div> | ||||
|             </li> | ||||
|            | ||||
|             <li class="dib"> | ||||
|                 <svg class="icon svg-icon" aria-hidden="true"> | ||||
|                   <use xlink:href="#icon-wangsu"></use> | ||||
|                 </svg> | ||||
|                 <div class="name">网宿</div> | ||||
|                 <div class="code-name">#icon-wangsu</div> | ||||
|             </li> | ||||
|            | ||||
|             <li class="dib"> | ||||
|                 <svg class="icon svg-icon" aria-hidden="true"> | ||||
|                   <use xlink:href="#icon-aikefu"></use> | ||||
|                 </svg> | ||||
|                 <div class="name">ai客服</div> | ||||
|                 <div class="code-name">#icon-aikefu</div> | ||||
|             </li> | ||||
|            | ||||
|             <li class="dib"> | ||||
|                 <svg class="icon svg-icon" aria-hidden="true"> | ||||
|                   <use xlink:href="#icon-cdn"></use> | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| @font-face { | ||||
|   font-family: "iconfont"; /* Project id 4688792 */ | ||||
|   src: url('iconfont.svg?t=1743267254898#iconfont') format('svg'); | ||||
|   src: url('iconfont.svg?t=1754884110189#iconfont') format('svg'); | ||||
| } | ||||
| 
 | ||||
| .iconfont { | ||||
|  | @ -11,6 +11,26 @@ | |||
|   -moz-osx-font-smoothing: grayscale; | ||||
| } | ||||
| 
 | ||||
| .icon-four:before { | ||||
|   content: "\e8fb"; | ||||
| } | ||||
| 
 | ||||
| .icon-ksyun:before { | ||||
|   content: "\e65a"; | ||||
| } | ||||
| 
 | ||||
| .icon-rainyun:before { | ||||
|   content: "\e608"; | ||||
| } | ||||
| 
 | ||||
| .icon-wangsu:before { | ||||
|   content: "\e655"; | ||||
| } | ||||
| 
 | ||||
| .icon-aikefu:before { | ||||
|   content: "\e727"; | ||||
| } | ||||
| 
 | ||||
| .icon-cdn:before { | ||||
|   content: "\e6e4"; | ||||
| } | ||||
|  |  | |||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							|  | @ -5,6 +5,41 @@ | |||
|   "css_prefix_text": "icon-", | ||||
|   "description": "", | ||||
|   "glyphs": [ | ||||
|     { | ||||
|       "icon_id": "544964", | ||||
|       "name": "social-foursquare", | ||||
|       "font_class": "four", | ||||
|       "unicode": "e8fb", | ||||
|       "unicode_decimal": 59643 | ||||
|     }, | ||||
|     { | ||||
|       "icon_id": "8567079", | ||||
|       "name": "ksyun-logo", | ||||
|       "font_class": "ksyun", | ||||
|       "unicode": "e65a", | ||||
|       "unicode_decimal": 58970 | ||||
|     }, | ||||
|     { | ||||
|       "icon_id": "42174864", | ||||
|       "name": "雨-copy", | ||||
|       "font_class": "rainyun", | ||||
|       "unicode": "e608", | ||||
|       "unicode_decimal": 58888 | ||||
|     }, | ||||
|     { | ||||
|       "icon_id": "14065547", | ||||
|       "name": "网宿", | ||||
|       "font_class": "wangsu", | ||||
|       "unicode": "e655", | ||||
|       "unicode_decimal": 58965 | ||||
|     }, | ||||
|     { | ||||
|       "icon_id": "41324539", | ||||
|       "name": "ai客服", | ||||
|       "font_class": "aikefu", | ||||
|       "unicode": "e727", | ||||
|       "unicode_decimal": 59175 | ||||
|     }, | ||||
|     { | ||||
|       "icon_id": "13592652", | ||||
|       "name": "cdn", | ||||
|  |  | |||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							| Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 77 KiB | 
|  | @ -95,7 +95,7 @@ function install(app: App, options: any = {}) { | |||
| 
 | ||||
|               //不能用 !scope.value , 否则switch组件设置为关之后就消失了
 | ||||
|               const { value, key, props } = scope; | ||||
|               return !value && key != "_index" && value != false; | ||||
|               return !value && key != "_index" && value != false && value != 0; | ||||
|             }, | ||||
|             render() { | ||||
|               return "-"; | ||||
|  |  | |||
|  | @ -44,6 +44,20 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat | |||
|       }, | ||||
|       rowHandle: { | ||||
|         width: 200, | ||||
|         buttons: { | ||||
|           copy: { | ||||
|             async click(ctx: any) { | ||||
|               const { row, index } = ctx; | ||||
|               await crudExpose.openCopy({ | ||||
|                 row: { | ||||
|                   ...row, | ||||
|                   _copyFrom: row.id, | ||||
|                 }, | ||||
|                 index: index, | ||||
|               }); | ||||
|             }, | ||||
|           }, | ||||
|         }, | ||||
|       }, | ||||
|       columns: { | ||||
|         id: { | ||||
|  |  | |||
|  | @ -41,7 +41,7 @@ const option = ref({ | |||
|       center: ["60%", "50%"], | ||||
|       name: "状态", | ||||
|       type: "pie", | ||||
|       radius: "80%", | ||||
|       radius: ["30%", "70%"], | ||||
|       avoidLabelOverlap: false, | ||||
|       itemStyle: { | ||||
|         borderRadius: 0, | ||||
|  |  | |||
|  | @ -47,10 +47,6 @@ | |||
|         <div class="helper" v-html="t('certd.commonCnameHelper')"></div> | ||||
|       </a-form-item> | ||||
| 
 | ||||
|       <a-form-item :label="t('certd.enableCommonSelfServicePasswordRetrieval')" :name="['public', 'selfServicePasswordRetrievalEnabled']"> | ||||
|         <a-switch v-model:checked="formState.public.selfServicePasswordRetrievalEnabled" /> | ||||
|       </a-form-item> | ||||
| 
 | ||||
|       <a-form-item label=" " :colon="false" :wrapper-col="{ span: 8 }"> | ||||
|         <a-button :loading="saveLoading" type="primary" html-type="submit">{{ t("certd.saveButton") }}</a-button> | ||||
|       </a-form-item> | ||||
|  |  | |||
|  | @ -11,6 +11,9 @@ | |||
|       <a-form-item :label="t('certd.enableSelfRegistration')" :name="['public', 'registerEnabled']"> | ||||
|         <a-switch v-model:checked="formState.public.registerEnabled" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item :label="t('certd.enableCommonSelfServicePasswordRetrieval')" :name="['public', 'selfServicePasswordRetrievalEnabled']"> | ||||
|         <a-switch v-model:checked="formState.public.selfServicePasswordRetrievalEnabled" /> | ||||
|       </a-form-item> | ||||
|       <a-form-item :label="t('certd.enableUserValidityPeriod')" :name="['public', 'userValidTimeEnabled']"> | ||||
|         <div class="flex-o"> | ||||
|           <a-switch v-model:checked="formState.public.userValidTimeEnabled" :disabled="!settingsStore.isPlus" /> | ||||
|  |  | |||
|  | @ -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.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 | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| { | ||||
|   "name": "@certd/ui-server", | ||||
|   "version": "1.36.15", | ||||
|   "version": "1.36.16", | ||||
|   "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.16", | ||||
|     "@certd/basic": "^1.36.16", | ||||
|     "@certd/commercial-core": "^1.36.16", | ||||
|     "@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.16", | ||||
|     "@certd/lib-huawei": "^1.36.16", | ||||
|     "@certd/lib-k8s": "^1.36.16", | ||||
|     "@certd/lib-server": "^1.36.16", | ||||
|     "@certd/midway-flyway-js": "^1.36.16", | ||||
|     "@certd/pipeline": "^1.36.16", | ||||
|     "@certd/plugin-cert": "^1.36.16", | ||||
|     "@certd/plugin-lib": "^1.36.16", | ||||
|     "@certd/plugin-plus": "^1.36.16", | ||||
|     "@certd/plus-core": "^1.36.16", | ||||
|     "@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120", | ||||
|     "@huaweicloud/huaweicloud-sdk-core": "^3.1.120", | ||||
|     "@koa/cors": "^5.0.0", | ||||
|  | @ -91,6 +91,7 @@ | |||
|     "jsonwebtoken": "^9.0.0", | ||||
|     "jszip": "^3.10.1", | ||||
|     "koa-send": "^5.0.1", | ||||
|     "ksyun-sdk-node": "^1.2.4", | ||||
|     "kubernetes-client": "^9.0.0", | ||||
|     "lodash-es": "^4.17.21", | ||||
|     "log4js": "^6.7.1", | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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) { | ||||
|  |  | |||
|  | @ -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' | ||||
|  |  | |||
|  | @ -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<void> { | ||||
|     this.logger.info('开始部署证书到阿里云Api网关'); | ||||
|     if(!this.customDomains){ | ||||
|       throw new Error('您还未选择域名'); | ||||
|     } | ||||
|     const access = await this.getAccess<AliyunAccess>(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<AliyunAccess>(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<AliyunAccess>(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<AliyunAccess>(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(); | ||||
|  | @ -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('部署完成'); | ||||
|  |  | |||
|  | @ -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'; | ||||
|  |  | |||
|  | @ -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(); | ||||
|  | @ -0,0 +1,2 @@ | |||
| export * from "./plugins/index.js"; | ||||
| export * from "./access.js"; | ||||
|  | @ -0,0 +1 @@ | |||
| import "./plugin-refresh-cert.js" | ||||
|  | @ -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<void> { | ||||
|     const access = await this.getAccess<ApisixAccess>(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<ApisixAccess>(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(); | ||||
|  | @ -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(); | ||||
|  | @ -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}`; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | @ -0,0 +1,2 @@ | |||
| export * from "./plugins/index.js"; | ||||
| export * from "./access.js"; | ||||
|  | @ -0,0 +1 @@ | |||
| import "./plugin-refresh-cert.js" | ||||
|  | @ -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<void> { | ||||
|     const access = await this.getAccess<KsyunAccess>(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<KsyunAccess>(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(); | ||||
							
								
								
									
										107
									
								
								pnpm-lock.yaml
								
								
								
								
							
							
						
						
									
										107
									
								
								pnpm-lock.yaml
								
								
								
								
							|  | @ -49,7 +49,7 @@ importers: | |||
|   packages/core/acme-client: | ||||
|     dependencies: | ||||
|       '@certd/basic': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         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.15 | ||||
|         version: link:../basic | ||||
|       '@certd/plus-core': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         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.15 | ||||
|         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.15 | ||||
|         version: link:../../core/acme-client | ||||
|       '@certd/basic': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../core/basic | ||||
|       '@certd/pipeline': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../core/pipeline | ||||
|       '@certd/plus-core': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         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.15 | ||||
|         version: link:../../core/acme-client | ||||
|       '@certd/basic': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../core/basic | ||||
|       '@certd/pipeline': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../core/pipeline | ||||
|       '@certd/plugin-lib': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         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.15 | ||||
|         version: link:../../core/basic | ||||
|       '@certd/pipeline': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         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.15 | ||||
|         version: link:../../core/basic | ||||
|       '@certd/lib-server': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../libs/lib-server | ||||
|       '@certd/pipeline': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../core/pipeline | ||||
|       '@certd/plugin-plus': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../plugin-plus | ||||
|       '@certd/plus-core': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         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.15 | ||||
|         version: link:../../core/basic | ||||
|       '@certd/lib-k8s': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../libs/lib-k8s | ||||
|       '@certd/pipeline': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../core/pipeline | ||||
|       '@certd/plugin-cert': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../plugins/plugin-cert | ||||
|       '@certd/plugin-lib': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../plugins/plugin-lib | ||||
|       '@certd/plus-core': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         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.15 | ||||
|         version: link:../../core/basic | ||||
|       dayjs: | ||||
|         specifier: ^1.11.7 | ||||
|  | @ -1297,10 +1297,10 @@ importers: | |||
|         version: 0.1.3(zod@3.24.4) | ||||
|     devDependencies: | ||||
|       '@certd/lib-iframe': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../libs/lib-iframe | ||||
|       '@certd/pipeline': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         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.15 | ||||
|         version: link:../../core/acme-client | ||||
|       '@certd/basic': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../core/basic | ||||
|       '@certd/commercial-core': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         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.15 | ||||
|         version: link:../../libs/lib-jdcloud | ||||
|       '@certd/lib-huawei': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../libs/lib-huawei | ||||
|       '@certd/lib-k8s': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../libs/lib-k8s | ||||
|       '@certd/lib-server': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../libs/lib-server | ||||
|       '@certd/midway-flyway-js': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../libs/midway-flyway-js | ||||
|       '@certd/pipeline': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../core/pipeline | ||||
|       '@certd/plugin-cert': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../plugins/plugin-cert | ||||
|       '@certd/plugin-lib': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../plugins/plugin-lib | ||||
|       '@certd/plugin-plus': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../pro/plugin-plus | ||||
|       '@certd/plus-core': | ||||
|         specifier: ^1.36.10 | ||||
|         specifier: ^1.36.15 | ||||
|         version: link:../../pro/plus-core | ||||
|       '@huaweicloud/huaweicloud-sdk-cdn': | ||||
|         specifier: ^3.1.120 | ||||
|  | @ -1629,6 +1629,9 @@ importers: | |||
|       koa-send: | ||||
|         specifier: ^5.0.1 | ||||
|         version: 5.0.1 | ||||
|       ksyun-sdk-node: | ||||
|         specifier: ^1.2.4 | ||||
|         version: 1.2.4(encoding@0.1.13) | ||||
|       kubernetes-client: | ||||
|         specifier: ^9.0.0 | ||||
|         version: 9.0.0 | ||||
|  | @ -9186,6 +9189,9 @@ packages: | |||
|   kolorist@1.8.0: | ||||
|     resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} | ||||
| 
 | ||||
|   ksyun-sdk-node@1.2.4: | ||||
|     resolution: {integrity: sha512-W/c1nhnZskadPP7ObmizMh+jJeHXWka0HkS8lcZfLWxwEH83B8iMFF0DrtSaDCjQRuBpgzwDLGbbp+U1D1rXlQ==} | ||||
| 
 | ||||
|   kubernetes-client@9.0.0: | ||||
|     resolution: {integrity: sha512-Qy8o42dZVHB9P+cIiKdWpQbz/65l/qW1fDYvlzzeSLftmL1Ne3HEiM+0TmKAwNuRW0pTJN2tRWhcccToclxJ8g==} | ||||
|     engines: {node: '>=10.13.0'} | ||||
|  | @ -23512,6 +23518,17 @@ snapshots: | |||
| 
 | ||||
|   kolorist@1.8.0: {} | ||||
| 
 | ||||
|   ksyun-sdk-node@1.2.4(encoding@0.1.13): | ||||
|     dependencies: | ||||
|       abort-controller: 3.0.0 | ||||
|       core-js: 3.42.0 | ||||
|       crypto-js: 4.2.0 | ||||
|       dayjs: 1.11.13 | ||||
|       node-fetch: 2.7.0(encoding@0.1.13) | ||||
|       qs: 6.14.0 | ||||
|     transitivePeerDependencies: | ||||
|       - encoding | ||||
| 
 | ||||
|   kubernetes-client@9.0.0: | ||||
|     dependencies: | ||||
|       '@kubernetes/client-node': 0.10.2 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 xiaojunnuo
						xiaojunnuo