Compare commits

..

29 Commits

Author SHA1 Message Date
xiaojunnuo
e0408f30ba v1.36.7 2025-07-15 15:33:36 +08:00
xiaojunnuo
dca44fa093 build: prepare to build 2025-07-15 15:21:52 +08:00
xiaojunnuo
bbacb76581 build: prepare to build 2025-07-15 15:19:18 +08:00
xiaojunnuo
1da8617a53 perf: 支持上传证书到各种对象存储,oss、cos、七牛、s3、minio等 2025-07-15 15:18:35 +08:00
xiaojunnuo
e5967f7e9d chore: 2025-07-15 15:17:11 +08:00
xiaojunnuo
65d84f9e9d chore: 2025-07-15 15:06:59 +08:00
xiaojunnuo
93e9498b41 fix: 修复流水线页面状态没有刷新的bug 2025-07-15 15:05:09 +08:00
xiaojunnuo
95332d5db9 perf: 支持邮箱发送证书 2025-07-15 13:58:01 +08:00
xiaojunnuo
9864792bbf fix: 修复流水线列表页报length错误的bug 2025-07-15 10:53:11 +08:00
xiaojunnuo
ca9d1eed7a Merge remote-tracking branch 'origin/v2-dev' into v2-dev 2025-07-15 10:45:34 +08:00
xiaojunnuo
38e867c917 fix: 修复自定义证书检查时间重启之后不生效的bug 2025-07-15 10:42:56 +08:00
xiaojunnuo
3ee1dbb8a5 build: publish 2025-07-14 23:54:12 +08:00
xiaojunnuo
b4571d5c98 build: trigger build image 2025-07-14 23:53:51 +08:00
xiaojunnuo
29d49d72f9 v1.36.6 2025-07-14 23:52:25 +08:00
xiaojunnuo
81de0fc7e4 build: prepare to build 2025-07-14 23:48:40 +08:00
xiaojunnuo
9d5d266d2a build: prepare to build 2025-07-14 23:46:40 +08:00
xiaojunnuo
b97935299f chore: auto功能 数据库升级脚本 2025-07-14 23:46:24 +08:00
xiaojunnuo
32a7ea1c16 chore: 2025-07-14 23:29:35 +08:00
xiaojunnuo
9fd95e6a1e chore: 2025-07-14 23:26:54 +08:00
xiaojunnuo
61ba83c775 perf: 通知和定时器的删除按钮显示为红色更显眼 2025-07-14 23:25:56 +08:00
xiaojunnuo
6369fed5fc chore: 2025-07-14 23:15:22 +08:00
xiaojunnuo
42f4d1477d perf: OpenAPI支持autoApply参数 2025-07-14 23:02:47 +08:00
xiaojunnuo
609ac9c9a2 perf: 优化流水线列表页面、详情页面性能,精简返回数据 2025-07-14 01:36:40 +08:00
xiaojunnuo
79f2367472 chore: 2025-07-14 00:34:47 +08:00
xiaojunnuo
dfc9362084 fix: 修复运行流水线后会闪烁一下的bug 2025-07-14 00:33:42 +08:00
xiaojunnuo
487b469603 chore: 增强cname安全性 2025-07-14 00:15:46 +08:00
xiaojunnuo
19e1df1e5d chore: 2025-07-13 23:58:07 +08:00
xiaojunnuo
fc55010888 Merge branch 'v2-dev-auto' into v2-dev 2025-07-13 23:23:47 +08:00
xiaojunnuo
902d246d1a perf: 部署plesk证书,支持删除未使用的证书 2025-07-13 17:10:15 +08:00
69 changed files with 1420 additions and 473 deletions

View File

@@ -3,6 +3,34 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
### Bug Fixes
* 修复流水线列表页报length错误的bug ([9864792](https://github.com/certd/certd/commit/9864792bbfd149e770d6e1ffa809573694f99dd3))
* 修复流水线页面状态没有刷新的bug ([93e9498](https://github.com/certd/certd/commit/93e9498b410353f504e11e264db62468895d7290))
* 修复自定义证书检查时间重启之后不生效的bug ([38e867c](https://github.com/certd/certd/commit/38e867c917bbc68bd228bdd8064f3e7358d6413d))
### Performance Improvements
* 支持上传证书到各种对象存储oss、cos、七牛、s3、minio等 ([1da8617](https://github.com/certd/certd/commit/1da8617a53a675776635bbc3bcb3c6d7dff83e27))
* 支持邮箱发送证书 ([95332d5](https://github.com/certd/certd/commit/95332d5db96cd54ddab6ab737332417a09169b39))
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
### Bug Fixes
* 修复某些页面翻译不全显示错误的bug ([0b3158f](https://github.com/certd/certd/commit/0b3158fdd5fe5bb0a98c4e65715dbc3de2c38047))
* 修复运行流水线后会闪烁一下的bug ([dfc9362](https://github.com/certd/certd/commit/dfc9362084082ee535b898f23b2609c1d946a6fd))
### Performance Improvements
* 部署plesk证书支持删除未使用的证书 ([902d246](https://github.com/certd/certd/commit/902d246d1a7473ad90f604028c4eb09c8c67d99c))
* 通知和定时器的删除按钮显示为红色更显眼 ([61ba83c](https://github.com/certd/certd/commit/61ba83c77546c3d505d081e19a3d68c127662bf1))
* 优化流水线列表页面、详情页面性能,精简返回数据 ([609ac9c](https://github.com/certd/certd/commit/609ac9c9a2dde605eb09834ae59693c1cb238765))
* 支持自动选择校验方式申请证书 ([3f99432](https://github.com/certd/certd/commit/3f9943270cfb12946e38e6272bc5e8d95ad6ab9e))
* OpenAPI支持autoApply参数 ([42f4d14](https://github.com/certd/certd/commit/42f4d1477dc791520a874aed56035abcbc8c433b))
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
### Bug Fixes ### Bug Fixes

View File

@@ -1 +1 @@
10:51 23:53

View File

@@ -3,6 +3,21 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
### Bug Fixes
* 修复某些页面翻译不全显示错误的bug ([0b3158f](https://github.com/certd/certd/commit/0b3158fdd5fe5bb0a98c4e65715dbc3de2c38047))
* 修复运行流水线后会闪烁一下的bug ([dfc9362](https://github.com/certd/certd/commit/dfc9362084082ee535b898f23b2609c1d946a6fd))
### Performance Improvements
* 部署plesk证书支持删除未使用的证书 ([902d246](https://github.com/certd/certd/commit/902d246d1a7473ad90f604028c4eb09c8c67d99c))
* 通知和定时器的删除按钮显示为红色更显眼 ([61ba83c](https://github.com/certd/certd/commit/61ba83c77546c3d505d081e19a3d68c127662bf1))
* 优化流水线列表页面、详情页面性能,精简返回数据 ([609ac9c](https://github.com/certd/certd/commit/609ac9c9a2dde605eb09834ae59693c1cb238765))
* 支持自动选择校验方式申请证书 ([3f99432](https://github.com/certd/certd/commit/3f9943270cfb12946e38e6272bc5e8d95ad6ab9e))
* OpenAPI支持autoApply参数 ([42f4d14](https://github.com/certd/certd/commit/42f4d1477dc791520a874aed56035abcbc8c433b))
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
### Bug Fixes ### Bug Fixes

View File

@@ -9,5 +9,5 @@
} }
}, },
"npmClient": "pnpm", "npmClient": "pnpm",
"version": "1.36.5" "version": "1.36.7"
} }

View File

@@ -36,6 +36,7 @@
}, },
"license": "AGPL-3.0", "license": "AGPL-3.0",
"dependencies": { "dependencies": {
"@certd/ui-server": "link:packages/ui/certd-server",
"axios": "^1.7.7", "axios": "^1.7.7",
"copyfiles": "^2.4.1", "copyfiles": "^2.4.1",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/publishlab/node-acme-client/compare/v1.36.6...v1.36.7) (2025-07-15)
**Note:** Version bump only for package @certd/acme-client
## [1.36.6](https://github.com/publishlab/node-acme-client/compare/v1.36.5...v1.36.6) (2025-07-14)
**Note:** Version bump only for package @certd/acme-client
## [1.36.5](https://github.com/publishlab/node-acme-client/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/publishlab/node-acme-client/compare/v1.36.4...v1.36.5) (2025-07-11)
**Note:** Version bump only for package @certd/acme-client **Note:** Version bump only for package @certd/acme-client

View File

@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client", "description": "Simple and unopinionated ACME client",
"private": false, "private": false,
"author": "nmorsman", "author": "nmorsman",
"version": "1.36.5", "version": "1.36.7",
"type": "module", "type": "module",
"module": "scr/index.js", "module": "scr/index.js",
"main": "src/index.js", "main": "src/index.js",
@@ -18,7 +18,7 @@
"types" "types"
], ],
"dependencies": { "dependencies": {
"@certd/basic": "^1.36.5", "@certd/basic": "^1.36.7",
"@peculiar/x509": "^1.11.0", "@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5", "asn1js": "^3.0.5",
"axios": "^1.7.2", "axios": "^1.7.2",
@@ -69,5 +69,5 @@
"bugs": { "bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues" "url": "https://github.com/publishlab/node-acme-client/issues"
}, },
"gitHead": "c2a95a13fe6edf05ea0f72f5f7c76f9eea3ab0bd" "gitHead": "29d49d72f95aa101729965710375104240a2c038"
} }

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
**Note:** Version bump only for package @certd/basic
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
**Note:** Version bump only for package @certd/basic
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
**Note:** Version bump only for package @certd/basic **Note:** Version bump only for package @certd/basic

View File

@@ -1 +1 @@
10:46 15:21

View File

@@ -1,7 +1,7 @@
{ {
"name": "@certd/basic", "name": "@certd/basic",
"private": false, "private": false,
"version": "1.36.5", "version": "1.36.7",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@@ -45,5 +45,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "c2a95a13fe6edf05ea0f72f5f7c76f9eea3ab0bd" "gitHead": "29d49d72f95aa101729965710375104240a2c038"
} }

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
### Performance Improvements
* 支持邮箱发送证书 ([95332d5](https://github.com/certd/certd/commit/95332d5db96cd54ddab6ab737332417a09169b39))
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
**Note:** Version bump only for package @certd/pipeline
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
**Note:** Version bump only for package @certd/pipeline **Note:** Version bump only for package @certd/pipeline

View File

@@ -1,7 +1,7 @@
{ {
"name": "@certd/pipeline", "name": "@certd/pipeline",
"private": false, "private": false,
"version": "1.36.5", "version": "1.36.7",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@@ -17,8 +17,8 @@
"pub": "npm publish" "pub": "npm publish"
}, },
"dependencies": { "dependencies": {
"@certd/basic": "^1.36.5", "@certd/basic": "^1.36.7",
"@certd/plus-core": "^1.36.5", "@certd/plus-core": "^1.36.7",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"reflect-metadata": "^0.1.13" "reflect-metadata": "^0.1.13"
@@ -44,5 +44,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "c2a95a13fe6edf05ea0f72f5f7c76f9eea3ab0bd" "gitHead": "29d49d72f95aa101729965710375104240a2c038"
} }

View File

@@ -1,7 +1,9 @@
export type EmailSend = { export type EmailSend = {
subject: string; subject: string;
content: string;
receivers: string[]; receivers: string[];
content?: string;
attachments?: any[];
html?: string;
}; };
export interface IEmailService { export interface IEmailService {

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
**Note:** Version bump only for package @certd/lib-huawei
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
**Note:** Version bump only for package @certd/lib-huawei
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
**Note:** Version bump only for package @certd/lib-huawei **Note:** Version bump only for package @certd/lib-huawei

View File

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

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
**Note:** Version bump only for package @certd/lib-iframe
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
**Note:** Version bump only for package @certd/lib-iframe
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
**Note:** Version bump only for package @certd/lib-iframe **Note:** Version bump only for package @certd/lib-iframe

View File

@@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-iframe", "name": "@certd/lib-iframe",
"private": false, "private": false,
"version": "1.36.5", "version": "1.36.7",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@@ -31,5 +31,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "c2a95a13fe6edf05ea0f72f5f7c76f9eea3ab0bd" "gitHead": "29d49d72f95aa101729965710375104240a2c038"
} }

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
**Note:** Version bump only for package @certd/jdcloud
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
**Note:** Version bump only for package @certd/jdcloud
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
**Note:** Version bump only for package @certd/jdcloud **Note:** Version bump only for package @certd/jdcloud

View File

@@ -1,6 +1,6 @@
{ {
"name": "@certd/jdcloud", "name": "@certd/jdcloud",
"version": "1.36.5", "version": "1.36.7",
"description": "jdcloud openApi sdk", "description": "jdcloud openApi sdk",
"main": "./dist/bundle.js", "main": "./dist/bundle.js",
"module": "./dist/bundle.js", "module": "./dist/bundle.js",
@@ -61,5 +61,5 @@
"fetch" "fetch"
] ]
}, },
"gitHead": "c2a95a13fe6edf05ea0f72f5f7c76f9eea3ab0bd" "gitHead": "29d49d72f95aa101729965710375104240a2c038"
} }

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
**Note:** Version bump only for package @certd/lib-k8s
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
**Note:** Version bump only for package @certd/lib-k8s
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
**Note:** Version bump only for package @certd/lib-k8s **Note:** Version bump only for package @certd/lib-k8s

View File

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

View File

@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
**Note:** Version bump only for package @certd/lib-server
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
### Performance Improvements
* 优化流水线列表页面、详情页面性能,精简返回数据 ([609ac9c](https://github.com/certd/certd/commit/609ac9c9a2dde605eb09834ae59693c1cb238765))
* OpenAPI支持autoApply参数 ([42f4d14](https://github.com/certd/certd/commit/42f4d1477dc791520a874aed56035abcbc8c433b))
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
**Note:** Version bump only for package @certd/lib-server **Note:** Version bump only for package @certd/lib-server

View File

@@ -1,6 +1,6 @@
{ {
"name": "@certd/lib-server", "name": "@certd/lib-server",
"version": "1.36.5", "version": "1.36.7",
"description": "midway with flyway, sql upgrade way ", "description": "midway with flyway, sql upgrade way ",
"private": false, "private": false,
"type": "module", "type": "module",
@@ -27,10 +27,10 @@
], ],
"license": "AGPL", "license": "AGPL",
"dependencies": { "dependencies": {
"@certd/acme-client": "^1.36.5", "@certd/acme-client": "^1.36.7",
"@certd/basic": "^1.36.5", "@certd/basic": "^1.36.7",
"@certd/pipeline": "^1.36.5", "@certd/pipeline": "^1.36.7",
"@certd/plus-core": "^1.36.5", "@certd/plus-core": "^1.36.7",
"@midwayjs/cache": "~3.14.0", "@midwayjs/cache": "~3.14.0",
"@midwayjs/core": "~3.20.3", "@midwayjs/core": "~3.20.3",
"@midwayjs/i18n": "~3.20.3", "@midwayjs/i18n": "~3.20.3",
@@ -61,5 +61,5 @@
"typeorm": "^0.3.11", "typeorm": "^0.3.11",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "c2a95a13fe6edf05ea0f72f5f7c76f9eea3ab0bd" "gitHead": "29d49d72f95aa101729965710375104240a2c038"
} }

View File

@@ -164,8 +164,11 @@ export abstract class BaseService<T> {
} }
private buildListQuery(listReq: ListReq<T>) { private buildListQuery(listReq: ListReq<T>) {
const { query, sort, buildQuery } = listReq; const { query, sort, buildQuery,select } = listReq;
const qb = this.getRepository().createQueryBuilder('main'); const qb = this.getRepository().createQueryBuilder('main');
if (select) {
qb.setFindOptions({select});
}
if (query) { if (query) {
const keys = Object.keys(query); const keys = Object.keys(query);
for (const key of keys) { for (const key of keys) {
@@ -191,6 +194,7 @@ export abstract class BaseService<T> {
if (buildQuery) { if (buildQuery) {
buildQuery(qb); buildQuery(qb);
} }
return qb; return qb;
} }

View File

@@ -107,5 +107,17 @@ export const Constants = {
code: 20012, code: 20012,
message: '证书还未生成', message: '证书还未生成',
}, },
openCertApplying: {
code: 20013,
message: '证书正在申请中,请稍后重新获取',
},
openDomainNoVerifier:{
code: 20014,
message: '域名校验方式未配置',
},
openEmailNotFound: {
code: 20021,
message: '用户邮箱还未配置',
},
}, },
}; };

View File

@@ -11,13 +11,13 @@ export class CommonException extends BaseException {
} }
export class CodeException extends BaseException { export class CodeException extends BaseException {
constructor(res: { code: number; message: string }) { constructor(res: { code: number; message: string; data?: any }) {
super("CodeException", res.code, res.message); super("CodeException", res.code, res.message, res.data);
} }
} }
export class TextException extends BaseException { export class TextException extends BaseException {
constructor(name, code,message, data?) { constructor(name, code, message, data?) {
super(name, code, message, data); super(name, code, message, data);
} }
} }

View File

@@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
**Note:** Version bump only for package @certd/midway-flyway-js **Note:** Version bump only for package @certd/midway-flyway-js

View File

@@ -1,6 +1,6 @@
{ {
"name": "@certd/midway-flyway-js", "name": "@certd/midway-flyway-js",
"version": "1.36.5", "version": "1.36.7",
"description": "midway with flyway, sql upgrade way ", "description": "midway with flyway, sql upgrade way ",
"private": false, "private": false,
"type": "module", "type": "module",
@@ -46,5 +46,5 @@
"typeorm": "^0.3.11", "typeorm": "^0.3.11",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "c2a95a13fe6edf05ea0f72f5f7c76f9eea3ab0bd" "gitHead": "29d49d72f95aa101729965710375104240a2c038"
} }

View File

@@ -3,6 +3,19 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
### Performance Improvements
* 支持邮箱发送证书 ([95332d5](https://github.com/certd/certd/commit/95332d5db96cd54ddab6ab737332417a09169b39))
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
### Performance Improvements
* 支持自动选择校验方式申请证书 ([3f99432](https://github.com/certd/certd/commit/3f9943270cfb12946e38e6272bc5e8d95ad6ab9e))
* OpenAPI支持autoApply参数 ([42f4d14](https://github.com/certd/certd/commit/42f4d1477dc791520a874aed56035abcbc8c433b))
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
**Note:** Version bump only for package @certd/plugin-cert **Note:** Version bump only for package @certd/plugin-cert

View File

@@ -1,7 +1,7 @@
{ {
"name": "@certd/plugin-cert", "name": "@certd/plugin-cert",
"private": false, "private": false,
"version": "1.36.5", "version": "1.36.7",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@@ -16,10 +16,10 @@
"pub": "npm publish" "pub": "npm publish"
}, },
"dependencies": { "dependencies": {
"@certd/acme-client": "^1.36.5", "@certd/acme-client": "^1.36.7",
"@certd/basic": "^1.36.5", "@certd/basic": "^1.36.7",
"@certd/pipeline": "^1.36.5", "@certd/pipeline": "^1.36.7",
"@certd/plugin-lib": "^1.36.5", "@certd/plugin-lib": "^1.36.7",
"@google-cloud/publicca": "^1.3.0", "@google-cloud/publicca": "^1.3.0",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"jszip": "^3.10.1", "jszip": "^3.10.1",
@@ -43,5 +43,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "c2a95a13fe6edf05ea0f72f5f7c76f9eea3ab0bd" "gitHead": "29d49d72f95aa101729965710375104240a2c038"
} }

View File

@@ -1,4 +1,4 @@
import { AbstractTaskPlugin, IContext, Step, TaskInput, TaskOutput } from "@certd/pipeline"; import { AbstractTaskPlugin, FileItem, IContext, Step, TaskInput, TaskOutput } from "@certd/pipeline";
import dayjs from "dayjs"; import dayjs from "dayjs";
import type { CertInfo } from "./acme.js"; import type { CertInfo } from "./acme.js";
import { CertReader } from "./cert-reader.js"; import { CertReader } from "./cert-reader.js";
@@ -71,6 +71,12 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
}) })
cert?: CertInfo; cert?: CertInfo;
@TaskOutput({
title: "域名证书压缩文件",
type: "certZip",
})
certZip?: FileItem;
async onInstance() { async onInstance() {
this.userContext = this.ctx.userContext; this.userContext = this.ctx.userContext;
this.lastStatus = this.ctx.lastStatus as Step; this.lastStatus = this.ctx.lastStatus as Step;
@@ -131,6 +137,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
} else { } else {
this.extendsFiles(); this.extendsFiles();
} }
this.certZip = this._result.files[0];
} }
async zipCert(cert: CertInfo, filename: string) { async zipCert(cert: CertInfo, filename: string) {

View File

@@ -470,6 +470,9 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
const domain = fullDomain.replaceAll("*.", ""); const domain = fullDomain.replaceAll("*.", "");
const mainDomain = await domainParser.parse(domain); const mainDomain = await domainParser.parse(domain);
const planSetting: DomainVerifyPlanInput = verifyPlanSetting[mainDomain]; const planSetting: DomainVerifyPlanInput = verifyPlanSetting[mainDomain];
if (planSetting == null) {
throw new Error(`没有找到域名(${domain})的校验计划`);
}
if (planSetting.type === "dns") { if (planSetting.type === "dns") {
plan[domain] = await this.createDnsDomainVerifyPlan(planSetting, domain, mainDomain); plan[domain] = await this.createDnsDomainVerifyPlan(planSetting, domain, mainDomain);
} else if (planSetting.type === "cname") { } else if (planSetting.type === "cname") {
@@ -498,6 +501,9 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
for (const domain in verifiers) { for (const domain in verifiers) {
const verifier = verifiers[domain]; const verifier = verifiers[domain];
if (verifier == null) {
throw new Error(`没有找到与该域名(${domain})匹配的校验方式,请先到‘域名管理’页面添加校验方式`);
}
if (verifier.type === "dns") { if (verifier.type === "dns") {
plan[domain] = await this.createDnsDomainVerifyPlan(verifier.dns, domain, verifier.mainDomain); plan[domain] = await this.createDnsDomainVerifyPlan(verifier.dns, domain, verifier.mainDomain);
} else if (verifier.type === "cname") { } else if (verifier.type === "cname") {

View File

@@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
### Bug Fixes
* 修复流水线页面状态没有刷新的bug ([93e9498](https://github.com/certd/certd/commit/93e9498b410353f504e11e264db62468895d7290))
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
**Note:** Version bump only for package @certd/plugin-lib
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
**Note:** Version bump only for package @certd/plugin-lib **Note:** Version bump only for package @certd/plugin-lib

View File

@@ -1,7 +1,7 @@
{ {
"name": "@certd/plugin-lib", "name": "@certd/plugin-lib",
"private": false, "private": false,
"version": "1.36.5", "version": "1.36.7",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@@ -21,8 +21,8 @@
"@alicloud/pop-core": "^1.7.10", "@alicloud/pop-core": "^1.7.10",
"@alicloud/tea-util": "^1.4.10", "@alicloud/tea-util": "^1.4.10",
"@aws-sdk/client-s3": "^3.787.0", "@aws-sdk/client-s3": "^3.787.0",
"@certd/basic": "^1.36.5", "@certd/basic": "^1.36.7",
"@certd/pipeline": "^1.36.5", "@certd/pipeline": "^1.36.7",
"@kubernetes/client-node": "0.21.0", "@kubernetes/client-node": "0.21.0",
"ali-oss": "^6.22.0", "ali-oss": "^6.22.0",
"basic-ftp": "^5.0.5", "basic-ftp": "^5.0.5",
@@ -53,5 +53,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "c2a95a13fe6edf05ea0f72f5f7c76f9eea3ab0bd" "gitHead": "29d49d72f95aa101729965710375104240a2c038"
} }

View File

@@ -16,6 +16,10 @@ export default class SshOssClientImpl extends BaseOssClient<SshAccess> {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
async upload(filePath: string, fileContent: Buffer) { async upload(filePath: string, fileContent: Buffer) {
if (!filePath) {
filePath = "";
}
filePath = filePath.trim();
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath); const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
// Write file to temp path // Write file to temp path

View File

@@ -3,6 +3,25 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
### Bug Fixes
* 修复流水线页面状态没有刷新的bug ([93e9498](https://github.com/certd/certd/commit/93e9498b410353f504e11e264db62468895d7290))
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
### Bug Fixes
* 修复某些页面翻译不全显示错误的bug ([0b3158f](https://github.com/certd/certd/commit/0b3158fdd5fe5bb0a98c4e65715dbc3de2c38047))
* 修复运行流水线后会闪烁一下的bug ([dfc9362](https://github.com/certd/certd/commit/dfc9362084082ee535b898f23b2609c1d946a6fd))
### Performance Improvements
* 通知和定时器的删除按钮显示为红色更显眼 ([61ba83c](https://github.com/certd/certd/commit/61ba83c77546c3d505d081e19a3d68c127662bf1))
* 优化流水线列表页面、详情页面性能,精简返回数据 ([609ac9c](https://github.com/certd/certd/commit/609ac9c9a2dde605eb09834ae59693c1cb238765))
* OpenAPI支持autoApply参数 ([42f4d14](https://github.com/certd/certd/commit/42f4d1477dc791520a874aed56035abcbc8c433b))
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
**Note:** Version bump only for package @certd/ui-client **Note:** Version bump only for package @certd/ui-client

View File

@@ -1,6 +1,6 @@
{ {
"name": "@certd/ui-client", "name": "@certd/ui-client",
"version": "1.36.5", "version": "1.36.7",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite --open", "dev": "vite --open",
@@ -103,8 +103,8 @@
"zod-defaults": "^0.1.3" "zod-defaults": "^0.1.3"
}, },
"devDependencies": { "devDependencies": {
"@certd/lib-iframe": "^1.36.5", "@certd/lib-iframe": "^1.36.7",
"@certd/pipeline": "^1.36.5", "@certd/pipeline": "^1.36.7",
"@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12", "@types/chai": "^4.3.12",

View File

@@ -46,6 +46,20 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const { openSiteIpMonitorDialog } = useSiteIpMonitor(); const { openSiteIpMonitorDialog } = useSiteIpMonitor();
const { openSiteImportDialog } = useSiteImport(); const { openSiteImportDialog } = useSiteImport();
function checkAll() {
Modal.confirm({
title: t("certd.monitor.confirmTitle"), // "确认"
content: t("certd.monitor.confirmContent"), // "确认触发检查全部站点证书吗?"
onOk: async () => {
await siteInfoApi.CheckAll();
notification.success({
message: t("certd.monitor.checkSubmitted"), // "检查任务已提交"
description: t("certd.monitor.pleaseRefresh"), // "请稍后刷新页面查看结果"
});
},
});
}
return { return {
id: "siteMonitorCrud", id: "siteMonitorCrud",
crudOptions: { crudOptions: {
@@ -114,6 +128,14 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}); });
}, },
}, },
checkAll: {
show: true,
text: t("certd.monitor.checkAll"),
type: "primary",
click() {
checkAll();
},
},
}, },
}, },
rowHandle: { rowHandle: {

View File

@@ -14,9 +14,6 @@
</div> </div>
</div> </div>
</div> </div>
<div class="more">
<a-button type="primary" @click="checkAll">{{ t("certd.monitor.checkAll") }}</a-button>
</div>
</template> </template>
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud> <fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
</fs-page> </fs-page>
@@ -35,19 +32,6 @@ defineOptions({
name: "SiteCertMonitor", name: "SiteCertMonitor",
}); });
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} }); const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: {} });
function checkAll() {
Modal.confirm({
title: t("certd.monitor.confirmTitle"), // "确认"
content: t("certd.monitor.confirmContent"), // "确认触发检查全部站点证书吗?"
onOk: async () => {
await siteInfoApi.CheckAll();
notification.success({
message: t("certd.monitor.checkSubmitted"), // "检查任务已提交"
description: t("certd.monitor.pleaseRefresh"), // "请稍后刷新页面查看结果"
});
},
});
}
// 页面打开后获取列表数据 // 页面打开后获取列表数据
onMounted(() => { onMounted(() => {

View File

@@ -125,6 +125,7 @@ export function useCertPipelineCreator() {
const pluginStore = usePluginStore(); const pluginStore = usePluginStore();
const randomHour = Math.floor(Math.random() * 6); const randomHour = Math.floor(Math.random() * 6);
const randomMin = Math.floor(Math.random() * 60); const randomMin = Math.floor(Math.random() * 60);
const randomCron = `0 ${randomMin} ${randomHour} * * *`;
const groupDictRef = dict({ const groupDictRef = dict({
url: "/pi/pipeline/group/all", url: "/pi/pipeline/group/all",
@@ -193,7 +194,7 @@ export function useCertPipelineCreator() {
title: t("certd.pipelineForm.triggerCronTitle"), title: t("certd.pipelineForm.triggerCronTitle"),
type: "text", type: "text",
form: { form: {
value: `0 ${randomMin} ${randomHour} * * *`, value: randomCron,
component: { component: {
name: "cron-editor", name: "cron-editor",
vModel: "modelValue", vModel: "modelValue",

View File

@@ -335,7 +335,7 @@ export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys
} }
}, },
}, },
_triggerCount: { triggerCount: {
title: t("certd.fields.scheduledTaskCount"), title: t("certd.fields.scheduledTaskCount"),
type: "number", type: "number",
column: { column: {
@@ -346,7 +346,7 @@ export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys
show: false, show: false,
}, },
}, },
_stepCount: { stepCount: {
title: t("certd.fields.deployTaskCount"), title: t("certd.fields.deployTaskCount"),
type: "number", type: "number",
form: { show: false }, form: { show: false },

View File

@@ -4,7 +4,7 @@
<fs-icon v-bind="status" :color="status.iconColor || status.color" /> <fs-icon v-bind="status" :color="status.iconColor || status.color" />
</template> </template>
<p> <p>
<fs-date-format :model-value="runnable.status?.startTime"></fs-date-format> <fs-date-format :model-value="runnable.createTime"></fs-date-format>
<a-tag class="ml-5" :color="status.color" :closable="status.value === 'start'" @close="cancelTask"> <a-tag class="ml-5" :color="status.color" :closable="status.value === 'start'" @close="cancelTask">
{{ status.label }} {{ status.label }}
</a-tag> </a-tag>
@@ -46,7 +46,7 @@ export default defineComponent({
emits: ["view", "cancel"], emits: ["view", "cancel"],
setup(props: any, ctx: any) { setup(props: any, ctx: any) {
const status = computed(() => { const status = computed(() => {
return statusUtil.get(props.runnable?.status?.result); return statusUtil.get(props.runnable?.status);
}); });
function view() { function view() {

View File

@@ -3,7 +3,7 @@
<template #title> <template #title>
<div> <div>
{{ t("certd.edit_notification") }} {{ t("certd.edit_notification") }}
<a-button v-if="mode === 'edit'" @click="notificationDelete()"> <a-button v-if="mode === 'edit'" danger @click="notificationDelete()">
<template #icon> <template #icon>
<DeleteOutlined /> <DeleteOutlined />
</template> </template>

View File

@@ -3,7 +3,7 @@
<template #title> <template #title>
<div> <div>
编辑任务 编辑任务
<a-button v-if="editMode" @click="taskDelete()"> <a-button v-if="editMode" danger @click="taskDelete()">
<template #icon><DeleteOutlined /></template> <template #icon><DeleteOutlined /></template>
</a-button> </a-button>
</div> </div>

View File

@@ -3,7 +3,7 @@
<template #title> <template #title>
<div> <div>
{{ t("certd.editTrigger") }} {{ t("certd.editTrigger") }}
<a-button v-if="mode === 'edit'" @click="triggerDelete()"> <a-button v-if="mode === 'edit'" danger @click="triggerDelete()">
<template #icon> <template #icon>
<DeleteOutlined /> <DeleteOutlined />
</template> </template>

View File

@@ -258,7 +258,7 @@
<a-timeline class="mt-10"> <a-timeline class="mt-10">
<template v-for="item of histories" :key="item.id"> <template v-for="item of histories" :key="item.id">
<pi-history-timeline-item <pi-history-timeline-item
:runnable="item.pipeline" :runnable="item"
:history-id="item.id" :history-id="item.id"
:is-current="currentHistory?.id === item.id" :is-current="currentHistory?.id === item.id"
:edit-mode="editMode" :edit-mode="editMode"
@@ -280,7 +280,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onMounted, onUnmounted, provide, Ref, ref, watch, computed } from "vue"; import { computed, defineComponent, onMounted, onUnmounted, provide, ref, Ref, watch } from "vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import PiTaskForm from "./component/task-form/index.vue"; import PiTaskForm from "./component/task-form/index.vue";
import PiTriggerForm from "./component/trigger-form/index.vue"; import PiTriggerForm from "./component/trigger-form/index.vue";
@@ -288,7 +288,7 @@ import PiNotificationForm from "./component/notification-form/index.vue";
import PiTaskView from "./component/task-view/index.vue"; import PiTaskView from "./component/task-view/index.vue";
import PiStatusShow from "./component/status-show.vue"; import PiStatusShow from "./component/status-show.vue";
import VDraggable from "vuedraggable"; import VDraggable from "vuedraggable";
import * as _ from "lodash-es"; import { cloneDeep, merge, remove } from "lodash-es";
import { message, Modal, notification } from "ant-design-vue"; import { message, Modal, notification } from "ant-design-vue";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { PipelineDetail, PipelineOptions, RunHistory } from "./type"; import { PipelineDetail, PipelineOptions, RunHistory } from "./type";
@@ -299,9 +299,10 @@ import { useSettingStore } from "/@/store/settings";
import { useUserStore } from "/@/store/user"; import { useUserStore } from "/@/store/user";
import TaskShortcuts from "./component/shortcut/task-shortcuts.vue"; import TaskShortcuts from "./component/shortcut/task-shortcuts.vue";
import { eachSteps, findStep } from "../utils"; import { eachSteps, findStep } from "../utils";
import { PluginGroups, usePluginStore } from "/@/store/plugin"; import { usePluginStore } from "/@/store/plugin";
import { getCronNextTimes } from "/@/components/cron-editor/utils"; import { getCronNextTimes } from "/@/components/cron-editor/utils";
import { useCertViewer } from "/@/views/certd/pipeline/use"; import { useCertViewer } from "/@/views/certd/pipeline/use";
import { useI18n } from "/@/locales";
export default defineComponent({ export default defineComponent({
name: "PipelineEdit", name: "PipelineEdit",
@@ -339,6 +340,7 @@ export default defineComponent({
}, },
emits: ["update:modelValue", "update:editMode"], emits: ["update:modelValue", "update:editMode"],
setup(props, ctx) { setup(props, ctx) {
const { t } = useI18n();
const currentPipeline: Ref<any> = ref({}); const currentPipeline: Ref<any> = ref({});
const pipeline: Ref<any> = ref({}); const pipeline: Ref<any> = ref({});
@@ -371,18 +373,20 @@ export default defineComponent({
const loadCurrentHistoryDetail = async () => { const loadCurrentHistoryDetail = async () => {
const detail: RunHistory = await props.options?.getHistoryDetail({ historyId: currentHistory.value.id }); const detail: RunHistory = await props.options?.getHistoryDetail({ historyId: currentHistory.value.id });
currentHistory.value.logs = detail.logs; currentHistory.value.logs = detail.logs;
_.merge(currentHistory.value.pipeline, detail.pipeline); currentHistory.value.pipeline = detail.pipeline;
currentHistory.value.status = detail.pipeline.status.result;
}; };
const changeCurrentHistory = async (history?: RunHistory) => { const changeCurrentHistory = async (history?: RunHistory) => {
if (!history) { if (!history) {
//取消历史记录查看模式 //取消历史记录查看模式
currentHistory.value = null; currentHistory.value = null;
pipeline.value = currentPipeline.value; pipeline.value = cloneDeep(currentPipeline.value);
return; return;
} }
currentHistory.value = history; currentHistory.value = history;
pipeline.value = history.pipeline;
await loadCurrentHistoryDetail(); await loadCurrentHistoryDetail();
pipeline.value = currentHistory.value.pipeline;
currentPipeline.value = currentHistory.value.pipeline;
}; };
async function loadHistoryList(reload = false) { async function loadHistoryList(reload = false) {
@@ -413,7 +417,8 @@ export default defineComponent({
return true; return true;
} }
} }
if (historyList[0].pipeline?.version === pipeline.value.version) { //@ts-ignore
if (historyList[0]?.version === pipeline.value.version) {
await changeCurrentHistory(historyList[0]); await changeCurrentHistory(historyList[0]);
} }
} }
@@ -435,8 +440,13 @@ export default defineComponent({
} }
if (currentHistory.value != null) { if (currentHistory.value != null) {
if (currentHistory.value.pipeline?.status?.status === "start") { if (currentHistory.value.status === "start") {
await loadCurrentHistoryDetail(); await loadCurrentHistoryDetail();
pipeline.value = currentHistory.value.pipeline;
// if (currentHistory.value.pipeline?.status?.status !== "start") {
// 不传true好像不会刷新
// await loadHistoryList(true);
// }
} }
} }
} catch (e) { } catch (e) {
@@ -477,7 +487,7 @@ export default defineComponent({
return; return;
} }
const detail: PipelineDetail = await props.options.getPipelineDetail({ pipelineId: value }); const detail: PipelineDetail = await props.options.getPipelineDetail({ pipelineId: value });
currentPipeline.value = _.merge( currentPipeline.value = merge(
{ {
title: "新管道流程", title: "新管道流程",
stages: [], stages: [],
@@ -540,7 +550,7 @@ export default defineComponent({
}; };
const taskCopy = (stage: any, stageIndex: number, task: any) => { const taskCopy = (stage: any, stageIndex: number, task: any) => {
task = _.cloneDeep(task); task = cloneDeep(task);
task.id = nanoid(); task.id = nanoid();
task.title = task.title + "_copy"; task.title = task.title + "_copy";
for (const step of task.steps) { for (const step of task.steps) {
@@ -560,7 +570,7 @@ export default defineComponent({
if (type === "delete") { if (type === "delete") {
stage.tasks.splice(taskIndex, 1); stage.tasks.splice(taskIndex, 1);
if (stage.tasks.length === 0) { if (stage.tasks.length === 0) {
_.remove(pipeline.value.stages, (item: Runnable) => { remove(pipeline.value.stages, (item: Runnable) => {
return item.id === stage.id; return item.id === stage.id;
}); });
} }
@@ -666,9 +676,19 @@ export default defineComponent({
notificationFormRef.value.notificationView(notification, (type: string, value: any) => {}); notificationFormRef.value.notificationView(notification, (type: string, value: any) => {});
} }
}; };
const notificationDelete = (notification: any, index: any) => {
Modal.confirm({
title: t("certd.confirm"),
content: t("certd.confirm_delete_trigger"),
async onOk() {
pipeline.value.notifications.splice(index, 1);
},
});
};
return { return {
notificationAdd, notificationAdd,
notificationEdit, notificationEdit,
notificationDelete,
notificationFormRef, notificationFormRef,
}; };
} }
@@ -788,7 +808,7 @@ export default defineComponent({
currentPipeline.value = pipeline.value; currentPipeline.value = pipeline.value;
//移除空阶段 //移除空阶段
_.remove(pipeline.value.stages, (item: Stage) => { remove(pipeline.value.stages, (item: Stage) => {
return item.tasks.length === 0; return item.tasks.length === 0;
}); });
@@ -802,12 +822,12 @@ export default defineComponent({
} }
}; };
const edit = () => { const edit = () => {
pipeline.value = _.cloneDeep(currentPipeline.value); pipeline.value = cloneDeep(currentPipeline.value);
currentHistory.value = null; currentHistory.value = null;
toggleEditMode(true); toggleEditMode(true);
}; };
const cancel = () => { const cancel = () => {
pipeline.value = currentPipeline.value; pipeline.value = cloneDeep(currentPipeline.value);
toggleEditMode(false); toggleEditMode(false);
}; };

View File

@@ -214,6 +214,7 @@ function transformStatusCount() {
{ name: "error", label: "失败" }, { name: "error", label: "失败" },
{ name: "canceled", label: "已取消" }, { name: "canceled", label: "已取消" },
{ name: null, label: "未执行" }, { name: null, label: "未执行" },
{ name: "skip", label: "跳过" },
]; ];
const result = []; const result = [];
for (const item of sorted) { for (const item of sorted) {

View File

@@ -43,7 +43,7 @@ const slots = defineSlots();
.data-item { .data-item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 180px; height: 188px;
.header { .header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@@ -3,6 +3,27 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.36.7](https://github.com/certd/certd/compare/v1.36.6...v1.36.7) (2025-07-15)
### Bug Fixes
* 修复流水线列表页报length错误的bug ([9864792](https://github.com/certd/certd/commit/9864792bbfd149e770d6e1ffa809573694f99dd3))
* 修复流水线页面状态没有刷新的bug ([93e9498](https://github.com/certd/certd/commit/93e9498b410353f504e11e264db62468895d7290))
* 修复自定义证书检查时间重启之后不生效的bug ([38e867c](https://github.com/certd/certd/commit/38e867c917bbc68bd228bdd8064f3e7358d6413d))
### Performance Improvements
* 支持上传证书到各种对象存储oss、cos、七牛、s3、minio等 ([1da8617](https://github.com/certd/certd/commit/1da8617a53a675776635bbc3bcb3c6d7dff83e27))
* 支持邮箱发送证书 ([95332d5](https://github.com/certd/certd/commit/95332d5db96cd54ddab6ab737332417a09169b39))
## [1.36.6](https://github.com/certd/certd/compare/v1.36.5...v1.36.6) (2025-07-14)
### Performance Improvements
* 优化流水线列表页面、详情页面性能,精简返回数据 ([609ac9c](https://github.com/certd/certd/commit/609ac9c9a2dde605eb09834ae59693c1cb238765))
* 支持自动选择校验方式申请证书 ([3f99432](https://github.com/certd/certd/commit/3f9943270cfb12946e38e6272bc5e8d95ad6ab9e))
* OpenAPI支持autoApply参数 ([42f4d14](https://github.com/certd/certd/commit/42f4d1477dc791520a874aed56035abcbc8c433b))
## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11) ## [1.36.5](https://github.com/certd/certd/compare/v1.36.4...v1.36.5) (2025-07-11)
### Bug Fixes ### Bug Fixes

View File

@@ -0,0 +1,20 @@
DROP TABLE IF EXISTS `cd_domain`;
CREATE TABLE `cd_domain`
(
`id` bigint PRIMARY KEY AUTO_INCREMENT NOT NULL,
`user_id` bigint,
`domain` varchar(512),
challenge_type varchar(50),
dns_provider_type varchar(50),
dns_provider_access bigint,
http_uploader_type varchar(50),
http_uploader_access bigint,
http_upload_root_dir varchar(512),
`disabled` boolean NOT NULL DEFAULT false,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX `index_domain_user_id` ON `cd_domain` (`user_id`);
CREATE INDEX `index_domain_domain` ON `cd_domain` (`domain`);

View File

@@ -0,0 +1,20 @@
DROP TABLE IF EXISTS "cd_domain";
CREATE TABLE "cd_domain"
(
"id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL,
"user_id" bigint,
"domain" varchar(512),
challenge_type varchar(50),
dns_provider_type varchar(50),
dns_provider_access bigint,
http_uploader_type varchar(50),
http_uploader_access bigint,
http_upload_root_dir varchar(512),
"disabled" boolean NOT NULL DEFAULT (false),
"create_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP),
"update_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP)
);
CREATE INDEX "index_domain_user_id" ON "cd_domain" ("user_id");
CREATE INDEX "index_domain_domain" ON "cd_domain" ("domain");

View File

@@ -1,8 +1,9 @@
DROP TABLE IF EXISTS "cd_domain";
CREATE TABLE "cd_domain" CREATE TABLE "cd_domain"
( (
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
"user_id" integer, "user_id" integer,
"domain" varchar(1024), "domain" varchar(512),
challenge_type varchar(50), challenge_type varchar(50),
dns_provider_type varchar(50), dns_provider_type varchar(50),
dns_provider_access bigint, dns_provider_access bigint,

View File

@@ -1,6 +1,6 @@
{ {
"name": "@certd/ui-server", "name": "@certd/ui-server",
"version": "1.36.5", "version": "1.36.7",
"description": "fast-server base midway", "description": "fast-server base midway",
"private": true, "private": true,
"type": "module", "type": "module",
@@ -42,20 +42,20 @@
"@aws-sdk/client-cloudfront": "^3.699.0", "@aws-sdk/client-cloudfront": "^3.699.0",
"@aws-sdk/client-iam": "^3.699.0", "@aws-sdk/client-iam": "^3.699.0",
"@aws-sdk/client-s3": "^3.705.0", "@aws-sdk/client-s3": "^3.705.0",
"@certd/acme-client": "^1.36.5", "@certd/acme-client": "^1.36.7",
"@certd/basic": "^1.36.5", "@certd/basic": "^1.36.7",
"@certd/commercial-core": "^1.36.5", "@certd/commercial-core": "^1.36.7",
"@certd/cv4pve-api-javascript": "^8.4.1", "@certd/cv4pve-api-javascript": "^8.4.1",
"@certd/jdcloud": "^1.36.5", "@certd/jdcloud": "^1.36.7",
"@certd/lib-huawei": "^1.36.5", "@certd/lib-huawei": "^1.36.7",
"@certd/lib-k8s": "^1.36.5", "@certd/lib-k8s": "^1.36.7",
"@certd/lib-server": "^1.36.5", "@certd/lib-server": "^1.36.7",
"@certd/midway-flyway-js": "^1.36.5", "@certd/midway-flyway-js": "^1.36.7",
"@certd/pipeline": "^1.36.5", "@certd/pipeline": "^1.36.7",
"@certd/plugin-cert": "^1.36.5", "@certd/plugin-cert": "^1.36.7",
"@certd/plugin-lib": "^1.36.5", "@certd/plugin-lib": "^1.36.7",
"@certd/plugin-plus": "^1.36.5", "@certd/plugin-plus": "^1.36.7",
"@certd/plus-core": "^1.36.5", "@certd/plus-core": "^1.36.7",
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120", "@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120", "@huaweicloud/huaweicloud-sdk-core": "^3.1.120",
"@koa/cors": "^5.0.0", "@koa/cors": "^5.0.0",

View File

@@ -1,13 +1,15 @@
import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from '@midwayjs/core'; import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from "@midwayjs/core";
import { CodeException, Constants, EncryptService } from '@certd/lib-server'; import { CodeException, Constants, EncryptService } from "@certd/lib-server";
import { CertInfoService } from '../../../modules/monitor/service/cert-info-service.js'; import { CertInfo } from "@certd/plugin-cert";
import { CertInfo } from '@certd/plugin-cert'; import { OpenKey } from "../../../modules/open/service/open-key-service.js";
import { OpenKey } from '../../../modules/open/service/open-key-service.js'; import { BaseOpenController } from "../base-open-controller.js";
import { BaseOpenController } from '../base-open-controller.js'; import { CertInfoFacade } from "../../../modules/monitor/facade/cert-info-facade.js";
import { merge } from "lodash-es";
export type CertGetReq = { export type CertGetReq = {
domains?: string; domains?: string;
certId: number; certId: number;
autoApply?:boolean;
}; };
/** /**
@@ -16,7 +18,7 @@ export type CertGetReq = {
@Controller('/api/v1/cert') @Controller('/api/v1/cert')
export class OpenCertController extends BaseOpenController { export class OpenCertController extends BaseOpenController {
@Inject() @Inject()
certInfoService: CertInfoService; certInfoFacade: CertInfoFacade;
@Inject() @Inject()
encryptService: EncryptService; encryptService: EncryptService;
@@ -29,10 +31,13 @@ export class OpenCertController extends BaseOpenController {
throw new CodeException(Constants.res.openKeyError); throw new CodeException(Constants.res.openKeyError);
} }
const res: CertInfo = await this.certInfoService.getCertInfo({ const req = merge({}, bean, query)
const res: CertInfo = await this.certInfoFacade.getCertInfo({
userId, userId,
domains: bean.domains || query.domains, domains: req.domains,
certId: bean.certId || query.certId, certId: req.certId,
autoApply: req.autoApply??false,
}); });
return this.ok(res); return this.ok(res);
} }

View File

@@ -1,16 +1,15 @@
import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from '@midwayjs/core'; import { ALL, Body, Controller, Get, Inject, Post, Provide, Query } from "@midwayjs/core";
import { CommonException, Constants, CrudController, PermissionException } from '@certd/lib-server'; import { CommonException, Constants, CrudController, PermissionException, SysSettingsService } from "@certd/lib-server";
import { PipelineEntity } from '../../../modules/pipeline/entity/pipeline.js'; import { PipelineEntity } from "../../../modules/pipeline/entity/pipeline.js";
import { HistoryService } from '../../../modules/pipeline/service/history-service.js'; import { HistoryService } from "../../../modules/pipeline/service/history-service.js";
import { HistoryLogService } from '../../../modules/pipeline/service/history-log-service.js'; import { HistoryLogService } from "../../../modules/pipeline/service/history-log-service.js";
import { HistoryEntity } from '../../../modules/pipeline/entity/history.js'; import { HistoryEntity } from "../../../modules/pipeline/entity/history.js";
import { HistoryLogEntity } from '../../../modules/pipeline/entity/history-log.js'; import { HistoryLogEntity } from "../../../modules/pipeline/entity/history-log.js";
import { PipelineService } from '../../../modules/pipeline/service/pipeline-service.js'; import { PipelineService } from "../../../modules/pipeline/service/pipeline-service.js";
import * as fs from 'fs'; import * as fs from "fs";
import { logger } from '@certd/basic'; import { logger } from "@certd/basic";
import { AuthService } from '../../../modules/sys/authority/service/auth-service.js'; import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
import { SysSettingsService } from '@certd/lib-server'; import { In } from "typeorm";
import { In } from 'typeorm';
/** /**
* 证书 * 证书
@@ -88,11 +87,30 @@ export class HistoryController extends CrudController<HistoryService> {
const buildQuery = qb => { const buildQuery = qb => {
qb.limit(20); qb.limit(20);
}; };
const withDetail = body.withDetail;
delete body.withDetail;
let select:any = null
if (!withDetail) {
select = {
pipeline: true, // 后面这里改成false
id: true,
userId: true,
pipelineId: true,
status: true,
// startTime: true,
triggerType: true,
endTime: true,
createTime: true,
updateTime: true
};
}
const listRet = await this.getService().list({ const listRet = await this.getService().list({
query: body, query: body,
sort: { prop: 'id', asc: false }, sort: { prop: 'id', asc: false },
buildQuery, buildQuery,
select
}); });
for (const item of listRet) { for (const item of listRet) {
if (!item.pipeline) { if (!item.pipeline) {
continue; continue;
@@ -100,7 +118,13 @@ export class HistoryController extends CrudController<HistoryService> {
const json = JSON.parse(item.pipeline); const json = JSON.parse(item.pipeline);
delete json.stages; delete json.stages;
item.pipeline = json; item.pipeline = json;
//@ts-ignore
item.version = json.version;
item.status = json.status.result
delete item.pipeline;
} }
return this.ok(listRet); return this.ok(listRet);
} }

View File

@@ -71,7 +71,7 @@ export class AutoCRegisterCron {
} }
}) })
for (const item of monitorSettingList) { for (const item of monitorSettingList) {
const setting = item.setting ?? JSON.parse(item.setting) const setting = item.setting ? JSON.parse(item.setting):{}
if(!setting?.cron){ if(!setting?.cron){
continue continue
} }

View File

@@ -98,6 +98,8 @@ export class EmailService implements IEmailService {
to: email.receivers.join(', '), // list of receivers to: email.receivers.join(', '), // list of receivers
subject: subject, subject: subject,
text: email.content, text: email.content,
html: email.html,
attachments: email.attachments,
}; };
await transporter.sendMail(mailOptions); await transporter.sendMail(mailOptions);
} }

View File

@@ -154,7 +154,7 @@ export class DomainService extends BaseService<DomainEntity> {
} }
continue continue
} }
const cnameRecord:CnameRecordEntity = cnameMap[mainDomain] const cnameRecord:CnameRecordEntity = cnameMap[domain]
if (cnameRecord) { if (cnameRecord) {
domainVerifiers[domain] = { domainVerifiers[domain] = {
domain, domain,
@@ -168,7 +168,7 @@ export class DomainService extends BaseService<DomainEntity> {
} }
continue continue
} }
const httpRecord = httpMap[mainDomain] const httpRecord = httpMap[domain]
if (httpRecord) { if (httpRecord) {
domainVerifiers[domain] = { domainVerifiers[domain] = {
domain, domain,

View File

@@ -1,19 +1,27 @@
import {Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core'; import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import {InjectEntityModel} from '@midwayjs/typeorm'; import { InjectEntityModel } from "@midwayjs/typeorm";
import {Repository} from 'typeorm'; import { Repository } from "typeorm";
import {AccessService, BaseService, PlusService, ValidateException} from '@certd/lib-server'; import {
import {CnameRecordEntity, CnameRecordStatusType} from '../entity/cname-record.js'; AccessService,
import {createDnsProvider, IDnsProvider} from '@certd/plugin-cert'; BaseService,
import {CnameProvider, CnameRecord} from '@certd/pipeline'; PlusService,
import {cache, http, isDev, logger, utils} from '@certd/basic'; SysInstallInfo,
import {getAuthoritativeDnsResolver, walkTxtRecord} from '@certd/acme-client'; SysSettingsService,
import {CnameProviderService} from './cname-provider-service.js'; ValidateException
import {CnameProviderEntity} from '../entity/cname-provider.js'; } from "@certd/lib-server";
import {CommonDnsProvider} from './common-provider.js'; import { CnameRecordEntity, CnameRecordStatusType } from "../entity/cname-record.js";
import {DomainParser} from "@certd/plugin-cert/dist/dns-provider/domain-parser.js"; import { createDnsProvider, IDnsProvider } from "@certd/plugin-cert";
import punycode from 'punycode.js' import { CnameProvider, CnameRecord } from "@certd/pipeline";
import {SubDomainService} from "../../pipeline/service/sub-domain-service.js"; import { cache, http, isDev, logger, utils } from "@certd/basic";
import {SubDomainsGetter} from "../../pipeline/service/getter/sub-domain-getter.js"; import { getAuthoritativeDnsResolver, walkTxtRecord } from "@certd/acme-client";
import { CnameProviderService } from "./cname-provider-service.js";
import { CnameProviderEntity } from "../entity/cname-provider.js";
import { CommonDnsProvider } from "./common-provider.js";
import { DomainParser } from "@certd/plugin-cert/dist/dns-provider/domain-parser.js";
import punycode from "punycode.js";
import { SubDomainService } from "../../pipeline/service/sub-domain-service.js";
import { SubDomainsGetter } from "../../pipeline/service/getter/sub-domain-getter.js";
type CnameCheckCacheValue = { type CnameCheckCacheValue = {
validating: boolean; validating: boolean;
pass: boolean; pass: boolean;
@@ -35,6 +43,8 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
@Inject() @Inject()
cnameProviderService: CnameProviderService; cnameProviderService: CnameProviderService;
@Inject()
sysSettingsService: SysSettingsService;
@Inject() @Inject()
accessService: AccessService; accessService: AccessService;
@@ -101,7 +111,17 @@ export class CnameRecordService extends BaseService<CnameRecordEntity> {
} }
param.hostRecord = hostRecord; param.hostRecord = hostRecord;
const cnameKey = utils.id.simpleNanoId().toLowerCase(); const randomKey = utils.id.simpleNanoId(6).toLowerCase();
let userIdHash = ""
if(param.cnameProviderId < 0){
//公共cname服务
userIdHash = utils.hash.md5(`userId${userId}_${randomKey}`).substring(0, 10)
}else{
const installInfo = await this.sysSettingsService.getSetting<SysInstallInfo>(SysInstallInfo)
userIdHash = utils.hash.md5(`${installInfo.siteId}_${randomKey}`).substring(0, 10)
}
const cnameKey = `${userIdHash}-${randomKey}`;
const safeDomain = param.domain.replaceAll('.', '-'); const safeDomain = param.domain.replaceAll('.', '-');
param.recordValue = `${safeDomain}.${cnameKey}.${cnameProvider.domain}`; param.recordValue = `${safeDomain}.${cnameKey}.${cnameProvider.domain}`;
} }

View File

@@ -0,0 +1,121 @@
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { CodeException, Constants } from "@certd/lib-server";
import { CertInfoEntity } from "../entity/cert-info.js";
import { utils } from "@certd/basic";
import { PipelineService } from "../../pipeline/service/pipeline-service.js";
import { UserSettingsService } from "../../mine/service/user-settings-service.js";
import { UserEmailSetting } from "../../mine/service/models.js";
import { PipelineEntity } from "../../pipeline/entity/pipeline.js";
import { CertInfoService } from "../service/cert-info-service.js";
import { DomainService } from "../../cert/service/domain-service.js";
import { DomainVerifierGetter } from "../../pipeline/service/getter/domain-verifier-getter.js";
@Provide("CertInfoFacade")
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class CertInfoFacade {
@Inject()
pipelineService: PipelineService;
@Inject()
certInfoService: CertInfoService ;
@Inject()
domainService: DomainService
@Inject()
userSettingsService : UserSettingsService
async getCertInfo(req: { domains?: string; certId?: number; userId: number,autoApply?:boolean }) {
const { domains, certId, userId } = req;
if (certId) {
return await this.certInfoService.getCertInfoById({ id: certId, userId });
}
const domainArr = domains.split(',');
const matchedList = await this.certInfoService.getMatchCertList({domains:domainArr,userId})
let matched: CertInfoEntity = null
if (matchedList.length === 0 ) {
if(req.autoApply === true){
//自动申请,先创建自动申请流水线
const pipeline:PipelineEntity = await this.createAutoPipeline({domains:domainArr,userId})
await this.triggerApplyPipeline({pipelineId:pipeline.id})
}else{
throw new CodeException(Constants.res.openCertNotFound);
}
}
matched = null;
for (const item of matchedList) {
if (item.expiresTime>0 && item.expiresTime < new Date().getTime()) {
matched = item;
break
}
}
if (!matched) {
if(req.autoApply === true){
//如果没有找到有效期内的证书,则自动触发一次申请
const first = matchedList[0]
await this.triggerApplyPipeline({pipelineId:first.pipelineId})
return
}else{
throw new CodeException(Constants.res.openCertNotFound);
}
}
return await this.certInfoService.getCertInfoById({ id: matched.id, userId: userId });
}
async createAutoPipeline(req:{domains:string[],userId:number}){
const verifierGetter = new DomainVerifierGetter(req.userId, this.domainService)
const allDomains = []
for (const item of req.domains) {
allDomains.push(item.replaceAll("*.",""))
}
const verifiers = await verifierGetter.getVerifiers(allDomains)
for (const item of allDomains) {
if (!verifiers[item]){
throw new CodeException({
...Constants.res.openDomainNoVerifier,
message:`域名${item}没有配置校验方式,请先在域名管理页面配置`,
data:{
domain:item
}
});
}
}
const userEmailSetting = await this.userSettingsService.getSetting<UserEmailSetting>(req.userId,UserEmailSetting)
if(!userEmailSetting.list){
throw new CodeException(Constants.res.openEmailNotFound)
}
const email = userEmailSetting.list[0]
return await this.pipelineService.createAutoPipeline({
domains: req.domains,
email,
userId: req.userId,
from:"OpenAPI"
})
}
async triggerApplyPipeline(req:{pipelineId:number}){
//查询流水线状态
const status = await this.pipelineService.getStatus(req.pipelineId)
if (status != 'running') {
await this.pipelineService.trigger(req.pipelineId)
await utils.sleep(1000)
}
throw new CodeException({
...Constants.res.openCertApplying,
data:{
pipelineId:req.pipelineId
}
});
}
}

View File

@@ -1,10 +1,10 @@
import {Provide, Scope, ScopeEnum} from "@midwayjs/core"; import { Provide, Scope, ScopeEnum } from "@midwayjs/core";
import {BaseService, CodeException, Constants, PageReq} from "@certd/lib-server"; import { BaseService, CodeException, Constants, PageReq } from "@certd/lib-server";
import {InjectEntityModel} from "@midwayjs/typeorm"; import { InjectEntityModel } from "@midwayjs/typeorm";
import {MoreThan, Repository} from "typeorm"; import { Repository } from "typeorm";
import {CertInfoEntity} from "../entity/cert-info.js"; import { CertInfoEntity } from "../entity/cert-info.js";
import {utils} from "@certd/basic"; import { utils } from "@certd/basic";
import {CertInfo, CertReader} from "@certd/plugin-cert"; import { CertInfo, CertReader } from "@certd/plugin-cert";
export type UploadCertReq = { export type UploadCertReq = {
id?: number; id?: number;
@@ -71,6 +71,7 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
} }
await this.addOrUpdate(bean); await this.addOrUpdate(bean);
return bean.id
} }
async deleteByPipelineId(id: number) { async deleteByPipelineId(id: number) {
@@ -82,44 +83,28 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
}); });
} }
async getCertInfo(params: { domains?: string; certId?: number; userId: number }) { async getMatchCertList(params: { domains: string[]; userId: number }) {
const { domains, certId, userId } = params;
if (certId) {
return await this.getCertInfoById({ id: certId, userId });
}
return await this.getCertInfoByDomains({
domains,
userId,
});
}
private async getCertInfoByDomains(params: { domains: string; userId: number }) {
const { domains, userId } = params; const { domains, userId } = params;
if (!domains) { if (!domains) {
throw new CodeException(Constants.res.openCertNotFound); throw new CodeException(Constants.res.openCertNotFound);
} }
const domainArr = domains.split(',');
const list = await this.find({ const list = await this.find({
select: { select: {
id: true, id: true,
domains: true, domains: true,
expiresTime:true,
pipelineId:true,
}, },
where: { where: {
userId, userId,
expiresTime: MoreThan(new Date().getTime())
}, },
}); });
//遍历查找 //遍历查找
const matched = list.find(item => { return list.filter(item => {
const itemDomains = item.domains.split(','); const itemDomains = item.domains.split(',');
return utils.domain.match(domainArr, itemDomains); return utils.domain.match(domains, itemDomains);
}); });
if (!matched) {
throw new CodeException(Constants.res.openCertNotFound);
}
return await this.getCertInfoById({ id: matched.id, userId: userId });
} }
async getCertInfoById(req: { id: number; userId: number }) { async getCertInfoById(req: { id: number; userId: number }) {

View File

@@ -1,22 +1,21 @@
import {IServiceGetter} from "@certd/pipeline"; import { IServiceGetter } from "@certd/pipeline";
import {Inject, Provide, Scope, ScopeEnum} from "@midwayjs/core"; import { ApplicationContext, IMidwayContainer, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import {AccessGetter, AccessService} from "@certd/lib-server"; import { AccessGetter, AccessService } from "@certd/lib-server";
import {CnameProxyService} from "./cname-proxy-service.js"; import { CnameProxyService } from "./cname-proxy-service.js";
import {NotificationGetter} from "./notification-getter.js"; import { NotificationGetter } from "./notification-getter.js";
import {NotificationService} from "../notification-service.js"; import { NotificationService } from "../notification-service.js";
import {CnameRecordService} from "../../../cname/service/cname-record-service.js"; import { CnameRecordService } from "../../../cname/service/cname-record-service.js";
import {SubDomainsGetter} from './sub-domain-getter.js' import { SubDomainsGetter } from "./sub-domain-getter.js";
import {DomainVerifierGetter} from "./domain-verifier-getter.js"; import { DomainVerifierGetter } from "./domain-verifier-getter.js";
import {Context} from "@midwayjs/koa"; import { DomainService } from "../../../cert/service/domain-service.js";
import {DomainService} from "../../../cert/service/domain-service.js"; import { SubDomainService } from "../sub-domain-service.js";
import {SubDomainService} from "../sub-domain-service.js";
export class TaskServiceGetter implements IServiceGetter{ export class TaskServiceGetter implements IServiceGetter{
private userId: number; private userId: number;
private ctx : Context; private appCtx : IMidwayContainer;
constructor(userId:number,ctx:Context) { constructor(userId:number,appCtx:IMidwayContainer) {
this.userId = userId; this.userId = userId;
this.ctx = ctx this.appCtx = appCtx
} }
async get<T>(serviceName: string): Promise<T> { async get<T>(serviceName: string): Promise<T> {
@@ -36,27 +35,27 @@ export class TaskServiceGetter implements IServiceGetter{
} }
async getSubDomainsGetter(): Promise<SubDomainsGetter> { async getSubDomainsGetter(): Promise<SubDomainsGetter> {
const subDomainsService:SubDomainService = await this.ctx.requestContext.getAsync("subDomainService") const subDomainsService:SubDomainService = await this.appCtx.getAsync("subDomainService")
return new SubDomainsGetter(this.userId, subDomainsService) return new SubDomainsGetter(this.userId, subDomainsService)
} }
async getAccessService(): Promise<AccessGetter> { async getAccessService(): Promise<AccessGetter> {
const accessService:AccessService = await this.ctx.requestContext.getAsync("accessService") const accessService:AccessService = await this.appCtx.getAsync("accessService")
return new AccessGetter(this.userId, accessService.getById.bind(accessService)); return new AccessGetter(this.userId, accessService.getById.bind(accessService));
} }
async getCnameProxyService(): Promise<CnameProxyService> { async getCnameProxyService(): Promise<CnameProxyService> {
const cnameRecordService:CnameRecordService = await this.ctx.requestContext.getAsync("cnameRecordService") const cnameRecordService:CnameRecordService = await this.appCtx.getAsync("cnameRecordService")
return new CnameProxyService(this.userId, cnameRecordService.getWithAccessByDomain.bind(cnameRecordService)); return new CnameProxyService(this.userId, cnameRecordService.getWithAccessByDomain.bind(cnameRecordService));
} }
async getNotificationService(): Promise<NotificationGetter> { async getNotificationService(): Promise<NotificationGetter> {
const notificationService:NotificationService = await this.ctx.requestContext.getAsync("notificationService") const notificationService:NotificationService = await this.appCtx.getAsync("notificationService")
return new NotificationGetter(this.userId, notificationService); return new NotificationGetter(this.userId, notificationService);
} }
async getDomainVerifierGetter(): Promise<DomainVerifierGetter> { async getDomainVerifierGetter(): Promise<DomainVerifierGetter> {
const domainService:DomainService = await this.ctx.requestContext.getAsync("domainService") const domainService:DomainService = await this.appCtx.getAsync("domainService")
return new DomainVerifierGetter(this.userId, domainService); return new DomainVerifierGetter(this.userId, domainService);
} }
} }
@@ -67,12 +66,12 @@ export type TaskServiceCreateReq = {
@Provide() @Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true }) @Scope(ScopeEnum.Request, { allowDowngrade: true })
export class TaskServiceBuilder { export class TaskServiceBuilder {
@Inject() @ApplicationContext()
ctx: Context; appCtx: IMidwayContainer;
create(req:TaskServiceCreateReq){ create(req:TaskServiceCreateReq){
const userId = req.userId; const userId = req.userId;
return new TaskServiceGetter(userId,this.ctx) return new TaskServiceGetter(userId,this.appCtx)
} }
} }

View File

@@ -1,6 +1,6 @@
import {Config, Inject, Provide, Scope, ScopeEnum, sleep} from "@midwayjs/core"; import { Config, Inject, Provide, Scope, ScopeEnum, sleep } from "@midwayjs/core";
import {InjectEntityModel} from "@midwayjs/typeorm"; import { InjectEntityModel } from "@midwayjs/typeorm";
import {In, MoreThan, Repository} from "typeorm"; import { In, MoreThan, Repository } from "typeorm";
import { import {
AccessService, AccessService,
BaseService, BaseService,
@@ -11,8 +11,8 @@ import {
SysSettingsService, SysSettingsService,
SysSiteInfo SysSiteInfo
} from "@certd/lib-server"; } from "@certd/lib-server";
import {PipelineEntity} from "../entity/pipeline.js"; import { PipelineEntity } from "../entity/pipeline.js";
import {PipelineDetail} from "../entity/vo/pipeline-detail.js"; import { PipelineDetail } from "../entity/vo/pipeline-detail.js";
import { import {
Executor, Executor,
IAccessService, IAccessService,
@@ -25,33 +25,32 @@ import {
SysInfo, SysInfo,
UserInfo UserInfo
} from "@certd/pipeline"; } from "@certd/pipeline";
import {DbStorage} from "./db-storage.js"; import { DbStorage } from "./db-storage.js";
import {StorageService} from "./storage-service.js"; import { StorageService } from "./storage-service.js";
import {Cron} from "../../cron/cron.js"; import { Cron } from "../../cron/cron.js";
import {HistoryService} from "./history-service.js"; import { HistoryService } from "./history-service.js";
import {HistoryEntity} from "../entity/history.js"; import { HistoryEntity } from "../entity/history.js";
import {HistoryLogEntity} from "../entity/history-log.js"; import { HistoryLogEntity } from "../entity/history-log.js";
import {HistoryLogService} from "./history-log-service.js"; import { HistoryLogService } from "./history-log-service.js";
import {EmailService} from "../../basic/service/email-service.js"; import { EmailService } from "../../basic/service/email-service.js";
import {UserService} from "../../sys/authority/service/user-service.js"; import { UserService } from "../../sys/authority/service/user-service.js";
import {CnameRecordService} from "../../cname/service/cname-record-service.js"; import { CnameRecordService } from "../../cname/service/cname-record-service.js";
import {PluginConfigGetter} from "../../plugin/service/plugin-config-getter.js"; import { PluginConfigGetter } from "../../plugin/service/plugin-config-getter.js";
import dayjs from "dayjs"; import dayjs from "dayjs";
import {DbAdapter} from "../../db/index.js"; import { DbAdapter } from "../../db/index.js";
import {isComm, isPlus} from "@certd/plus-core"; import { isComm, isPlus } from "@certd/plus-core";
import {logger} from "@certd/basic"; import { logger } from "@certd/basic";
import {UrlService} from "./url-service.js"; import { UrlService } from "./url-service.js";
import {NotificationService} from "./notification-service.js"; import { NotificationService } from "./notification-service.js";
import {UserSuiteEntity, UserSuiteService} from "@certd/commercial-core"; import { UserSuiteEntity, UserSuiteService } from "@certd/commercial-core";
import {CertInfoService} from "../../monitor/service/cert-info-service.js"; import { CertInfoService } from "../../monitor/service/cert-info-service.js";
import {TaskServiceBuilder} from "./getter/task-service-getter.js"; import { TaskServiceBuilder } from "./getter/task-service-getter.js";
import {nanoid} from "nanoid"; import { nanoid } from "nanoid";
import {set} from "lodash-es"; import { set } from "lodash-es";
const runningTasks: Map<string | number, Executor> = new Map(); const runningTasks: Map<string | number, Executor> = new Map();
/** /**
* 证书申请 * 证书申请
*/ */
@@ -91,7 +90,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
@Inject() @Inject()
cron: Cron; cron: Cron;
@Config('certd') @Config("certd")
private certdConfig: any; private certdConfig: any;
@Inject() @Inject()
@@ -112,17 +111,35 @@ export class PipelineService extends BaseService<PipelineEntity> {
} }
async add(bean: PipelineEntity) { async add(bean: PipelineEntity) {
bean.status = ResultType.none bean.status = ResultType.none;
await this.save(bean); await this.save(bean);
return bean; return bean;
} }
async page(pageReq: PageReq<PipelineEntity>) { async page(pageReq: PageReq<PipelineEntity>) {
//模版流水线不要被查询出来 //模版流水线不要被查询出来
set(pageReq,"query.isTemplate",false) set(pageReq, "query.isTemplate", false);
const result = await super.page(pageReq); const result = await super.page(pageReq);
await this.fillLastVars(result.records); await this.fillLastVars(result.records);
for (const item of result.records) {
if (!item.content) {
continue;
}
const pipeline = JSON.parse(item.content);
let stepCount = 0;
if(pipeline.stages){
RunnableCollection.each(pipeline.stages, (runnable: any) => {
stepCount++;
});
}
// @ts-ignore
item.stepCount = stepCount;
// @ts-ignore
item.triggerCount = pipeline.triggers?.length;
delete item.content;
}
return result; return result;
} }
@@ -132,7 +149,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
for (const record of records) { for (const record of records) {
pipelineIds.push(record.id); pipelineIds.push(record.id);
recordMap[record.id] = record; recordMap[record.id] = record;
record.title = record.title + ''; record.title = record.title + "";
} }
if (pipelineIds?.length > 0) { if (pipelineIds?.length > 0) {
const vars = await this.storageService.findPipelineVars(pipelineIds); const vars = await this.storageService.findPipelineVars(pipelineIds);
@@ -153,17 +170,17 @@ export class PipelineService extends BaseService<PipelineEntity> {
const info = await this.info(pipelineId); const info = await this.info(pipelineId);
if (info && !info.disabled) { if (info && !info.disabled) {
const pipeline = JSON.parse(info.content); const pipeline = JSON.parse(info.content);
this.registerTriggers(pipeline,false); this.registerTriggers(pipeline, false);
} }
} }
public async registerTrigger(info:PipelineEntity) { public async registerTrigger(info: PipelineEntity) {
if (info == null) { if (info == null) {
return; return;
} }
if (info && !info.disabled) { if (info && !info.disabled) {
const pipeline = JSON.parse(info.content); const pipeline = JSON.parse(info.content);
this.registerTriggers(pipeline,false); this.registerTriggers(pipeline, false);
} }
} }
@@ -190,12 +207,12 @@ export class PipelineService extends BaseService<PipelineEntity> {
const isUpdate = bean.id > 0 && old != null; const isUpdate = bean.id > 0 && old != null;
const pipeline = JSON.parse(bean.content || '{}'); const pipeline = JSON.parse(bean.content || "{}");
RunnableCollection.initPipelineRunnableType(pipeline); RunnableCollection.initPipelineRunnableType(pipeline);
let domains = []; let domains = [];
if (pipeline.stages) { if (pipeline.stages) {
RunnableCollection.each(pipeline.stages, (runnable: any) => { RunnableCollection.each(pipeline.stages, (runnable: any) => {
if (runnable.runnableType === 'step' && runnable.type.indexOf('CertApply')>=0) { if (runnable.runnableType === "step" && runnable.type.indexOf("CertApply") >= 0) {
domains = runnable.input.domains || []; domains = runnable.input.domains || [];
} }
}); });
@@ -206,7 +223,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
await this.checkMaxPipelineCount(bean, pipeline, domains); await this.checkMaxPipelineCount(bean, pipeline, domains);
} }
if (!bean.status ){ if (!bean.status) {
bean.status = ResultType.none; bean.status = ResultType.none;
} }
if (!isUpdate) { if (!isUpdate) {
@@ -217,9 +234,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
await this.doUpdatePipelineJson(bean, pipeline); await this.doUpdatePipelineJson(bean, pipeline);
//保存域名信息到certInfo表 //保存域名信息到certInfo表
let fromType = 'pipeline'; let fromType = "pipeline";
if (bean.type === 'cert_upload') { if (bean.type === "cert_upload") {
fromType = 'upload'; fromType = "upload";
}else if (bean.type === "cert_auto") {
fromType = "auto";
} }
await this.certInfoService.updateDomains(pipeline.id, pipeline.userId || bean.userId, domains, fromType); await this.certInfoService.updateDomains(pipeline.id, pipeline.userId || bean.userId, domains, fromType);
return bean; return bean;
@@ -230,7 +249,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
* @param bean * @param bean
* @param pipeline * @param pipeline
*/ */
async doUpdatePipelineJson(bean: PipelineEntity, pipeline:Pipeline) { async doUpdatePipelineJson(bean: PipelineEntity, pipeline: Pipeline) {
await this.clearTriggers(bean); await this.clearTriggers(bean);
if (pipeline.title) { if (pipeline.title) {
bean.title = pipeline.title; bean.title = pipeline.title;
@@ -261,7 +280,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
throw new NeedSuiteException(`对不起,您最多只能添加${userSuite.domainCount.max}个域名,请购买或升级套餐`); throw new NeedSuiteException(`对不起,您最多只能添加${userSuite.domainCount.max}个域名,请购买或升级套餐`);
} }
} }
}else{ } else {
//非商业版校验用户最大流水线数量 //非商业版校验用户最大流水线数量
const userId = bean.userId; const userId = bean.userId;
const userIsAdmin = await this.userService.isAdmin(userId); const userIsAdmin = await this.userService.isAdmin(userId);
@@ -280,12 +299,12 @@ export class PipelineService extends BaseService<PipelineEntity> {
async foreachPipeline(callback: (pipeline: PipelineEntity) => void) { async foreachPipeline(callback: (pipeline: PipelineEntity) => void) {
const idEntityList = await this.repository.find({ const idEntityList = await this.repository.find({
select: { select: {
id: true, id: true
}, },
where: { where: {
disabled: false, disabled: false,
templateId: 0, templateId: 0
}, }
}); });
const ids = idEntityList.map(item => { const ids = idEntityList.map(item => {
return item.id; return item.id;
@@ -305,7 +324,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
//分段加载记录 //分段加载记录
for (const idArr of idsSpan) { for (const idArr of idsSpan) {
const list = await this.repository.findBy({ const list = await this.repository.findBy({
id: In(idArr), id: In(idArr)
}); });
for (const entity of list) { for (const entity of list) {
@@ -330,14 +349,14 @@ export class PipelineService extends BaseService<PipelineEntity> {
if (onlyAdminUser && entity.userId !== 1) { if (onlyAdminUser && entity.userId !== 1) {
return; return;
} }
const pipeline = JSON.parse(entity.content ?? '{}'); const pipeline = JSON.parse(entity.content ?? "{}");
try { try {
await this.registerTriggers(pipeline, immediateTriggerOnce); await this.registerTriggers(pipeline, immediateTriggerOnce);
} catch (e) { } catch (e) {
logger.error('加载定时trigger失败', e); logger.error("加载定时trigger失败", e);
} }
}); });
logger.info('定时器数量:', this.cron.getTaskSize()); logger.info("定时器数量:", this.cron.getTaskSize());
} }
async registerTriggers(pipeline?: Pipeline, immediateTriggerOnce = false) { async registerTriggers(pipeline?: Pipeline, immediateTriggerOnce = false) {
@@ -359,18 +378,18 @@ export class PipelineService extends BaseService<PipelineEntity> {
if (isComm()) { if (isComm()) {
await this.checkHasDeployCount(id, entity.userId); await this.checkHasDeployCount(id, entity.userId);
} }
await this.checkUserStatus(entity.userId) await this.checkUserStatus(entity.userId);
this.cron.register({ this.cron.register({
name: `pipeline.${id}.trigger.once`, name: `pipeline.${id}.trigger.once`,
cron: null, cron: null,
job: async () => { job: async () => {
logger.info('用户手动启动job'); logger.info("用户手动启动job");
try { try {
await this.doRun(entity, null, stepId); await this.doRun(entity, null, stepId);
} catch (e) { } catch (e) {
logger.error('手动job执行失败', e); logger.error("手动job执行失败", e);
} }
}, }
}); });
} }
@@ -382,7 +401,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
logger.error(e.message); logger.error(e.message);
await this.update({ await this.update({
id: pipelineId, id: pipelineId,
status: 'no_deploy_count', status: "no_deploy_count"
}); });
} }
throw e; throw e;
@@ -390,7 +409,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
} }
//@ts-ignore //@ts-ignore
async delete(id:any) { async delete(id: any) {
await this.clearTriggers(id); await this.clearTriggers(id);
//TODO 删除storage //TODO 删除storage
// const storage = new DbStorage(pipeline.userId, this.storageService); // const storage = new DbStorage(pipeline.userId, this.storageService);
@@ -405,11 +424,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
if (id == null) { if (id == null) {
return; return;
} }
let pipeline:PipelineEntity = null let pipeline: PipelineEntity = null;
if (typeof id === 'number') { if (typeof id === "number") {
pipeline = await this.info(id); pipeline = await this.info(id);
}else{ } else {
pipeline = id pipeline = id;
} }
if (!pipeline) { if (!pipeline) {
return; return;
@@ -429,7 +448,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
registerCron(pipelineId, trigger) { registerCron(pipelineId, trigger) {
if (pipelineId == null) { if (pipelineId == null) {
logger.warn('pipelineId为空无法注册定时任务'); logger.warn("pipelineId为空无法注册定时任务");
return; return;
} }
@@ -438,11 +457,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
return; return;
} }
cron = cron.trim(); cron = cron.trim();
if (cron.startsWith('* *')) { if (cron.startsWith("* *")) {
cron = cron.replace('* *', '0 0'); cron = cron.replace("* *", "0 0");
} }
if (cron.startsWith('*')) { if (cron.startsWith("*")) {
cron = cron.replace('*', '0'); cron = cron.replace("*", "0");
} }
const triggerId = trigger.id; const triggerId = trigger.id;
const name = this.buildCronKey(pipelineId, triggerId); const name = this.buildCronKey(pipelineId, triggerId);
@@ -451,19 +470,19 @@ export class PipelineService extends BaseService<PipelineEntity> {
name, name,
cron, cron,
job: async () => { job: async () => {
logger.info('定时任务触发:', pipelineId, triggerId); logger.info("定时任务触发:", pipelineId, triggerId);
if (pipelineId == null) { if (pipelineId == null) {
logger.warn('pipelineId为空,无法执行'); logger.warn("pipelineId为空,无法执行");
return; return;
} }
try { try {
await this.run(pipelineId, triggerId); await this.run(pipelineId, triggerId);
} catch (e) { } catch (e) {
logger.error('定时job执行失败', e); logger.error("定时job执行失败", e);
} }
}, }
}); });
logger.info('当前定时器数量:', this.cron.getTaskSize()); logger.info("当前定时器数量:", this.cron.getTaskSize());
} }
/** /**
@@ -483,11 +502,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
if (isComm()) { if (isComm()) {
suite = await this.checkHasDeployCount(id, entity.userId); suite = await this.checkHasDeployCount(id, entity.userId);
} }
try{ try {
await this.checkUserStatus(entity.userId) await this.checkUserStatus(entity.userId);
}catch (e) { } catch (e) {
logger.info(e.message) logger.info(e.message);
return return;
} }
@@ -505,7 +524,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
return; return;
} }
if (triggerType === 'timer') { if (triggerType === "timer") {
if (entity.disabled) { if (entity.disabled) {
return; return;
} }
@@ -514,25 +533,25 @@ export class PipelineService extends BaseService<PipelineEntity> {
const onChanged = async (history: RunHistory) => { const onChanged = async (history: RunHistory) => {
//保存执行历史 //保存执行历史
try { try {
logger.info('保存执行历史:', history.id); logger.info("保存执行历史:", history.id);
await this.saveHistory(history); await this.saveHistory(history);
} catch (e) { } catch (e) {
const pipelineEntity = new PipelineEntity(); const pipelineEntity = new PipelineEntity();
pipelineEntity.id = id; pipelineEntity.id = id;
pipelineEntity.status = 'error'; pipelineEntity.status = "error";
pipelineEntity.lastHistoryTime = history.pipeline.status.startTime; pipelineEntity.lastHistoryTime = history.pipeline.status.startTime;
await this.update(pipelineEntity); await this.update(pipelineEntity);
logger.error('保存执行历史失败:', e); logger.error("保存执行历史失败:", e);
throw e; throw e;
} }
}; };
const userId = entity.userId; const userId = entity.userId;
const historyId = await this.historyService.start(entity,triggerType); const historyId = await this.historyService.start(entity, triggerType);
const userIsAdmin = await this.userService.isAdmin(userId); const userIsAdmin = await this.userService.isAdmin(userId);
const user: UserInfo = { const user: UserInfo = {
id: userId, id: userId,
role: userIsAdmin ? 'admin' : 'user', role: userIsAdmin ? "admin" : "user"
}; };
@@ -543,11 +562,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
} }
const taskServiceGetter = this.taskServiceBuilder.create({ const taskServiceGetter = this.taskServiceBuilder.create({
userId, userId
}) });
const accessGetter = await taskServiceGetter.get<IAccessService>("accessService") const accessGetter = await taskServiceGetter.get<IAccessService>("accessService");
const notificationGetter =await taskServiceGetter.get<INotificationService>("notificationService") const notificationGetter = await taskServiceGetter.get<INotificationService>("notificationService");
const cnameProxyService =await taskServiceGetter.get<ICnameProxyService>("cnameProxyService") const cnameProxyService = await taskServiceGetter.get<ICnameProxyService>("cnameProxyService");
const executor = new Executor({ const executor = new Executor({
user, user,
pipeline, pipeline,
@@ -561,7 +580,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
notificationService: notificationGetter, notificationService: notificationGetter,
fileRootDir: this.certdConfig.fileRootDir, fileRootDir: this.certdConfig.fileRootDir,
sysInfo, sysInfo,
serviceGetter:taskServiceGetter serviceGetter: taskServiceGetter
}); });
try { try {
runningTasks.set(historyId, executor); runningTasks.set(historyId, executor);
@@ -579,7 +598,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
} }
} }
} catch (e) { } catch (e) {
logger.error('执行失败:', e); logger.error("执行失败:", e);
// throw e; // throw e;
} finally { } finally {
runningTasks.delete(historyId); runningTasks.delete(historyId);
@@ -603,7 +622,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
} }
private getTriggerType(triggerId, pipeline) { private getTriggerType(triggerId, pipeline) {
let triggerType = 'user'; let triggerType = "user";
if (triggerId != null) { if (triggerId != null) {
//如果不是手动触发 //如果不是手动触发
//查找trigger //查找trigger
@@ -613,8 +632,8 @@ export class PipelineService extends BaseService<PipelineEntity> {
this.cron.remove(this.buildCronKey(pipeline.id, triggerId)); this.cron.remove(this.buildCronKey(pipeline.id, triggerId));
triggerType = null; triggerType = null;
} else { } else {
logger.info('timer trigger:' + found.id, found.title, found.cron); logger.info("timer trigger:" + found.id, found.title, found.cron);
triggerType = 'timer'; triggerType = "timer";
} }
} }
return triggerType; return triggerType;
@@ -637,7 +656,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
//修改pipeline状态 //修改pipeline状态
const pipelineEntity = new PipelineEntity(); const pipelineEntity = new PipelineEntity();
pipelineEntity.id = parseInt(history.pipeline.id); pipelineEntity.id = parseInt(history.pipeline.id);
pipelineEntity.status = history.pipeline.status.status + ''; pipelineEntity.status = history.pipeline.status.result + "";
pipelineEntity.lastHistoryTime = history.pipeline.status.startTime; pipelineEntity.lastHistoryTime = history.pipeline.status.startTime;
await this.update(pipelineEntity); await this.update(pipelineEntity);
@@ -661,8 +680,8 @@ export class PipelineService extends BaseService<PipelineEntity> {
async count(param: { userId?: any }) { async count(param: { userId?: any }) {
const count = await this.repository.count({ const count = await this.repository.count({
where: { where: {
userId: param.userId, userId: param.userId
}, }
}); });
return count; return count;
} }
@@ -670,12 +689,12 @@ export class PipelineService extends BaseService<PipelineEntity> {
async statusCount(param: { userId?: any } = {}) { async statusCount(param: { userId?: any } = {}) {
const statusCount = await this.repository const statusCount = await this.repository
.createQueryBuilder() .createQueryBuilder()
.select('status') .select("status")
.addSelect('count(1)', 'count') .addSelect("count(1)", "count")
.where({ .where({
userId: param.userId, userId: param.userId
}) })
.groupBy('status') .groupBy("status")
.getRawMany(); .getRawMany();
return statusCount; return statusCount;
} }
@@ -685,11 +704,11 @@ export class PipelineService extends BaseService<PipelineEntity> {
select: { select: {
id: true, id: true,
title: true, title: true,
status: true, status: true
}, },
where: { where: {
userId, userId
}, }
}); });
await this.fillLastVars(list); await this.fillLastVars(list);
list = list.filter(item => { list = list.filter(item => {
@@ -703,16 +722,16 @@ export class PipelineService extends BaseService<PipelineEntity> {
} }
async createCountPerDay(param: { days: number } = { days: 7 }) { async createCountPerDay(param: { days: number } = { days: 7 }) {
const todayEnd = dayjs().endOf('day'); const todayEnd = dayjs().endOf("day");
const result = await this.getRepository() const result = await this.getRepository()
.createQueryBuilder('main') .createQueryBuilder("main")
.select(`${this.dbAdapter.date('main.createTime')} AS date`) // 将UNIX时间戳转换为日期 .select(`${this.dbAdapter.date("main.createTime")} AS date`) // 将UNIX时间戳转换为日期
.addSelect('COUNT(1) AS count') .addSelect("COUNT(1) AS count")
.where({ .where({
// 0点 // 0点
createTime: MoreThan(todayEnd.add(-param.days, 'day').toDate()), createTime: MoreThan(todayEnd.add(-param.days, "day").toDate())
}) })
.groupBy('date') .groupBy("date")
.getRawMany(); .getRawMany();
return result; return result;
@@ -729,48 +748,48 @@ export class PipelineService extends BaseService<PipelineEntity> {
await this.repository.update( await this.repository.update(
{ {
id: In(ids), id: In(ids),
userId, userId
}, },
{ groupId } { groupId }
); );
} }
async batchUpdateTrigger(ids: number[], trigger: any, userId: any){ async batchUpdateTrigger(ids: number[], trigger: any, userId: any) {
const list = await this.find({ const list = await this.find({
where:{ where: {
id: In(ids), id: In(ids),
userId userId
} }
}) });
for (const item of list) { for (const item of list) {
const pipeline = JSON.parse(item.content); const pipeline = JSON.parse(item.content);
pipeline.triggers = [{ pipeline.triggers = [{
id: nanoid(), id: nanoid(),
title: '定时触发', title: "定时触发",
...trigger ...trigger
}] }];
await this.doUpdatePipelineJson(item,pipeline) await this.doUpdatePipelineJson(item, pipeline);
} }
} }
async batchUpdateNotifications(ids: number[], notification: Notification, userId: any){ async batchUpdateNotifications(ids: number[], notification: Notification, userId: any) {
const list = await this.find({ const list = await this.find({
where:{ where: {
id: In(ids), id: In(ids),
userId userId
} }
}) });
for (const item of list) { for (const item of list) {
const pipeline = JSON.parse(item.content); const pipeline = JSON.parse(item.content);
pipeline.notifications = [{ pipeline.notifications = [{
id: nanoid(), id: nanoid(),
title: '通知', title: "通知",
/** /**
* type: NotificationType; * type: NotificationType;
* when: NotificationWhen[]; * when: NotificationWhen[];
@@ -781,44 +800,44 @@ export class PipelineService extends BaseService<PipelineEntity> {
*/ */
type: "other", type: "other",
...notification ...notification
}] }];
await this.doUpdatePipelineJson(item,pipeline) await this.doUpdatePipelineJson(item, pipeline);
} }
} }
async batchRerun(ids: number[], userId: any) { async batchRerun(ids: number[], userId: any) {
if (!isPlus()){ if (!isPlus()) {
throw new NeedVIPException("此功能需要升级专业版") throw new NeedVIPException("此功能需要升级专业版");
} }
if (!userId || ids.length === 0) { if (!userId || ids.length === 0) {
return; return;
} }
const list = await this.repository.find({ const list = await this.repository.find({
select:{ select: {
id:true id: true
}, },
where:{ where: {
id: In(ids), id: In(ids),
userId userId
} }
}) });
ids = list.map(item=>item.id) ids = list.map(item => item.id);
//异步执行 //异步执行
this.startBatchRerun(ids) this.startBatchRerun(ids);
} }
async startBatchRerun(ids: number[]){ async startBatchRerun(ids: number[]) {
//20条一批 //20条一批
const batchSize = 20; const batchSize = 20;
for (let i = 0; i < ids.length; i += batchSize) { for (let i = 0; i < ids.length; i += batchSize) {
const batchIds = ids.slice(i, i + batchSize); const batchIds = ids.slice(i, i + batchSize);
const batchPromises = batchIds.map(async (id)=>{ const batchPromises = batchIds.map(async (id) => {
await this.run(id,null,"ALL") await this.run(id, null, "ALL");
}); });
await Promise.all(batchPromises) await Promise.all(batchPromises);
} }
} }
@@ -831,35 +850,132 @@ export class PipelineService extends BaseService<PipelineEntity> {
return await this.repository.find({ return await this.repository.find({
select: { select: {
id: true, id: true,
title: true, title: true
}, },
where: { where: {
id: In(pipelineIds), id: In(pipelineIds),
userId, userId
}, }
}); });
} }
private async checkUserStatus(userId: number) { private async checkUserStatus(userId: number) {
const userEntity = await this.userService.info(userId); const userEntity = await this.userService.info(userId);
if(userEntity == null){ if (userEntity == null) {
throw new Error('用户不存在'); throw new Error("用户不存在");
} }
if(userEntity.status === 0){ if (userEntity.status === 0) {
const message = `账户${userId}已被禁用,禁止运行流水线` const message = `账户${userId}已被禁用,禁止运行流水线`;
throw new Error(message) throw new Error(message);
} }
const sysPublic = await this.sysSettingsService.getPublicSettings() const sysPublic = await this.sysSettingsService.getPublicSettings();
if(isPlus() && sysPublic.userValidTimeEnabled === true){ if (isPlus() && sysPublic.userValidTimeEnabled === true) {
//校验用户有效期是否设置 //校验用户有效期是否设置
if(userEntity.validTime!= null && userEntity.validTime > 0){ if (userEntity.validTime != null && userEntity.validTime > 0) {
if(userEntity.validTime < new Date().getTime()){ if (userEntity.validTime < new Date().getTime()) {
//用户已过期 //用户已过期
const message = `账户${userId}已过有效期,禁止运行流水线` const message = `账户${userId}已过有效期,禁止运行流水线`;
throw new Error(message) throw new Error(message);
} }
} }
} }
} }
async createAutoPipeline(req: { domains: string[]; email: string; userId: number ,from:string}) {
const randomHour = Math.floor(Math.random() * 6);
const randomMin = Math.floor(Math.random() * 60);
const randomCron = `0 ${randomMin} ${randomHour} * * *`;
let pipeline: any = {
title: req.domains[0] + `证书自动申请【${req.from??"OpenAPI"}`,
runnableType: "pipeline",
triggers: [
{
id: nanoid(),
title: "定时触发",
props:{
cron: randomCron,
},
type: "timer"
}
],
notifications: [
{
id: nanoid(),
type: "custom",
when: ["error", "turnToSuccess", "success"],
notificationId: 0,
title: "默认通知",
}
],
stages: [
{
id: nanoid(),
title: "证书申请阶段",
maxTaskCount: 1,
runnableType: "stage",
tasks: [
{
id: nanoid(),
title: "证书申请任务",
runnableType: "task",
steps: [
{
id: nanoid(),
title: "申请证书",
runnableType: "step",
input: {
renewDays: 35,
domains: req.domains,
email: req.email,
"challengeType": "auto",
"sslProvider": "letsencrypt",
"privateKeyType": "rsa_2048",
"certProfile": "classic",
"useProxy": false,
"skipLocalVerify": false,
"maxCheckRetryCount": 20,
"waitDnsDiffuseTime": 30,
"pfxArgs": "-macalg SHA1 -keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES",
"successNotify": true
},
strategy: {
runStrategy: 0 // 正常执行
},
type: "CertApply"
}
]
}
]
}
]
};
const bean = new PipelineEntity();
bean.title = pipeline.title;
bean.content = JSON.stringify(pipeline);
bean.userId = req.userId;
bean.status = "none";
bean.type = "cert_auto";
bean.disabled = false
bean.keepHistoryCount = 30
await this.save(bean)
return bean;
}
async getStatus(pipelineId: number) {
const res = await this.repository.findOne({
select: {
status: true
},
where: {
id: pipelineId
}
});
return res?.status;
}
} }

View File

@@ -1,3 +1,4 @@
export * from './host-shell-execute/index.js'; export * from './host-shell-execute/index.js';
export * from './upload-to-host/index.js'; export * from './upload-to-host/index.js';
export * from './copy-to-local/index.js' export * from './copy-to-local/index.js'
export * from './plugin-upload-to-oss.js'

View File

@@ -0,0 +1,274 @@
import {AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput} from '@certd/pipeline';
import {CertInfo} from "@certd/plugin-cert";
import {ossClientFactory} from "@certd/plugin-lib";
import {utils} from "@certd/basic";
@IsTaskPlugin({
name: 'UploadCertToOss',
title: '上传证书到对象存储OSS',
icon: 'ion:cloud-upload-outline',
desc: '支持阿里云OSS、腾讯云COS、七牛云KODO、S3、MinIO、FTP、SFTP',
group: pluginGroups.host.key,
showRunStrategy:false,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class UploadCertToOssPlugin extends AbstractTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: [":cert:"],
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: 'OSS类型',
component: {
name: 'a-select',
vModel:"value",
options: [
{ label: "阿里云OSS", value: "alioss" },
{ label: "腾讯云COS", value: "tencentcos" },
{ label: "七牛OSS", value: "qiniuoss" },
{ label: "S3/Minio", value: "s3" },
{ label: "SFTP", value: "sftp" },
{ label: "FTP", value: "ftp" },
]
},
required: true,
})
uploaderType!: string;
@TaskInput({
title: 'OSS授权',
component: {
name: 'access-selector',
},
required: true,
mergeScript: `
return {
component: {
type: ctx.compute(({form})=>{
return form.uploaderType;
})
}
}
`,
})
accessId!: string;
@TaskInput({
title: '证书格式',
helper: '要部署的证书格式支持pem、pfx、der、jks',
component: {
name: 'a-select',
options: [
{ value: 'pem', label: 'pemcrtNginx等大部分应用' },
{ value: 'pfx', label: 'pfx一般用于IIS' },
{ value: 'der', label: 'der一般用于Apache' },
{ value: 'jks', label: 'jks一般用于JAVA应用' },
{ value: 'one', label: '证书私钥一体crt+key简单合并为一个pem文件' },
],
},
required: true,
})
certType!: string;
@TaskInput({
title: '证书保存路径',
helper: '路径要包含证书文件名,例如:/tmp/cert.pem',
component: {
placeholder: '/root/deploy/nginx/full_chain.pem',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'pem';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
crtPath!: string;
@TaskInput({
title: '私钥保存路径',
helper: '路径要包含私钥文件名,例如:/tmp/cert.key',
component: {
placeholder: '/root/deploy/nginx/cert.key',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'pem';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
keyPath!: string;
@TaskInput({
title: '中间证书保存路径',
helper: '路径要包含文件名,一般情况传上面两个文件即可,极少数情况需要这个中间证书',
component: {
placeholder: '/root/deploy/nginx/intermediate.pem',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'pem';
})
}
`,
rules: [{ type: 'filepath' }],
})
icPath!: string;
@TaskInput({
title: 'PFX证书保存路径',
helper: '路径要包含证书文件名例如D:\\iis\\cert.pfx',
component: {
placeholder: 'D:\\iis\\cert.pfx',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'pfx';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
pfxPath!: string;
@TaskInput({
title: 'DER证书保存路径',
helper: '路径要包含证书文件名,例如:/tmp/cert.der',
component: {
placeholder: '/root/deploy/apache/cert.der',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'der';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
derPath!: string;
@TaskInput({
title: 'jks证书保存路径',
helper: '路径要包含证书文件名,例如:/tmp/cert.jks',
component: {
placeholder: '/root/deploy/java_app/cert.jks',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'jks';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
jksPath!: string;
@TaskInput({
title: '一体证书保存路径',
helper: '路径要包含证书文件名,例如:/tmp/crt_key.pem',
component: {
placeholder: '/app/crt_key.pem',
},
mergeScript: `
return {
show: ctx.compute(({form})=>{
return form.certType === 'one';
})
}
`,
required: true,
rules: [{ type: 'filepath' }],
})
onePath!: string;
async onInstance() {}
async execute(): Promise<void> {
const { accessId } = this;
let { crtPath, keyPath, icPath, pfxPath, derPath, jksPath, onePath } = this;
if (!accessId) {
throw new Error('OSS授权配置不能为空');
}
const uploaderType = this.uploaderType
const uploaderAccess = this.accessId
const httpUploaderContext = {
accessService: this.ctx.accessService,
logger: this.logger,
utils,
};
const access = await this.getAccess(uploaderAccess);
this.logger.info("上传方式", uploaderType);
const httpUploader = await ossClientFactory.createOssClientByType(uploaderType, {
access,
rootDir: "",
ctx: httpUploaderContext,
});
this.logger.info('准备上传文件到OSS');
if (crtPath) {
await httpUploader.upload(crtPath, Buffer.from(this.cert.crt))
this.logger.info(`上传证书:${crtPath}`);
}
if (keyPath) {
await httpUploader.upload(keyPath, Buffer.from(this.cert.key))
this.logger.info(`上传私钥:${keyPath}`);
}
if (icPath) {
await httpUploader.upload(icPath, Buffer.from(this.cert.ic))
this.logger.info(`上传中间证书:${icPath}`);
}
if (pfxPath) {
await httpUploader.upload(pfxPath, Buffer.from(this.cert.pfx, "base64"))
this.logger.info(`上传PFX证书${pfxPath}`);
}
if (derPath) {
await httpUploader.upload(derPath, Buffer.from(this.cert.der, "base64"))
this.logger.info(`上传DER证书${derPath}`);
}
if (this.jksPath) {
await httpUploader.upload(jksPath,Buffer.from(this.cert.jks, "base64"))
this.logger.info(`上传jks证书${jksPath}`);
}
if (onePath) {
await httpUploader.upload(onePath, Buffer.from(this.cert.one))
this.logger.info(`上传一体证书:${onePath}`);
}
this.logger.info('上传文件成功');
}
}
new UploadCertToOssPlugin();

View File

@@ -2,3 +2,4 @@ export * from './plugin-restart.js';
export * from './plugin-script.js'; export * from './plugin-script.js';
export * from './plugin-wait.js'; export * from './plugin-wait.js';
export * from './plugin-db-backup.js'; export * from './plugin-db-backup.js';
export * from './plugin-deploy-to-mail.js';

View File

@@ -0,0 +1,186 @@
import {AbstractTaskPlugin, FileItem, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput} from '@certd/pipeline';
import {CertInfo, CertReader} from "@certd/plugin-cert";
import dayjs from "dayjs";
@IsTaskPlugin({
name: 'DeployCertToMailPlugin',
title: '邮件发送证书',
icon: 'ion:mail-outline',
desc: '通过邮件发送证书',
group: pluginGroups.other.key,
showRunStrategy:false,
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class DeployCertToMailPlugin extends AbstractTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: [":cert:"],
},
required: true,
})
cert!: CertInfo;
@TaskInput({
title: '证书压缩文件',
helper: '请选择前置任务输出的域名证书压缩文件',
component: {
name: 'output-selector',
from: [":certZip:"],
},
required: true,
})
certZip!: FileItem;
@TaskInput({
title: '接收邮箱',
component: {
name: 'EmailSelector',
vModel: 'value',
mode:"tags",
},
required: true,
})
email!: string[];
/**
* title:
* title: 邮件标题
* helper: |-
* 请输入邮件标题否则将使用默认标题
* 域名:${certDomains}
* component:
* name: a-input
* required: false
* template:
* title: 邮件模版
* helper: |-
* 请输入模版内容否则将使用默认模版
* 域名:${certDomains}
* value: |-
* 尊敬的用户你好:
* 以下是域名(${certDomains})证书文件
* component:
* name: a-textarea
* autosize:
* minRows: 6
* maxRows: 10
* required: false
*/
@TaskInput({
title: '邮件标题',
component: {
name: 'a-input',
vModel: 'value',
placeholder:`证书申请成功【$\{mainDomain}】`,
},
helper: '请输入邮件标题否则将使用默认标题\n模板变量主域名=$\{mainDomain}、全部域名=$\{domains}、过期时间=$\{expiresTime}、备注=$\{remark}、证书PEM=$\{crt}、证书私钥=$\{key}、中间证书/CA证书=$\{ic}',
required: false,
})
title!: string;
@TaskInput({
title: '邮件模版',
component: {
name: 'a-textarea',
vModel: 'value',
autosize: {
minRows: 6,
maxRows: 10,
},
placeholder: `
<div>
<p>证书申请成功</p>
<p>域名:$\{domains}</p>
<p>证书有效期:$\{expiresTime}</p>
<p>备注:$\{remark}</p>
</div>
`,
},
helper: `请输入模版内容否则将使用默认模版,模板变量同上`,
required: false,
})
template!: string;
@TaskInput({
title: '备注',
component: {
name: 'a-input',
vModel: 'value',
},
required: false,
})
remark!: string;
async onInstance() {}
async execute(): Promise<void> {
this.logger.info(`开始发送邮件`);
const certReader = new CertReader(this.cert)
const mainDomain = certReader.getMainDomain();
const domains = certReader.getAllDomains().join(',');
const data = {
mainDomain,
domains,
expiresTime: dayjs(certReader.expires).format("YYYY-MM-DD HH:mm:ss"),
remark:this.remark ||"",
crt: this.cert.crt,
key: this.cert.key,
ic: this.cert.ic
}
let title = `证书申请成功【${mainDomain}`;
let html = `
<div>
<p>证书申请成功</p>
<p>域名:${domains}</p>
<p>证书有效期:${data.expiresTime}</p>
<p>备注:${this.remark||""}</p>
</div>
`;
if (this.title) {
const compile = this.compile(this.title);
title = compile(data);
}
if (this.template) {
const compile = this.compile(this.template);
html = compile(data);
}
const file = this.certZip
if (!file) {
throw new Error('证书压缩文件还未生成,重新运行证书任务');
}
await this.ctx.emailService.send({
subject:title,
html: html,
receivers: this.email,
attachments: [
{
filename: file.filename,
path: file.path,
},
],
})
}
compile(templateString:string) {
return new Function('data', ` with(data || {}) {
return \`${templateString}\`;
}
`);
}
}
new DeployCertToMailPlugin();

View File

@@ -5,7 +5,7 @@ import { TencentAccess } from "@certd/plugin-lib";
name: 'DeployCertToTencentEO', name: 'DeployCertToTencentEO',
title: '腾讯云-部署到腾讯云EO', title: '腾讯云-部署到腾讯云EO',
icon: 'svg:icon-tencentcloud', icon: 'svg:icon-tencentcloud',
desc: '腾讯云边缘安全加速平台EO,必须配置上传证书到腾讯云任务', desc: '腾讯云边缘安全加速平台EdgeOne(EO),必须配置上传证书到腾讯云任务',
group: pluginGroups.tencent.key, group: pluginGroups.tencent.key,
default: { default: {
strategy: { strategy: {

185
pnpm-lock.yaml generated
View File

@@ -4,10 +4,16 @@ settings:
autoInstallPeers: true autoInstallPeers: true
excludeLinksFromLockfile: false excludeLinksFromLockfile: false
overrides:
'@certd/ui-server': link:packages/ui/certd-server
importers: importers:
.: .:
dependencies: dependencies:
'@certd/ui-server':
specifier: link:packages/ui/certd-server
version: link:packages/ui/certd-server
axios: axios:
specifier: ^1.7.7 specifier: ^1.7.7
version: 1.9.0(debug@4.4.1) version: 1.9.0(debug@4.4.1)
@@ -46,7 +52,7 @@ importers:
packages/core/acme-client: packages/core/acme-client:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../basic version: link:../basic
'@peculiar/x509': '@peculiar/x509':
specifier: ^1.11.0 specifier: ^1.11.0
@@ -207,11 +213,11 @@ importers:
packages/core/pipeline: packages/core/pipeline:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../basic version: link:../basic
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.36.1 specifier: ^1.36.5
version: 1.36.1 version: link:../../pro/plus-core
dayjs: dayjs:
specifier: ^1.11.7 specifier: ^1.11.7
version: 1.11.13 version: 1.11.13
@@ -415,7 +421,7 @@ importers:
packages/libs/lib-k8s: packages/libs/lib-k8s:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/basic version: link:../../core/basic
'@kubernetes/client-node': '@kubernetes/client-node':
specifier: 0.21.0 specifier: 0.21.0
@@ -455,17 +461,17 @@ importers:
packages/libs/lib-server: packages/libs/lib-server:
dependencies: dependencies:
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.36.1 specifier: ^1.36.5
version: 1.36.1 version: link:../../pro/plus-core
'@midwayjs/cache': '@midwayjs/cache':
specifier: ~3.14.0 specifier: ~3.14.0
version: 3.14.0 version: 3.14.0
@@ -607,16 +613,16 @@ importers:
packages/plugins/plugin-cert: packages/plugins/plugin-cert:
dependencies: dependencies:
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../plugin-lib version: link:../plugin-lib
'@google-cloud/publicca': '@google-cloud/publicca':
specifier: ^1.3.0 specifier: ^1.3.0
@@ -698,10 +704,10 @@ importers:
specifier: ^3.787.0 specifier: ^3.787.0
version: 3.810.0(aws-crt@1.26.2) version: 3.810.0(aws-crt@1.26.2)
'@certd/basic': '@certd/basic':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@kubernetes/client-node': '@kubernetes/client-node':
specifier: 0.21.0 specifier: 0.21.0
@@ -789,19 +795,19 @@ importers:
packages/pro/commercial-core: packages/pro/commercial-core:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.36.1 specifier: ^1.36.5
version: link:../../core/basic version: link:../../core/basic
'@certd/lib-server': '@certd/lib-server':
specifier: ^1.36.1 specifier: ^1.36.5
version: link:../../libs/lib-server version: link:../../libs/lib-server
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.36.1 specifier: ^1.36.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-plus': '@certd/plugin-plus':
specifier: ^1.36.1 specifier: ^1.36.5
version: link:../plugin-plus version: link:../plugin-plus
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.36.1 specifier: ^1.36.5
version: link:../plus-core version: link:../plus-core
'@midwayjs/core': '@midwayjs/core':
specifier: ~3.20.3 specifier: ~3.20.3
@@ -886,22 +892,22 @@ importers:
specifier: ^1.0.2 specifier: ^1.0.2
version: 1.0.3 version: 1.0.3
'@certd/basic': '@certd/basic':
specifier: ^1.36.1 specifier: ^1.36.5
version: link:../../core/basic version: link:../../core/basic
'@certd/lib-k8s': '@certd/lib-k8s':
specifier: ^1.36.1 specifier: ^1.36.5
version: link:../../libs/lib-k8s version: link:../../libs/lib-k8s
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.36.1 specifier: ^1.36.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-cert': '@certd/plugin-cert':
specifier: ^1.36.1 specifier: ^1.36.5
version: link:../../plugins/plugin-cert version: link:../../plugins/plugin-cert
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.36.1 specifier: ^1.36.5
version: link:../../plugins/plugin-lib version: link:../../plugins/plugin-lib
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.36.1 specifier: ^1.36.5
version: link:../plus-core version: link:../plus-core
ali-oss: ali-oss:
specifier: ^6.21.0 specifier: ^6.21.0
@@ -1004,7 +1010,7 @@ importers:
packages/pro/plus-core: packages/pro/plus-core:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.36.1 specifier: ^1.36.5
version: link:../../core/basic version: link:../../core/basic
dayjs: dayjs:
specifier: ^1.11.7 specifier: ^1.11.7
@@ -1294,10 +1300,10 @@ importers:
version: 0.1.3(zod@3.24.4) version: 0.1.3(zod@3.24.4)
devDependencies: devDependencies:
'@certd/lib-iframe': '@certd/lib-iframe':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../libs/lib-iframe version: link:../../libs/lib-iframe
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@rollup/plugin-commonjs': '@rollup/plugin-commonjs':
specifier: ^25.0.7 specifier: ^25.0.7
@@ -1480,47 +1486,47 @@ importers:
specifier: ^3.705.0 specifier: ^3.705.0
version: 3.810.0(aws-crt@1.26.2) version: 3.810.0(aws-crt@1.26.2)
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/basic version: link:../../core/basic
'@certd/commercial-core': '@certd/commercial-core':
specifier: ^1.36.1 specifier: ^1.36.5
version: 1.36.1(better-sqlite3@11.10.0)(encoding@0.1.13)(mysql2@3.14.1)(pg@8.16.0)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.100)(typescript@5.8.3)) version: link:../../pro/commercial-core
'@certd/cv4pve-api-javascript': '@certd/cv4pve-api-javascript':
specifier: ^8.4.1 specifier: ^8.4.1
version: 8.4.1 version: 8.4.1
'@certd/jdcloud': '@certd/jdcloud':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../libs/lib-jdcloud version: link:../../libs/lib-jdcloud
'@certd/lib-huawei': '@certd/lib-huawei':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../libs/lib-huawei version: link:../../libs/lib-huawei
'@certd/lib-k8s': '@certd/lib-k8s':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../libs/lib-k8s version: link:../../libs/lib-k8s
'@certd/lib-server': '@certd/lib-server':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../libs/lib-server version: link:../../libs/lib-server
'@certd/midway-flyway-js': '@certd/midway-flyway-js':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../libs/midway-flyway-js version: link:../../libs/midway-flyway-js
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-cert': '@certd/plugin-cert':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../plugins/plugin-cert version: link:../../plugins/plugin-cert
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.36.2 specifier: ^1.36.5
version: link:../../plugins/plugin-lib version: link:../../plugins/plugin-lib
'@certd/plugin-plus': '@certd/plugin-plus':
specifier: ^1.36.1 specifier: ^1.36.5
version: 1.36.1(encoding@0.1.13) version: link:../../pro/plugin-plus
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.36.1 specifier: ^1.36.5
version: 1.36.1 version: link:../../pro/plus-core
'@huaweicloud/huaweicloud-sdk-cdn': '@huaweicloud/huaweicloud-sdk-cdn':
specifier: ^3.1.120 specifier: ^3.1.120
version: 3.1.149 version: 3.1.149
@@ -2760,18 +2766,9 @@ packages:
'@better-scroll/zoom@2.5.1': '@better-scroll/zoom@2.5.1':
resolution: {integrity: sha512-aGvFY5ooeZWS4RcxQLD+pGLpQHQxpPy0sMZV3yadcd2QK53PK9gS4Dp+BYfRv8lZ4/P2LoNEhr6Wq1DN6+uPlA==} resolution: {integrity: sha512-aGvFY5ooeZWS4RcxQLD+pGLpQHQxpPy0sMZV3yadcd2QK53PK9gS4Dp+BYfRv8lZ4/P2LoNEhr6Wq1DN6+uPlA==}
'@certd/commercial-core@1.36.1':
resolution: {integrity: sha512-CpZ7K7s0JoXJ8MD3FyeryKnA62fvX3DKVQ7XkSSomaqgH/a8DgP4QrJGrt2ytIYtu2SzJvJsBfy14UhVM6174Q==}
'@certd/cv4pve-api-javascript@8.4.1': '@certd/cv4pve-api-javascript@8.4.1':
resolution: {integrity: sha512-jxlRieJmCA0Z9LnwX6Ra6ZekRGJEu8o8RGYoKU0Jjkhc9jm6ChEbVyfE7Iw49/hlpA+2yaHdAXb46au/afCISg==} resolution: {integrity: sha512-jxlRieJmCA0Z9LnwX6Ra6ZekRGJEu8o8RGYoKU0Jjkhc9jm6ChEbVyfE7Iw49/hlpA+2yaHdAXb46au/afCISg==}
'@certd/plugin-plus@1.36.1':
resolution: {integrity: sha512-Bd3tFwn/jDrpSWqUH9488YxFHnEIGI15NFoqQaLv58L+Ycm7mRbyTUC/S3xpiUkfhDI6wqvKL8SOtdpgo0IEzA==}
'@certd/plus-core@1.36.1':
resolution: {integrity: sha512-TiRpqWCC7RGJxmVaDkSkMAFYe2zehRqZFKmNcfG8DZJTgKpQhd98HvXlyXCJD5qUtPmyvFoffTD1Q8bGqN84Vw==}
'@colors/colors@1.5.0': '@colors/colors@1.5.0':
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
engines: {node: '>=0.1.90'} engines: {node: '>=0.1.90'}
@@ -15447,84 +15444,12 @@ snapshots:
dependencies: dependencies:
'@better-scroll/core': 2.5.1 '@better-scroll/core': 2.5.1
'@certd/commercial-core@1.36.1(better-sqlite3@11.10.0)(encoding@0.1.13)(mysql2@3.14.1)(pg@8.16.0)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.100)(typescript@5.8.3))':
dependencies:
'@certd/basic': link:packages/core/basic
'@certd/lib-server': link:packages/libs/lib-server
'@certd/pipeline': link:packages/core/pipeline
'@certd/plugin-plus': 1.36.1(encoding@0.1.13)
'@certd/plus-core': 1.36.1
'@midwayjs/core': 3.20.4
'@midwayjs/koa': 3.20.5
'@midwayjs/logger': 3.4.2
'@midwayjs/typeorm': 3.20.4
alipay-sdk: 4.14.0
dayjs: 1.11.13
typeorm: 0.3.24(better-sqlite3@11.10.0)(mysql2@3.14.1)(pg@8.16.0)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.100)(typescript@5.8.3))
wechatpay-node-v3: 2.2.1
transitivePeerDependencies:
- '@google-cloud/spanner'
- '@sap/hana-client'
- babel-plugin-macros
- better-sqlite3
- encoding
- hdb-pool
- ioredis
- mongodb
- mssql
- mysql2
- oracledb
- pg
- pg-native
- pg-query-stream
- proxy-agent
- redis
- reflect-metadata
- sql.js
- sqlite3
- supports-color
- ts-node
- typeorm-aurora-data-api-driver
'@certd/cv4pve-api-javascript@8.4.1': '@certd/cv4pve-api-javascript@8.4.1':
dependencies: dependencies:
debug: 4.4.1(supports-color@8.1.1) debug: 4.4.1(supports-color@8.1.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
'@certd/plugin-plus@1.36.1(encoding@0.1.13)':
dependencies:
'@alicloud/pop-core': 1.8.0
'@baiducloud/sdk': 1.0.3
'@certd/basic': link:packages/core/basic
'@certd/lib-k8s': link:packages/libs/lib-k8s
'@certd/pipeline': link:packages/core/pipeline
'@certd/plugin-cert': link:packages/plugins/plugin-cert
'@certd/plugin-lib': link:packages/plugins/plugin-lib
'@certd/plus-core': 1.36.1
ali-oss: 6.23.0
baidu-aip-sdk: 4.16.16
basic-ftp: 5.0.5
cos-nodejs-sdk-v5: 2.14.7
crypto-js: 4.2.0
dayjs: 1.11.13
form-data: 4.0.2
https-proxy-agent: 7.0.6
js-yaml: 4.1.0
jsencrypt: 3.3.2
jsrsasign: 11.1.0
qiniu: 7.14.0
tencentcloud-sdk-nodejs: 4.1.37(encoding@0.1.13)
transitivePeerDependencies:
- encoding
- proxy-agent
- supports-color
'@certd/plus-core@1.36.1':
dependencies:
'@certd/basic': link:packages/core/basic
dayjs: 1.11.13
'@colors/colors@1.5.0': '@colors/colors@1.5.0':
optional: true optional: true
@@ -21599,13 +21524,13 @@ snapshots:
resolve: 1.22.10 resolve: 1.22.10
semver: 6.3.1 semver: 6.3.1
eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8): eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8):
dependencies: dependencies:
eslint: 7.32.0 eslint: 7.32.0
prettier: 2.8.8 prettier: 2.8.8
prettier-linter-helpers: 1.0.0 prettier-linter-helpers: 1.0.0
optionalDependencies: optionalDependencies:
eslint-config-prettier: 8.10.0(eslint@8.57.0) eslint-config-prettier: 8.10.0(eslint@7.32.0)
eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8): eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8):
dependencies: dependencies:
@@ -24313,7 +24238,7 @@ snapshots:
eslint: 7.32.0 eslint: 7.32.0
eslint-config-prettier: 8.10.0(eslint@7.32.0) eslint-config-prettier: 8.10.0(eslint@7.32.0)
eslint-plugin-node: 11.1.0(eslint@7.32.0) eslint-plugin-node: 11.1.0(eslint@7.32.0)
eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8) eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8)
execa: 5.1.1 execa: 5.1.1
inquirer: 7.3.3 inquirer: 7.3.3
json5: 2.2.3 json5: 2.2.3