mirror of
https://github.com/certd/certd.git
synced 2026-04-14 12:30:54 +08:00
Compare commits
180 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fbf4959463 | ||
|
|
02bb0be06a | ||
|
|
87e440ee2a | ||
|
|
2182dce07c | ||
|
|
3f0a10007c | ||
|
|
67934cdebd | ||
|
|
6765a48706 | ||
|
|
b4252033d5 | ||
|
|
f78ae93eed | ||
|
|
0227155ab4 | ||
|
|
330b84de33 | ||
|
|
f47f86b669 | ||
|
|
95eeb93822 | ||
|
|
367f807313 | ||
|
|
a954629ff9 | ||
|
|
3bbbc41062 | ||
|
|
bf63b0d73f | ||
|
|
5362df55f4 | ||
|
|
59897c4cea | ||
|
|
a9717b9a0d | ||
|
|
680941af11 | ||
|
|
1cf8d4e5e7 | ||
|
|
70ce6be0bf | ||
|
|
9187e87419 | ||
|
|
6ed1e18c7d | ||
|
|
8d27f07213 | ||
|
|
e4f4570b29 | ||
|
|
d86fc9569a | ||
|
|
fa7a983bcb | ||
|
|
9ac908ebee | ||
|
|
6e594ee66e | ||
|
|
c26d3e9c38 | ||
|
|
5db5607faa | ||
|
|
728f27e0a0 | ||
|
|
3d8f329e2d | ||
|
|
351fb70d5d | ||
|
|
b5cbeb9bde | ||
|
|
e7e89b8de7 | ||
|
|
225894d15c | ||
|
|
64ba485b0f | ||
|
|
3a666db36c | ||
|
|
ce7e5a2461 | ||
|
|
b22f94b079 | ||
|
|
3408465df6 | ||
|
|
e97dfb456b | ||
|
|
439c6c8b6c | ||
|
|
afa2b0307a | ||
|
|
56867fa777 | ||
|
|
9c2e33fa39 | ||
|
|
2ca72f838b | ||
|
|
37a9e6aae0 | ||
|
|
6a8a02dae5 | ||
|
|
eaee5db69e | ||
|
|
25d06904c6 | ||
|
|
fa14f87a80 | ||
|
|
4404f99642 | ||
|
|
bafab905b4 | ||
|
|
44d5e54550 | ||
|
|
a23c13d7d9 | ||
|
|
17a7a1432f | ||
|
|
26e8932b85 | ||
|
|
32beb02d40 | ||
|
|
af7177e6bb | ||
|
|
69ac0fd0a8 | ||
|
|
e0998f35e8 | ||
|
|
6d371b38c3 | ||
|
|
587f11138a | ||
|
|
aa936c279e | ||
|
|
5b11d351b2 | ||
|
|
b45b97d3c6 | ||
|
|
338eb3bdfe | ||
|
|
f059e91efc | ||
|
|
1cdf1c433f | ||
|
|
a7b8bac4c8 | ||
|
|
b7b5df0587 | ||
|
|
4060f6ecbc | ||
|
|
7cb5f21444 | ||
|
|
e5da46cfc3 | ||
|
|
eabb3e38b5 | ||
|
|
46140c8efa | ||
|
|
95d071ba56 | ||
|
|
3c9c3ca3b0 | ||
|
|
e7c4ade57d | ||
|
|
ca524657b6 | ||
|
|
bc02559bc7 | ||
|
|
741172fd98 | ||
|
|
83d0209775 | ||
|
|
6693d1acfb | ||
|
|
a2c43b50a6 | ||
|
|
f7fc06e657 | ||
|
|
b9fe3b9c87 | ||
|
|
06be993afc | ||
|
|
b6ef39fb30 | ||
|
|
0b131c00ed | ||
|
|
b6b8661c36 | ||
|
|
7bf19f8f6f | ||
|
|
c9d9c6513b | ||
|
|
4e7b7ae974 | ||
|
|
dfcabc02a4 | ||
|
|
6f2c5674c9 | ||
|
|
2877b9b505 | ||
|
|
e40bb9e14d | ||
|
|
d456ff9830 | ||
|
|
ffddb3b4ac | ||
|
|
a6113f237b | ||
|
|
093520b686 | ||
|
|
3a8d44b8e9 | ||
|
|
72bff652f7 | ||
|
|
9559bdf817 | ||
|
|
5a88b8c24e | ||
|
|
a9ebac82c7 | ||
|
|
cfd8836083 | ||
|
|
e01e59b188 | ||
|
|
d2fd729961 | ||
|
|
5d4ff2e3b7 | ||
|
|
6e5133f6b8 | ||
|
|
a96d5839b2 | ||
|
|
a827bc306a | ||
|
|
d8b3d7a6e0 | ||
|
|
b8f072909b | ||
|
|
fa48f2b2f0 | ||
|
|
019a1fe24e | ||
|
|
427620d34f | ||
|
|
a5a0c1f6e7 | ||
|
|
affef13037 | ||
|
|
4afbf20c1a | ||
|
|
0ef7b036dd | ||
|
|
17ef7b8b9e | ||
|
|
15eba52fad | ||
|
|
f2d894b036 | ||
|
|
d9da27710e | ||
|
|
981bff70c3 | ||
|
|
bb30d6e02f | ||
|
|
b09ccda54d | ||
|
|
a5de8d79ec | ||
|
|
a092a1e843 | ||
|
|
1c6740feff | ||
|
|
79c6e05e02 | ||
|
|
31e2085c16 | ||
|
|
64fda2f1a0 | ||
|
|
a674719a8b | ||
|
|
2f7ef0620b | ||
|
|
153e98b593 | ||
|
|
d62ea41671 | ||
|
|
8fcd9813d3 | ||
|
|
dcbf8c85dd | ||
|
|
ea0eafdb16 | ||
|
|
eda89a057a | ||
|
|
21e6eef1d3 | ||
|
|
54a27c1840 | ||
|
|
a3be0a1618 | ||
|
|
8e19e44f4c | ||
|
|
e5d93bd114 | ||
|
|
47fe3d5826 | ||
|
|
8409f5fd4a | ||
|
|
634a468ec4 | ||
|
|
7ed0544664 | ||
|
|
3d4a046634 | ||
|
|
96954b4aaa | ||
|
|
d77fc11552 | ||
|
|
5bad0bbde8 | ||
|
|
cd85ac611b | ||
|
|
0bc6d0a211 | ||
|
|
b1cd055342 | ||
|
|
303097b835 | ||
|
|
a438028002 | ||
|
|
4813872bcc | ||
|
|
3b19bfb429 | ||
|
|
a917a7bca6 | ||
|
|
86e64af35c | ||
|
|
bf6f1d8137 | ||
|
|
b1688525db | ||
|
|
2fd14430a2 | ||
|
|
bd3d959944 | ||
|
|
390e4853a5 | ||
|
|
485e603b51 | ||
|
|
3ccd7ad95d | ||
|
|
54d9050483 | ||
|
|
6d58c68e9b | ||
|
|
80019d4dc1 |
69
.github/workflows/build-image.yml
vendored
Normal file
69
.github/workflows/build-image.yml
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
name: build-image
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['v2']
|
||||||
|
paths:
|
||||||
|
- "build.trigger"
|
||||||
|
|
||||||
|
# schedule:
|
||||||
|
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||||
|
# - cron: '17 19 * * *'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-certd-image:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: get_certd_version
|
||||||
|
id: get_certd_version
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
result-encoding: string
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const pnpmWorkspace = "./pnpm-workspace.yaml";
|
||||||
|
fs.unlinkSync(pnpmWorkspace)
|
||||||
|
const jsonFilePath = "./packages/ui/certd-server/package.json";
|
||||||
|
const jsonContent = fs.readFileSync(jsonFilePath, 'utf-8');
|
||||||
|
const pkg = JSON.parse(jsonContent)
|
||||||
|
console.log("certd_version:",pkg.version);
|
||||||
|
return pkg.version
|
||||||
|
# - name: Use Node.js
|
||||||
|
# uses: actions/setup-node@v4
|
||||||
|
# with:
|
||||||
|
# node-version: 18
|
||||||
|
# cache: 'npm'
|
||||||
|
# working-directory: ./packages/ui/certd-client
|
||||||
|
- run: |
|
||||||
|
npm install -g pnpm
|
||||||
|
pnpm install
|
||||||
|
npm run build
|
||||||
|
working-directory: ./packages/ui/certd-client
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to aliyun container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: registry.cn-shenzhen.aliyuncs.com
|
||||||
|
username: ${{ secrets.aliyun_cs_username }}
|
||||||
|
password: ${{ secrets.aliyun_cs_password }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6.5.0
|
||||||
|
with:
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
|
push: true
|
||||||
|
context: ./packages/ui/
|
||||||
|
tags: |
|
||||||
|
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||||
|
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}
|
||||||
55
.github/workflows/deploy-demo.yml
vendored
Normal file
55
.github/workflows/deploy-demo.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
name: deploy-demo
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: ['v2']
|
||||||
|
paths:
|
||||||
|
- "deploy.trigger"
|
||||||
|
workflow_run:
|
||||||
|
workflows: [ "build-image" ]
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
# schedule:
|
||||||
|
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||||
|
# - cron: '17 19 * * *'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy-certd-demo:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: get_certd_version
|
||||||
|
id: get_certd_version
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
result-encoding: string
|
||||||
|
script: |
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const jsonFilePath = "./packages/ui/certd-server/package.json";
|
||||||
|
const jsonContent = fs.readFileSync(jsonFilePath, 'utf-8');
|
||||||
|
const pkg = JSON.parse(jsonContent)
|
||||||
|
console.log("certd_version:",pkg.version);
|
||||||
|
return pkg.version
|
||||||
|
- uses: GuillaumeFalourd/wait-sleep-action@v1
|
||||||
|
with:
|
||||||
|
time: '10' # for 60 seconds
|
||||||
|
- name: Send HTTP request
|
||||||
|
id: request
|
||||||
|
uses: tyrrrz/action-http-request@master
|
||||||
|
with:
|
||||||
|
url: http://flow-openapi.aliyun.com/pipeline/webhook/lzCzlGrLCOHQaTMMt0mG
|
||||||
|
method: POST
|
||||||
|
headers: |
|
||||||
|
Content-Type: application/json
|
||||||
|
body: |
|
||||||
|
{
|
||||||
|
"CERTD_VERSION": "${{steps.get_certd_version.outputs.result}}"
|
||||||
|
}
|
||||||
|
retry-count: 3
|
||||||
|
retry-delay: 5000
|
||||||
|
|
||||||
|
|
||||||
2
.github/workflows/sync-to-gitee.yml
vendored
2
.github/workflows/sync-to-gitee.yml
vendored
@@ -2,8 +2,6 @@ name: sync-to-gitee
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: ['v2']
|
branches: ['v2']
|
||||||
pull_request:
|
|
||||||
branches: ['v2']
|
|
||||||
# schedule:
|
# schedule:
|
||||||
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
# - # 国际时间 19:17 执行,北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
|
||||||
# - cron: '17 19 * * *'
|
# - cron: '17 19 * * *'
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -34,3 +34,8 @@ gen
|
|||||||
|
|
||||||
docker/image/workspace
|
docker/image/workspace
|
||||||
/packages/core/lego
|
/packages/core/lego
|
||||||
|
|
||||||
|
tsconfig.tsbuildinfo
|
||||||
|
test/**/*.js
|
||||||
|
/packages/ui/certd-server/data/db.sqlite
|
||||||
|
/packages/ui/certd-server/data/keys.yaml
|
||||||
|
|||||||
3
.npmrc
3
.npmrc
@@ -1 +1,2 @@
|
|||||||
link-workspace-packages=deep
|
link-workspace-packages=true
|
||||||
|
prefer-workspace-packages=true
|
||||||
|
|||||||
7
.prettierrc
Normal file
7
.prettierrc
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 160,
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
||||||
79
CHANGELOG.md
79
CHANGELOG.md
@@ -3,6 +3,85 @@
|
|||||||
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.22.9](https://github.com/certd/certd/compare/v1.22.8...v1.22.9) (2024-08-05)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 优化定时任务 ([87e440e](https://github.com/certd/certd/commit/87e440ee2a8b10dc571ce619f28bc83c1e5eb147))
|
||||||
|
|
||||||
|
## [1.22.8](https://github.com/certd/certd/compare/v1.22.7...v1.22.8) (2024-08-05)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 修复删除历史记录没有删除log的bug,新增history管理页面,演示站点启动时不自动启动非管理员用户的定时任务 ([f78ae93](https://github.com/certd/certd/commit/f78ae93eedfe214008c3d071ca3d77c962137a64))
|
||||||
|
* 优化pipeline删除时,删除其他history ([b425203](https://github.com/certd/certd/commit/b4252033d56a9ad950f3e204ff021497c3978015))
|
||||||
|
|
||||||
|
## [1.22.7](https://github.com/certd/certd/compare/v1.22.6...v1.22.7) (2024-08-04)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复保存配置报id不能为空的bug ([367f807](https://github.com/certd/certd/commit/367f80731396003416665c22853dfbc09c2c03a0))
|
||||||
|
|
||||||
|
## [1.22.6](https://github.com/certd/certd/compare/v1.22.5...v1.22.6) (2024-08-03)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复在相同的cron时偶尔无法触发定时任务的bug ([680941a](https://github.com/certd/certd/commit/680941af119619006b592e3ab6fb112cb5556a8b))
|
||||||
|
* 修复pg下pipeline title 类型问题 ([a9717b9](https://github.com/certd/certd/commit/a9717b9a0df7b5a64d4fe03314fecad4f59774cc))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 流水线支持名称模糊查询 ([59897c4](https://github.com/certd/certd/commit/59897c4ceae992ebe2972ca9e8f9196616ffdfd7))
|
||||||
|
* 腾讯云clb支持更多大区选择 ([e4f4570](https://github.com/certd/certd/commit/e4f4570b29f26c60f1ee9660a4c507cbeaba3d7e))
|
||||||
|
* 优化前置任务输出为空的提示 ([6ed1e18](https://github.com/certd/certd/commit/6ed1e18c7d9c46d964ecc6abc90f3908297b7632))
|
||||||
|
|
||||||
|
## [1.22.5](https://github.com/certd/certd/compare/v1.22.4...v1.22.5) (2024-07-26)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复用户管理无法添加用户的bug ([e7e89b8](https://github.com/certd/certd/commit/e7e89b8de7386e84c0d6b8e217e2034909657d68))
|
||||||
|
|
||||||
|
## [1.22.4](https://github.com/certd/certd/compare/v1.22.3...v1.22.4) (2024-07-26)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 证书申请支持反向代理,letsencrypt无法访问时的备用方案 ([b7b5df0](https://github.com/certd/certd/commit/b7b5df0587e0f7ea288c1b2af6f87211f207395f))
|
||||||
|
* 支持arm64 ([fa14f87](https://github.com/certd/certd/commit/fa14f87a8093ef3addc5e5f3315ce1bfc9982782))
|
||||||
|
|
||||||
|
## [1.22.3](https://github.com/certd/certd/compare/v1.22.2...v1.22.3) (2024-07-25)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* lege 无执行权限问题 ([338eb3b](https://github.com/certd/certd/commit/338eb3bdfeb461e9b3bc7eee97b97a59f5642ffe))
|
||||||
|
|
||||||
|
## [1.22.2](https://github.com/certd/certd/compare/v1.22.1...v1.22.2) (2024-07-23)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复创建流水线时,无法根据dns类型默认正确的dns授权的bug ([a2c43b5](https://github.com/certd/certd/commit/a2c43b50a6069ed48958fd142844a8568c2af452))
|
||||||
|
|
||||||
|
## [1.22.1](https://github.com/certd/certd/compare/v1.22.0...v1.22.1) (2024-07-20)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 创建证书任务可以选择lege插件 ([affef13](https://github.com/certd/certd/commit/affef130378030c517250c58a4e787b0fc85d7d1))
|
||||||
|
* 创建证书任务增加定时任务和邮件通知输入 ([427620d](https://github.com/certd/certd/commit/427620d34f3b8ad6933005faf1878908441a2453))
|
||||||
|
* 支持配置启动后自动触发一次任务 ([a5a0c1f](https://github.com/certd/certd/commit/a5a0c1f6e7a3f05e581005e491d5b102ee854412))
|
||||||
|
|
||||||
|
# [1.22.0](https://github.com/certd/certd/compare/v1.21.2...v1.22.0) (2024-07-19)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* 升级midway,支持esm ([485e603](https://github.com/certd/certd/commit/485e603b5165c28bc08694997726eaf2a585ebe7))
|
||||||
|
* 支持lego,海量DNS提供商 ([0bc6d0a](https://github.com/certd/certd/commit/0bc6d0a211920fb0084d705e1db67ee1e7262c44))
|
||||||
|
* 支持postgresql ([3b19bfb](https://github.com/certd/certd/commit/3b19bfb4291e89064b3b407a80dae092d54747d5))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 优化一些小细节 ([b168852](https://github.com/certd/certd/commit/b1688525dbbbfd67e0ab1cf5b4ddfbe9d394f370))
|
||||||
|
* 增加备案号设置 ([bd3d959](https://github.com/certd/certd/commit/bd3d959944db63a5690b55ee150e1007133868b9))
|
||||||
|
* 自动生成jwtkey,无需手动配置 ([390e485](https://github.com/certd/certd/commit/390e4853a570390a97df6a3b3882579f9547eeb4))
|
||||||
|
|
||||||
## [1.21.2](https://github.com/certd/certd/compare/v1.21.1...v1.21.2) (2024-07-08)
|
## [1.21.2](https://github.com/certd/certd/compare/v1.21.1...v1.21.2) (2024-07-08)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
111
README.md
111
README.md
@@ -8,16 +8,14 @@ CertD 是一个免费全自动申请和自动部署更新SSL证书的工具。
|
|||||||
## 一、特性
|
## 一、特性
|
||||||
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
|
本项目不仅支持证书申请过程自动化,还可以自动化部署更新证书,让你的证书永不过期。
|
||||||
|
|
||||||
* 全自动申请证书(支持阿里云、腾讯云、华为云、Cloudflare注册的域名)
|
* 全自动申请证书(支持阿里云、腾讯云、华为云、Cloudflare等各种途径注册的域名)
|
||||||
* 全自动部署更新证书(目前支持服务器上传部署、部署到阿里云、腾讯云等)
|
* 全自动部署更新证书(目前支持部署到主机、部署到阿里云、腾讯云等)
|
||||||
* 支持通配符域名
|
* 支持通配符域名/泛域名,支持多个域名打到一个证书上
|
||||||
* 支持多个域名打到一个证书上
|
|
||||||
* 邮件通知
|
* 邮件通知
|
||||||
* 证书自动更新
|
* 私有化部署,保障安全
|
||||||
* 免费、免费、免费([阿里云单个通配符域名证书最便宜也要1800/年](https://yundun.console.aliyun.com/?p=cas#/certExtend/buy/cn-hangzhou))
|
* 免费、免费、免费([阿里云单个通配符域名证书最便宜也要1800/年](https://yundun.console.aliyun.com/?p=cas#/certExtend/buy/cn-hangzhou))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 二、在线体验
|
## 二、在线体验
|
||||||
|
|
||||||
官方Demo地址,自助注册后体验
|
官方Demo地址,自助注册后体验
|
||||||
@@ -40,7 +38,9 @@ https://certd.handsfree.work/
|
|||||||
-------> [点我查看详细使用步骤演示](./step.md) <--------
|
-------> [点我查看详细使用步骤演示](./step.md) <--------
|
||||||
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
|
↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
|
||||||
|
|
||||||
## 四、本地docker部署
|
## 四、私有化部署
|
||||||
|
|
||||||
|
由于证书、授权信息等属于高度敏感数据,请务必私有化部署,保障数据安全
|
||||||
|
|
||||||
### 1. 安装docker、docker-compose
|
### 1. 安装docker、docker-compose
|
||||||
|
|
||||||
@@ -49,61 +49,71 @@ https://certd.handsfree.work/
|
|||||||
* 【腾讯云】云服务器2核2G,新老用户同享,99元/年,续费同价!【 [立即购买](https://cloud.tencent.com/act/cps/redirect?redirect=6094&cps_key=b3ef73330335d7a6efa4a4bbeeb6b2c9&from=console)】
|
* 【腾讯云】云服务器2核2G,新老用户同享,99元/年,续费同价!【 [立即购买](https://cloud.tencent.com/act/cps/redirect?redirect=6094&cps_key=b3ef73330335d7a6efa4a4bbeeb6b2c9&from=console)】
|
||||||
|
|
||||||
|
|
||||||
1.2 安装docker
|
1.2 安装docker
|
||||||
https://docs.docker.com/engine/install/
|
|
||||||
选择对应的操作系统,按照官方文档执行命令即可
|
|
||||||
|
|
||||||
|
https://docs.docker.com/engine/install/
|
||||||
|
选择对应的操作系统,按照官方文档执行命令即可
|
||||||
|
|
||||||
### 2. 下载docker-compose.yaml文件
|
### 2. 运行certd
|
||||||
|
|
||||||
[docker-compose.yaml下载](https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml)
|
[docker-compose.yaml 下载](https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml)
|
||||||
|
|
||||||
|
当前版本号: 
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# 随便创建一个目录
|
||||||
mkdir certd
|
mkdir certd
|
||||||
|
# 进入目录
|
||||||
cd certd
|
cd certd
|
||||||
# wget下载docker-compose.yaml文件
|
# 下载docker-compose.yaml文件,或者手动下载放到certd目录下
|
||||||
wget https://raw.githubusercontent.com/certd/certd/v2/docker/run/docker-compose.yaml
|
|
||||||
# 或者使用gitee地址
|
|
||||||
wget https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml
|
wget https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml
|
||||||
|
|
||||||
# 根据需要修改里面的配置
|
# 可以根据需要修改里面的配置
|
||||||
# 1.修改镜像版本号【可选】
|
# 1.修改镜像版本号【可选】
|
||||||
# 2.配置数据保存路径【可选】
|
# 2.配置数据保存路径【可选】
|
||||||
# 3.配置certd_auth_jwt_secret【必须】
|
# 3.修改端口号【可选】
|
||||||
vi docker-compose.yaml
|
vi docker-compose.yaml # 【可选】
|
||||||
|
|
||||||
|
# 启动certd
|
||||||
```
|
|
||||||
> 镜像版本号与release版本号同步:
|
|
||||||
https://github.com/certd/certd/releases
|
|
||||||
|
|
||||||
|
|
||||||
### 3. 运行
|
|
||||||
```bash
|
|
||||||
# 如果docker compose是插件化安装
|
|
||||||
export CERTD_VERSION=latest
|
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
||||||
```
|
```
|
||||||
如果提示 没有compose命令,请安装docker-compose
|
> 如果提示 没有compose命令,请安装docker-compose
|
||||||
https://docs.docker.com/compose/install/linux/
|
> https://docs.docker.com/compose/install/linux/
|
||||||
|
|
||||||
|
#### 镜像说明:
|
||||||
|
* certd镜像地址:
|
||||||
|
* `registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest`
|
||||||
|
|
||||||
|
* 镜像构建通过`Actions`自动执行,过程公开透明,请放心使用
|
||||||
|
* [点我查看镜像构建日志](https://github.com/certd/certd/actions/workflows/build-image.yml)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
### 3. 访问
|
||||||
|
|
||||||
### 4. 访问
|
http://your_server_ip:7001
|
||||||
|
默认账号密码:admin/123456
|
||||||
http://your_server_ip:7001
|
记得修改密码
|
||||||
默认账号密码:admin/123456
|
|
||||||
记得修改密码
|
|
||||||
|
|
||||||
|
|
||||||
### 5. 升级
|
## 五、 升级
|
||||||
|
如果使用固定版本号
|
||||||
|
1. 修改`docker-compose.yaml`中的镜像版本号
|
||||||
|
2. 运行 `docker compose up -d` 即可
|
||||||
|
|
||||||
* 修改版本号,重新运行 `docker compose up -d` 即可
|
如果使用`latest`版本
|
||||||
* 数据存在`/data/certd`目录下,不用担心数据丢失
|
1. 重新拉取镜像 `docker pull registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest`
|
||||||
|
2. 重新启动容器 `docker compose restart`
|
||||||
|
|
||||||
|
> 数据默认存在`/data/certd`目录下,不用担心数据丢失
|
||||||
|
|
||||||
|
|
||||||
|
更新日志: [CHANGELOG](./CHANGELOG.md)
|
||||||
|
|
||||||
## 五、一些说明
|
|
||||||
|
## 六、一些说明
|
||||||
* 本项目ssl证书提供商为letencrypt
|
* 本项目ssl证书提供商为letencrypt
|
||||||
* 申请过程遵循acme协议
|
* 申请过程遵循acme协议
|
||||||
* 需要验证域名所有权,一般有两种方式(目前本项目仅支持dns-01)
|
* 需要验证域名所有权,一般有两种方式(目前本项目仅支持dns-01)
|
||||||
@@ -115,14 +125,15 @@ http://your_server_ip:7001
|
|||||||
* 免费证书过期时间90天,以后可能还会缩短,所以自动化部署必不可少
|
* 免费证书过期时间90天,以后可能还会缩短,所以自动化部署必不可少
|
||||||
* 设置每天自动运行,当证书过期前20天,会自动重新申请证书并部署
|
* 设置每天自动运行,当证书过期前20天,会自动重新申请证书并部署
|
||||||
|
|
||||||
## 六、不同平台的设置说明
|
|
||||||
|
## 七、不同平台的设置说明
|
||||||
|
|
||||||
* [Cloudflare](./doc/cf/cf.md)
|
* [Cloudflare](./doc/cf/cf.md)
|
||||||
* [腾讯云](./doc/tencent/tencent.md)
|
* [腾讯云](./doc/tencent/tencent.md)
|
||||||
* [windows主机](./doc/host/host.md)
|
* [windows主机](./doc/host/host.md)
|
||||||
|
|
||||||
|
|
||||||
## 七、问题处理
|
## 八、问题处理
|
||||||
### 7.1 忘记管理员密码
|
### 7.1 忘记管理员密码
|
||||||
解决方法如下:
|
解决方法如下:
|
||||||
1. 修改docker-compose.yaml文件,将环境变量`certd_system_resetAdminPassword`改为`true`
|
1. 修改docker-compose.yaml文件,将环境变量`certd_system_resetAdminPassword`改为`true`
|
||||||
@@ -143,9 +154,9 @@ docker logs -f --tail 500 certd
|
|||||||
```shell
|
```shell
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
5. 使用admin/123456登录系统,请及时修改管理员密码
|
5. 使用`admin/123456`登录系统,请及时修改管理员密码
|
||||||
|
|
||||||
## 八、联系作者
|
## 九、联系作者
|
||||||
如有疑问,欢迎加入群聊(请备注certd)
|
如有疑问,欢迎加入群聊(请备注certd)
|
||||||
* QQ群:141236433
|
* QQ群:141236433
|
||||||
* 微信群:
|
* 微信群:
|
||||||
@@ -157,7 +168,7 @@ docker compose up -d
|
|||||||
<img height="230" src="./doc/images/me.png">
|
<img height="230" src="./doc/images/me.png">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 九、捐赠
|
## 十、捐赠
|
||||||
媳妇儿说:“一天到晚搞开源,也不管管老婆孩子!😡😡😡”
|
媳妇儿说:“一天到晚搞开源,也不管管老婆孩子!😡😡😡”
|
||||||
拜托各位捐赠支持一下,让媳妇儿开心开心,我也能有更多时间进行开源项目,感谢🙏🙏🙏
|
拜托各位捐赠支持一下,让媳妇儿开心开心,我也能有更多时间进行开源项目,感谢🙏🙏🙏
|
||||||
<p align="center">
|
<p align="center">
|
||||||
@@ -165,16 +176,20 @@ docker compose up -d
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
## 十、贡献代码
|
## 十一、贡献代码
|
||||||
|
|
||||||
[贡献插件教程](./plugin.md)
|
[贡献插件教程](./plugin.md)
|
||||||
|
|
||||||
|
|
||||||
## 十一、我的其他项目
|
## 十二、我的其他项目(求Star)
|
||||||
* [袖手GPT](https://ai.handsfree.work/) ChatGPT,国内可用,无需FQ,每日免费额度
|
* [袖手GPT](https://ai.handsfree.work/) ChatGPT,国内可用,无需FQ,每日免费额度
|
||||||
* [fast-crud](https://gitee.com/fast-crud/fast-crud/) 基于vue3的crud快速开发框架
|
* [fast-crud](https://gitee.com/fast-crud/fast-crud/) 基于vue3的crud快速开发框架
|
||||||
* [dev-sidecar](https://github.com/docmirror/dev-sidecar/) 直连访问github工具,无需FQ,解决github无法访问的问题
|
* [dev-sidecar](https://github.com/docmirror/dev-sidecar/) 直连访问github工具,无需FQ,解决github无法访问的问题
|
||||||
|
|
||||||
|
|
||||||
## 十二、版本更新日志
|
|
||||||
https://github.com/certd/certd/blob/v2/CHANGELOG.md
|
## 十三、更新日志
|
||||||
|
|
||||||
|
更新日志:[CHANGELOG](./CHANGELOG.md)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
build.trigger
Normal file
1
build.trigger
Normal file
@@ -0,0 +1 @@
|
|||||||
|
13:20
|
||||||
11
deploy.js
11
deploy.js
@@ -32,8 +32,9 @@ async function getPackages(directoryPath) {
|
|||||||
async function getAllPackages() {
|
async function getAllPackages() {
|
||||||
const base = await getPackages("./packages/core")
|
const base = await getPackages("./packages/core")
|
||||||
const plugins = await getPackages("./packages/plugins")
|
const plugins = await getPackages("./packages/plugins")
|
||||||
|
const libs = await getPackages("./packages/libs")
|
||||||
|
|
||||||
return base.concat(plugins)
|
return base.concat(plugins).concat(libs)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sync() {
|
async function sync() {
|
||||||
@@ -48,7 +49,7 @@ async function sync() {
|
|||||||
data: {}
|
data: {}
|
||||||
})
|
})
|
||||||
console.log(`sync success:${pkg}`)
|
console.log(`sync success:${pkg}`)
|
||||||
await sleep(100*1000)
|
await sleep(30*1000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,7 +79,7 @@ async function triggerBuild() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
console.log(`webhook success:${webhook}`)
|
console.log(`webhook success:${webhook}`)
|
||||||
await sleep(10*60*1000)
|
await sleep(30*60*1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -86,9 +87,9 @@ async function triggerBuild() {
|
|||||||
async function start() {
|
async function start() {
|
||||||
// await build()
|
// await build()
|
||||||
console.log("等待60秒")
|
console.log("等待60秒")
|
||||||
await sleep(200 * 1000)
|
await sleep(100* 1000)
|
||||||
await sync()
|
await sync()
|
||||||
await sleep(60 * 1000)
|
await sleep(100 * 1000)
|
||||||
await triggerBuild()
|
await triggerBuild()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
deploy.trigger
Normal file
1
deploy.trigger
Normal file
@@ -0,0 +1 @@
|
|||||||
|
5
|
||||||
BIN
doc/images/action-build.jpg
Normal file
BIN
doc/images/action-build.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 109 KiB |
8
doc/server/free.md
Normal file
8
doc/server/free.md
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
|
||||||
|
# 免费服务器部署
|
||||||
|
|
||||||
|
## 1. 注册koyeb账号
|
||||||
|
|
||||||
|
https://app.koyeb.com/
|
||||||
|
|
||||||
|
## 2. 创建应用
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
FROM registry.cn-shenzhen.aliyuncs.com/handsfree/node:18-alpine
|
|
||||||
EXPOSE 7001
|
|
||||||
ENV NODE_ENV production
|
|
||||||
ENV MIDWAY_SERVER_ENV production
|
|
||||||
WORKDIR /app/
|
|
||||||
#RUN npm install -g pnpm
|
|
||||||
#RUN npm install cross-env -g --registry=https://registry.npmmirror.com
|
|
||||||
#RUN npm install pm2 -g --registry=https://registry.npmmirror.com
|
|
||||||
#RUN pm2 install pm2-logrotate
|
|
||||||
ADD ./workspace/certd-server/ /app/
|
|
||||||
RUN sed -i "s/workspace://g" "/app/package.json"
|
|
||||||
RUN yarn install --production --registry=https://registry.npmmirror.com
|
|
||||||
#RUN yarn install --production
|
|
||||||
RUN npm run build
|
|
||||||
#CMD ["pm2-runtime", "start", "./bootstrap.js","--name", "certd","-i","1"]
|
|
||||||
CMD ["npm", "run","start"]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
echo "请先输入一个版本号:"
|
|
||||||
read version
|
|
||||||
|
|
||||||
echo "您输入的版本号是: $version"
|
|
||||||
echo "登录aliyun镜像仓库"
|
|
||||||
sudo docker login --username=252959493@qq.com registry.cn-shenzhen.aliyuncs.com
|
|
||||||
|
|
||||||
build=$(pwd)
|
|
||||||
cd ../../
|
|
||||||
root=$(pwd)
|
|
||||||
echo "安装依赖"
|
|
||||||
#pnpm install --registry=https://registry.npmmirror.com
|
|
||||||
pnpm install
|
|
||||||
|
|
||||||
echo "client build"
|
|
||||||
cd $root/packages/ui/certd-client
|
|
||||||
pnpm run build
|
|
||||||
echo "client build success"
|
|
||||||
|
|
||||||
echo "server build"
|
|
||||||
cd $root/packages/ui/certd-server
|
|
||||||
pnpm run build
|
|
||||||
echo "server build success"
|
|
||||||
|
|
||||||
echo "rm node_modules"
|
|
||||||
rm ./node_modules -rf
|
|
||||||
|
|
||||||
echo "copy to workspace"
|
|
||||||
mkdir -p $build/workspace/certd-server
|
|
||||||
\cp ./* $build/workspace/certd-server -rf
|
|
||||||
\cp ../certd-client/dist/* $build/workspace/certd-server/public/ -rf
|
|
||||||
|
|
||||||
#export TAG=$version
|
|
||||||
#sudo -E docker compose build
|
|
||||||
#sudo -E docker compose push
|
|
||||||
@@ -1,26 +1,33 @@
|
|||||||
version: '3.3'
|
version: '3.3'
|
||||||
services:
|
services:
|
||||||
certd:
|
certd:
|
||||||
# 镜像 # ↓↓↓↓↓ --- 1、 修改镜像版本号,或者干脆写成latest, 如果设置了环境变量 export CERTD_VERSION=latest,这里可以不修改
|
# 镜像 # ↓↓↓↓↓ --- 1、 镜像版本号,建议改成固定版本号【可选】
|
||||||
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${CERTD_VERSION}
|
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||||
container_name: certd # 容器名
|
container_name: certd # 容器名
|
||||||
restart: unless-stopped # 自动重启
|
restart: unless-stopped # 自动重启
|
||||||
volumes:
|
volumes:
|
||||||
# ↓↓↓↓↓ ------------------------------------------------------- 2、 修改数据库以及证书存储路径【可选】
|
# ↓↓↓↓↓ ------------------------------------------------------- 2、 数据库以及证书存储路径,默认存在宿主机的/data/certd/目录下【可选】
|
||||||
- /data/certd:/app/data
|
- /data/certd:/app/data
|
||||||
ports: # 端口映射
|
ports: # 端口映射
|
||||||
# ↓↓↓↓ 如果端口有冲突,可以修改第一个7001为其他不冲突的端口号
|
# ↓↓↓↓ ----------------------------------------------------------3、如果端口有冲突,可以修改第一个7001为其他不冲突的端口号【可选】
|
||||||
- "7001:7001"
|
- "7001:7001"
|
||||||
|
dns:
|
||||||
|
# 如果出现getaddrinfo ENOTFOUND等错误,可以尝试修改或注释dns配置
|
||||||
|
- 223.5.5.5
|
||||||
|
- 223.6.6.6
|
||||||
|
- 8.8.8.8
|
||||||
|
- 8.8.4.4
|
||||||
environment: # 环境变量
|
environment: # 环境变量
|
||||||
- TZ=Asia/Shanghai
|
- TZ=Asia/Shanghai
|
||||||
- certd_auth_jwt_secret=changeme
|
|
||||||
# ↑↑↑↑↑ ---------------------------------- 3、 修改成你的自定义密钥【必须,安全需要】
|
|
||||||
- certd_system_resetAdminPassword=false
|
- certd_system_resetAdminPassword=false
|
||||||
# ↑↑↑↑↑ 如果忘记管理员密码,可以设置为true,重启之后,管理员密码将改成123456,然后请及时修改回false
|
# ↑↑↑↑↑---------------------------4、如果忘记管理员密码,可以设置为true,重启之后,管理员密码将改成123456,然后请及时修改回false【可选】
|
||||||
|
- certd_cron_immediateTriggerOnce=false
|
||||||
|
# ↑↑↑↑↑---------------------------5、如果设置为true,启动后所有配置了cron的流水线任务都将被立即触发一次【可选】
|
||||||
|
- VITE_APP_ICP_NO=
|
||||||
|
# ↑↑↑↑↑ -----------------------------------------6、这里可以设置备案号【可选】
|
||||||
# 设置环境变量即可自定义certd配置
|
# 设置环境变量即可自定义certd配置
|
||||||
# 服务端配置项见: packages/ui/certd-server/src/config/config.default.ts
|
# 服务端配置项见: packages/ui/certd-server/src/config/config.default.ts
|
||||||
# 服务端配置规则: certd_ + 配置项, 点号用_代替
|
# 服务端配置规则: certd_ + 配置项, 点号用_代替
|
||||||
# 如jwt密钥配置为: auth.jwt.secret,则设置环境变量 certd_auth_jwt_secret=changeme
|
|
||||||
|
|
||||||
# 客户端配置项见: packages/ui/certd-client/.env
|
# 客户端配置项见: packages/ui/certd-client/.env
|
||||||
# 按实际名称配置环境变量即可,如: VITE_APP_API=http://localhost:7001
|
# 按实际名称配置环境变量即可,如: VITE_APP_API=http://localhost:7001
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
# 判断$CERTD_VERSION 是否存在
|
|
||||||
if [ -n "$CERTD_VERSION" ]; then
|
|
||||||
echo "CERTD_VERSION is set = $CERTD_VERSION"
|
|
||||||
else
|
|
||||||
echo "CERTD_VERSION is not set"
|
|
||||||
echo "请先输入一个版本号(如 1.0.6):"
|
|
||||||
read CERTD_VERSION
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "您输入的版本号是: $CERTD_VERSION"
|
|
||||||
sudo -E docker compose up -d
|
|
||||||
@@ -9,5 +9,5 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"npmClient": "pnpm",
|
"npmClient": "pnpm",
|
||||||
"version": "1.21.2"
|
"version": "1.22.9"
|
||||||
}
|
}
|
||||||
|
|||||||
14
package.json
14
package.json
@@ -12,16 +12,18 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "lerna bootstrap --hoist",
|
"start": "lerna bootstrap --hoist",
|
||||||
"i-all": "lerna link && lerna exec npm install ",
|
"i-all": "lerna link && lerna exec npm install ",
|
||||||
"publish": "npm run prepublishOnly1 && lerna publish --conventional-commits && npm run afterpublishOnly && npm run deploy1",
|
"publish": "npm run prepublishOnly1 && lerna publish --conventional-commits --create-release github && npm run afterpublishOnly",
|
||||||
"afterpublishOnly": "",
|
"afterpublishOnly": "time /t >build.trigger && git add ./build.trigger && git commit -m \"build: trigger build image\" && TIMEOUT /T 10 && git push",
|
||||||
"prepublishOnly1": "npm run check && npm run before-build && lerna run build ",
|
"prepublishOnly1": "npm run check && lerna run build ",
|
||||||
"before-build": "cd ./packages/core/acme-client && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"",
|
"prepublishOnly2": "npm run check && npm run before-build && lerna run build ",
|
||||||
|
"before-build": "cd ./packages/core/pipeline && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"",
|
||||||
"deploy1": "node --experimental-json-modules deploy.js ",
|
"deploy1": "node --experimental-json-modules deploy.js ",
|
||||||
"check": "node --experimental-json-modules publish-check.js"
|
"check": "node --experimental-json-modules publish-check.js",
|
||||||
|
"init": "lerna run build"
|
||||||
},
|
},
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.7.2",
|
||||||
"lodash": "^4.17.21"
|
"lodash": "^4.17.21"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Download and install
|
# Download and install
|
||||||
wget -nv "https://github.com/letsencrypt/pebble/releases/download/v${PEBBLECTS_VERSION}/pebble-challtestsrv_linux-amd64" -O /usr/local/bin/pebble-challtestsrv
|
wget -nv "https://github.com/letsencrypt/pebble/releases/download/v${PEBBLECTS_VERSION}/pebble-challtestsrv-linux-amd64.tar.gz" -O /tmp/pebble-challtestsrv.tar.gz
|
||||||
|
tar zxvf /tmp/pebble-challtestsrv.tar.gz -C /tmp
|
||||||
|
|
||||||
|
mv /tmp/pebble-challtestsrv-linux-amd64/linux/amd64/pebble-challtestsrv /usr/local/bin/pebble-challtestsrv
|
||||||
chown root:root /usr/local/bin/pebble-challtestsrv
|
chown root:root /usr/local/bin/pebble-challtestsrv
|
||||||
chmod 0755 /usr/local/bin/pebble-challtestsrv
|
chmod 0755 /usr/local/bin/pebble-challtestsrv
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,10 @@ wget -nv "https://raw.githubusercontent.com/letsencrypt/pebble/v${PEBBLE_VERSION
|
|||||||
wget -nv "https://raw.githubusercontent.com/letsencrypt/pebble/v${PEBBLE_VERSION}/test/config/${CONFIG_NAME}" -O /etc/pebble/pebble.json
|
wget -nv "https://raw.githubusercontent.com/letsencrypt/pebble/v${PEBBLE_VERSION}/test/config/${CONFIG_NAME}" -O /etc/pebble/pebble.json
|
||||||
|
|
||||||
# Download and install Pebble
|
# Download and install Pebble
|
||||||
wget -nv "https://github.com/letsencrypt/pebble/releases/download/v${PEBBLE_VERSION}/pebble_linux-amd64" -O /usr/local/bin/pebble
|
wget -nv "https://github.com/letsencrypt/pebble/releases/download/v${PEBBLE_VERSION}/pebble-linux-amd64.tar.gz" -O /tmp/pebble.tar.gz
|
||||||
|
tar zxvf /tmp/pebble.tar.gz -C /tmp
|
||||||
|
|
||||||
|
mv /tmp/pebble-linux-amd64/linux/amd64/pebble /usr/local/bin/pebble
|
||||||
chown root:root /usr/local/bin/pebble
|
chown root:root /usr/local/bin/pebble
|
||||||
chmod 0755 /usr/local/bin/pebble
|
chmod 0755 /usr/local/bin/pebble
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ jobs:
|
|||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node: [16, 18, 20, 22]
|
node: [16, 18, 20]
|
||||||
eab: [0, 1]
|
eab: [0, 1]
|
||||||
|
|
||||||
#
|
#
|
||||||
@@ -19,9 +19,9 @@ jobs:
|
|||||||
FORCE_COLOR: 1
|
FORCE_COLOR: 1
|
||||||
NPM_CONFIG_COLOR: always
|
NPM_CONFIG_COLOR: always
|
||||||
|
|
||||||
PEBBLE_VERSION: 2.3.1
|
PEBBLE_VERSION: 2.6.0
|
||||||
PEBBLE_ALTERNATE_ROOTS: 2
|
PEBBLE_ALTERNATE_ROOTS: 2
|
||||||
PEBBLECTS_VERSION: 2.3.1
|
PEBBLECTS_VERSION: 2.6.0
|
||||||
PEBBLECTS_DNS_PORT: 8053
|
PEBBLECTS_DNS_PORT: 8053
|
||||||
COREDNS_VERSION: 1.11.1
|
COREDNS_VERSION: 1.11.1
|
||||||
|
|
||||||
|
|||||||
@@ -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.22.6](https://github.com/publishlab/node-acme-client/compare/v1.22.5...v1.22.6) (2024-08-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
|
## [1.22.4](https://github.com/publishlab/node-acme-client/compare/v1.22.3...v1.22.4) (2024-07-26)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 证书申请支持反向代理,letsencrypt无法访问时的备用方案 ([b7b5df0](https://github.com/publishlab/node-acme-client/commit/b7b5df0587e0f7ea288c1b2af6f87211f207395f))
|
||||||
|
|
||||||
|
## [1.22.3](https://github.com/publishlab/node-acme-client/compare/v1.22.2...v1.22.3) (2024-07-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
|
## [1.22.2](https://github.com/publishlab/node-acme-client/compare/v1.22.1...v1.22.2) (2024-07-23)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
|
## [1.22.1](https://github.com/publishlab/node-acme-client/compare/v1.22.0...v1.22.1) (2024-07-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
|
# [1.22.0](https://github.com/publishlab/node-acme-client/compare/v1.21.2...v1.22.0) (2024-07-19)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* 升级midway,支持esm ([485e603](https://github.com/publishlab/node-acme-client/commit/485e603b5165c28bc08694997726eaf2a585ebe7))
|
||||||
|
|
||||||
## [1.21.2](https://github.com/publishlab/node-acme-client/compare/v1.21.1...v1.21.2) (2024-07-08)
|
## [1.21.2](https://github.com/publishlab/node-acme-client/compare/v1.21.1...v1.21.2) (2024-07-08)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/acme-client
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
@@ -82,6 +110,11 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v5.4.0
|
||||||
|
|
||||||
|
* `added` Directory URLs for [Google](https://cloud.google.com/certificate-manager/docs/overview) ACME provider
|
||||||
|
* `fixed` Invalidate ACME directory cache after 24 hours
|
||||||
|
|
||||||
## v5.3.1 (2024-05-22)
|
## v5.3.1 (2024-05-22)
|
||||||
|
|
||||||
* `fixed` Allow `client.auto()` being called with an empty CSR common name
|
* `fixed` Allow `client.auto()` being called with an empty CSR common name
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ const client = new acme.Client({
|
|||||||
acme.directory.buypass.staging;
|
acme.directory.buypass.staging;
|
||||||
acme.directory.buypass.production;
|
acme.directory.buypass.production;
|
||||||
|
|
||||||
|
acme.directory.google.staging;
|
||||||
|
acme.directory.google.production;
|
||||||
|
|
||||||
acme.directory.letsencrypt.staging;
|
acme.directory.letsencrypt.staging;
|
||||||
acme.directory.letsencrypt.production;
|
acme.directory.letsencrypt.production;
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
15:57
|
20:55
|
||||||
|
|||||||
@@ -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.21.2",
|
"version": "1.22.6",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"types": "types/index.d.ts",
|
"types": "types/index.d.ts",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@@ -59,5 +59,5 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||||
},
|
},
|
||||||
"gitHead": "a31f1c7f5e71fa946de9bf0283e11d6ce049b3e9"
|
"gitHead": "e5da46cfc31b2e30a4903bcb2251b1851265ef41"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class AcmeClient {
|
|||||||
max: this.opts.backoffMax,
|
max: this.opts.backoffMax,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.http = new HttpClient(this.opts.directoryUrl, this.opts.accountKey, this.opts.externalAccountBinding);
|
this.http = new HttpClient(this.opts.directoryUrl, this.opts.accountKey, this.opts.externalAccountBinding, this.opts.urlMapping);
|
||||||
this.api = new AcmeApi(this.http, this.opts.accountUrl);
|
this.api = new AcmeApi(this.http, this.opts.accountUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,10 +12,11 @@ const httpsProxy = process.env.HTTPS_PROXY || process.env.https_proxy;
|
|||||||
let httpsAgent = null;
|
let httpsAgent = null;
|
||||||
if (httpsProxy) {
|
if (httpsProxy) {
|
||||||
httpsAgent = new HttpsProxyAgent(httpsProxy);
|
httpsAgent = new HttpsProxyAgent(httpsProxy);
|
||||||
|
log(`use https_proxy:${httpsProxy}`);
|
||||||
}
|
}
|
||||||
const axios = axios1.create({
|
const axios = axios1.create({
|
||||||
proxy: false,
|
proxy: false,
|
||||||
httpsAgent
|
httpsAgent,
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,14 +31,18 @@ const axios = axios1.create({
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
class HttpClient {
|
class HttpClient {
|
||||||
constructor(directoryUrl, accountKey, externalAccountBinding = {}) {
|
constructor(directoryUrl, accountKey, externalAccountBinding = {}, urlMapping = {}) {
|
||||||
this.directoryUrl = directoryUrl;
|
this.directoryUrl = directoryUrl;
|
||||||
this.accountKey = accountKey;
|
this.accountKey = accountKey;
|
||||||
this.externalAccountBinding = externalAccountBinding;
|
this.externalAccountBinding = externalAccountBinding;
|
||||||
|
|
||||||
this.maxBadNonceRetries = 5;
|
this.maxBadNonceRetries = 5;
|
||||||
this.directory = null;
|
|
||||||
this.jwk = null;
|
this.jwk = null;
|
||||||
|
|
||||||
|
this.directoryCache = null;
|
||||||
|
this.directoryMaxAge = 86400;
|
||||||
|
this.directoryTimestamp = 0;
|
||||||
|
this.urlMapping = urlMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,6 +55,16 @@ class HttpClient {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
async request(url, method, opts = {}) {
|
async request(url, method, opts = {}) {
|
||||||
|
if (this.urlMapping && this.urlMapping.enabled === true && this.urlMapping.mappings) {
|
||||||
|
// eslint-disable-next-line no-restricted-syntax
|
||||||
|
for (const key in this.urlMapping.mappings) {
|
||||||
|
if (url.includes(key)) {
|
||||||
|
const newUrl = url.replace(key, this.urlMapping.mappings[key]);
|
||||||
|
log(`use reverse proxy: ${newUrl}`);
|
||||||
|
url = newUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
opts.url = url;
|
opts.url = url;
|
||||||
opts.method = method;
|
opts.method = method;
|
||||||
opts.validateStatus = null;
|
opts.validateStatus = null;
|
||||||
@@ -70,15 +85,17 @@ class HttpClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure provider directory exists
|
* Get ACME provider directory
|
||||||
*
|
*
|
||||||
* https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.1
|
* https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.1
|
||||||
*
|
*
|
||||||
* @returns {Promise}
|
* @returns {Promise<object>} ACME directory contents
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async getDirectory() {
|
async getDirectory() {
|
||||||
if (!this.directory) {
|
const age = (Math.floor(Date.now() / 1000) - this.directoryTimestamp);
|
||||||
|
|
||||||
|
if (!this.directoryCache || (age > this.directoryMaxAge)) {
|
||||||
const resp = await this.request(this.directoryUrl, 'get');
|
const resp = await this.request(this.directoryUrl, 'get');
|
||||||
|
|
||||||
if (resp.status >= 400) {
|
if (resp.status >= 400) {
|
||||||
@@ -89,8 +106,10 @@ class HttpClient {
|
|||||||
throw new Error('Attempting to read ACME directory returned no data');
|
throw new Error('Attempting to read ACME directory returned no data');
|
||||||
}
|
}
|
||||||
|
|
||||||
this.directory = resp.data;
|
this.directoryCache = resp.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this.directoryCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,13 +153,13 @@ class HttpClient {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
async getResourceUrl(resource) {
|
async getResourceUrl(resource) {
|
||||||
await this.getDirectory();
|
const dir = await this.getDirectory();
|
||||||
|
|
||||||
if (!this.directory[resource]) {
|
if (!dir[resource]) {
|
||||||
throw new Error(`Unable to locate API resource URL in ACME directory: "${resource}"`);
|
throw new Error(`Unable to locate API resource URL in ACME directory: "${resource}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.directory[resource];
|
return dir[resource];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -151,10 +170,10 @@ class HttpClient {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
async getMetaField(field) {
|
async getMetaField(field) {
|
||||||
await this.getDirectory();
|
const dir = await this.getDirectory();
|
||||||
|
|
||||||
if (('meta' in this.directory) && (field in this.directory.meta)) {
|
if (('meta' in dir) && (field in dir.meta)) {
|
||||||
return this.directory.meta[field];
|
return dir.meta[field];
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ exports.directory = {
|
|||||||
staging: 'https://api.test4.buypass.no/acme/directory',
|
staging: 'https://api.test4.buypass.no/acme/directory',
|
||||||
production: 'https://api.buypass.com/acme/directory',
|
production: 'https://api.buypass.com/acme/directory',
|
||||||
},
|
},
|
||||||
|
google: {
|
||||||
|
staging: 'https://dv.acme-v02.test-api.pki.goog/directory',
|
||||||
|
production: 'https://dv.acme-v02.api.pki.goog/directory',
|
||||||
|
},
|
||||||
letsencrypt: {
|
letsencrypt: {
|
||||||
staging: 'https://acme-staging-v02.api.letsencrypt.org/directory',
|
staging: 'https://acme-staging-v02.api.letsencrypt.org/directory',
|
||||||
production: 'https://acme-v02.api.letsencrypt.org/directory',
|
production: 'https://acme-v02.api.letsencrypt.org/directory',
|
||||||
|
|||||||
@@ -414,7 +414,7 @@ describe('client.auto', () => {
|
|||||||
const info = acme.crypto.readCertificateInfo(testCertificate);
|
const info = acme.crypto.readCertificateInfo(testCertificate);
|
||||||
|
|
||||||
spec.crypto.certificateInfo(info);
|
spec.crypto.certificateInfo(info);
|
||||||
assert.strictEqual(info.domains.commonName, testDomain);
|
assert.isNull(info.domains.commonName);
|
||||||
assert.deepStrictEqual(info.domains.altNames, [testDomain]);
|
assert.deepStrictEqual(info.domains.altNames, [testDomain]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -422,7 +422,7 @@ describe('client.auto', () => {
|
|||||||
const info = acme.crypto.readCertificateInfo(testSanCertificate);
|
const info = acme.crypto.readCertificateInfo(testSanCertificate);
|
||||||
|
|
||||||
spec.crypto.certificateInfo(info);
|
spec.crypto.certificateInfo(info);
|
||||||
assert.strictEqual(info.domains.commonName, testSanDomains[0]);
|
assert.isNull(info.domains.commonName);
|
||||||
assert.deepStrictEqual(info.domains.altNames, testSanDomains);
|
assert.deepStrictEqual(info.domains.altNames, testSanDomains);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -430,7 +430,7 @@ describe('client.auto', () => {
|
|||||||
const info = acme.crypto.readCertificateInfo(testWildcardCertificate);
|
const info = acme.crypto.readCertificateInfo(testWildcardCertificate);
|
||||||
|
|
||||||
spec.crypto.certificateInfo(info);
|
spec.crypto.certificateInfo(info);
|
||||||
assert.strictEqual(info.domains.commonName, testWildcardDomain);
|
assert.isNull(info.domains.commonName);
|
||||||
assert.deepStrictEqual(info.domains.altNames, [testWildcardDomain, `*.${testWildcardDomain}`]);
|
assert.deepStrictEqual(info.domains.altNames, [testWildcardDomain, `*.${testWildcardDomain}`]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
{
|
{
|
||||||
|
"compileOnSave": false,
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
"lib": ["es6"],
|
"lib": ["es6"],
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noEmit": true,
|
"noEmit": false,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"composite": true,
|
||||||
"paths": { "acme-client": ["."] }
|
"paths": { "acme-client": ["."] }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
packages/core/acme-client/types/index.d.ts
vendored
10
packages/core/acme-client/types/index.d.ts
vendored
@@ -27,6 +27,11 @@ export interface Authorization extends rfc8555.Authorization {
|
|||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type UrlMapping={
|
||||||
|
enabled: boolean
|
||||||
|
mappings: Record<string, string>
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client
|
* Client
|
||||||
*/
|
*/
|
||||||
@@ -39,6 +44,7 @@ export interface ClientOptions {
|
|||||||
backoffAttempts?: number;
|
backoffAttempts?: number;
|
||||||
backoffMin?: number;
|
backoffMin?: number;
|
||||||
backoffMax?: number;
|
backoffMax?: number;
|
||||||
|
urlMapping?: UrlMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClientExternalAccountBindingOptions {
|
export interface ClientExternalAccountBindingOptions {
|
||||||
@@ -87,6 +93,10 @@ export const directory: {
|
|||||||
staging: string,
|
staging: string,
|
||||||
production: string
|
production: string
|
||||||
},
|
},
|
||||||
|
google: {
|
||||||
|
staging: string,
|
||||||
|
production: string
|
||||||
|
},
|
||||||
letsencrypt: {
|
letsencrypt: {
|
||||||
staging: string,
|
staging: string,
|
||||||
production: string
|
production: string
|
||||||
|
|||||||
4
packages/core/pipeline/.gitignore
vendored
4
packages/core/pipeline/.gitignore
vendored
@@ -23,4 +23,6 @@ dist-ssr
|
|||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
test/user.secret.ts
|
test/user.secret.*
|
||||||
|
test/**/*.js
|
||||||
|
src/**/*.spec.ts
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"extension": ["ts"],
|
"extension": ["ts"],
|
||||||
"spec": "test/**/*.test.ts",
|
"spec": "src/**/*.spec.ts"
|
||||||
"require": "ts-node/register"
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
node_modules
|
node_modules
|
||||||
src
|
src
|
||||||
|
dist/**/*.spec.*
|
||||||
2
packages/core/pipeline/.npmrc
Normal file
2
packages/core/pipeline/.npmrc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
link-workspace-packages=true
|
||||||
|
prefer-workspace-packages=true
|
||||||
@@ -3,6 +3,53 @@
|
|||||||
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.22.8](https://github.com/certd/certd/compare/v1.22.7...v1.22.8) (2024-08-05)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 优化pipeline删除时,删除其他history ([b425203](https://github.com/certd/certd/commit/b4252033d56a9ad950f3e204ff021497c3978015))
|
||||||
|
|
||||||
|
## [1.22.7](https://github.com/certd/certd/compare/v1.22.6...v1.22.7) (2024-08-04)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|
||||||
|
## [1.22.6](https://github.com/certd/certd/compare/v1.22.5...v1.22.6) (2024-08-03)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复在相同的cron时偶尔无法触发定时任务的bug ([680941a](https://github.com/certd/certd/commit/680941af119619006b592e3ab6fb112cb5556a8b))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 流水线支持名称模糊查询 ([59897c4](https://github.com/certd/certd/commit/59897c4ceae992ebe2972ca9e8f9196616ffdfd7))
|
||||||
|
* 优化前置任务输出为空的提示 ([6ed1e18](https://github.com/certd/certd/commit/6ed1e18c7d9c46d964ecc6abc90f3908297b7632))
|
||||||
|
|
||||||
|
## [1.22.5](https://github.com/certd/certd/compare/v1.22.4...v1.22.5) (2024-07-26)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|
||||||
|
## [1.22.3](https://github.com/certd/certd/compare/v1.22.2...v1.22.3) (2024-07-25)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|
||||||
|
## [1.22.2](https://github.com/certd/certd/compare/v1.22.1...v1.22.2) (2024-07-23)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|
||||||
|
## [1.22.1](https://github.com/certd/certd/compare/v1.22.0...v1.22.1) (2024-07-20)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 创建证书任务可以选择lege插件 ([affef13](https://github.com/certd/certd/commit/affef130378030c517250c58a4e787b0fc85d7d1))
|
||||||
|
|
||||||
|
# [1.22.0](https://github.com/certd/certd/compare/v1.21.2...v1.22.0) (2024-07-19)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* 升级midway,支持esm ([485e603](https://github.com/certd/certd/commit/485e603b5165c28bc08694997726eaf2a585ebe7))
|
||||||
|
* 支持lego,海量DNS提供商 ([0bc6d0a](https://github.com/certd/certd/commit/0bc6d0a211920fb0084d705e1db67ee1e7262c44))
|
||||||
|
* 支持postgresql ([3b19bfb](https://github.com/certd/certd/commit/3b19bfb4291e89064b3b407a80dae092d54747d5))
|
||||||
|
|
||||||
## [1.21.2](https://github.com/certd/certd/compare/v1.21.1...v1.21.2) (2024-07-08)
|
## [1.21.2](https://github.com/certd/certd/compare/v1.21.1...v1.21.2) (2024-07-08)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/pipeline
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|||||||
1
packages/core/pipeline/build.md
Normal file
1
packages/core/pipeline/build.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
22:30
|
||||||
96
packages/core/pipeline/fix-esm-import-paths.js
Normal file
96
packages/core/pipeline/fix-esm-import-paths.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
|
||||||
|
// https://gist.github.com/lovasoa/8691344
|
||||||
|
async function* walk(dir) {
|
||||||
|
for await (const d of await fs.promises.opendir(dir)) {
|
||||||
|
const entry = path.join(dir, d.name);
|
||||||
|
if (d.isDirectory()) {
|
||||||
|
yield* walk(entry);
|
||||||
|
} else if (d.isFile()) {
|
||||||
|
yield entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveImportPath(sourceFile, importPath, options) {
|
||||||
|
const sourceFileAbs = path.resolve(process.cwd(), sourceFile);
|
||||||
|
const root = path.dirname(sourceFileAbs);
|
||||||
|
const { moduleFilter = defaultModuleFilter } = options;
|
||||||
|
|
||||||
|
if (moduleFilter(importPath)) {
|
||||||
|
const importPathAbs = path.resolve(root, importPath);
|
||||||
|
let possiblePath = [path.resolve(importPathAbs, "./index.ts"), path.resolve(importPathAbs, "./index.js"), importPathAbs + ".ts", importPathAbs + ".js"];
|
||||||
|
|
||||||
|
if (possiblePath.length) {
|
||||||
|
for (let i = 0; i < possiblePath.length; i++) {
|
||||||
|
let entry = possiblePath[i];
|
||||||
|
if (fs.existsSync(entry)) {
|
||||||
|
const resolved = path.relative(root, entry.replace(/\.ts$/, ".js"));
|
||||||
|
|
||||||
|
if (!resolved.startsWith(".")) {
|
||||||
|
return "./" + resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function replace(filePath, outFilePath, options) {
|
||||||
|
const code = fs.readFileSync(filePath).toString();
|
||||||
|
const newCode = code.replace(/(import|export) (.+?) from ('[^\n']+'|"[^\n"]+");/gs, function (found, action, imported, from) {
|
||||||
|
const importPath = from.slice(1, -1);
|
||||||
|
let resolvedPath = resolveImportPath(filePath, importPath, options);
|
||||||
|
|
||||||
|
if (resolvedPath) {
|
||||||
|
resolvedPath = resolvedPath.replaceAll("\\", "/");
|
||||||
|
console.log("\t", importPath, resolvedPath);
|
||||||
|
return `${action} ${imported} from "${resolvedPath}";`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (code !== newCode) {
|
||||||
|
fs.writeFileSync(outFilePath, newCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, use it with a simple async for loop
|
||||||
|
async function run(srcDir, options = defaultOptions) {
|
||||||
|
const { sourceFileFilter = defaultSourceFileFilter } = options;
|
||||||
|
|
||||||
|
for await (const entry of walk(srcDir)) {
|
||||||
|
if (sourceFileFilter(entry)) {
|
||||||
|
console.log(entry);
|
||||||
|
replace(entry, entry, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultSourceFileFilter = function (sourceFilePath) {
|
||||||
|
return /\.(js|ts)$/.test(sourceFilePath) && !/node_modules/.test(sourceFilePath);
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultModuleFilter = function (importedModule) {
|
||||||
|
return !path.isAbsolute(importedModule) && !importedModule.startsWith("@") && !importedModule.endsWith(".js");
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
sourceFileFilter: defaultSourceFileFilter,
|
||||||
|
moduleFilter: defaultModuleFilter,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Switch this to test on one file or directly run on a directory.
|
||||||
|
const DEBUG = false;
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
replace("./src/index.ts", "./out.ts", defaultOptions);
|
||||||
|
} else {
|
||||||
|
await run("./src/", defaultOptions);
|
||||||
|
}
|
||||||
@@ -1,49 +1,47 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/pipeline",
|
"name": "@certd/pipeline",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.21.2",
|
"version": "1.22.8",
|
||||||
"main": "./src/index.ts",
|
"type": "module",
|
||||||
"module": "./src/index.ts",
|
"main": "./dist/index.js",
|
||||||
"types": "./src/index.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"publishConfig": {
|
|
||||||
"main": "./dist/bundle.js",
|
|
||||||
"module": "./dist/bundle.mjs",
|
|
||||||
"types": "./dist/d/index.d.ts"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "rollup -c",
|
"build": "tsc --skipLibCheck",
|
||||||
|
"build3": "rollup -c",
|
||||||
"build2": "vue-tsc --noEmit && vite build",
|
"build2": "vue-tsc --noEmit && vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"test": "mocha --loader=ts-node/esm"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.7.2",
|
||||||
|
"fix-path": "^4.0.0",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
"node-forge": "^1.3.1",
|
"node-forge": "^1.3.1",
|
||||||
"nodemailer": "^6.9.3",
|
"nodemailer": "^6.9.3",
|
||||||
"qs": "^6.11.2"
|
"qs": "^6.11.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@certd/acme-client": "workspace:^1.21.2",
|
|
||||||
"@rollup/plugin-commonjs": "^23.0.4",
|
"@rollup/plugin-commonjs": "^23.0.4",
|
||||||
"@rollup/plugin-json": "^6.0.0",
|
"@rollup/plugin-json": "^6.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
"@rollup/plugin-terser": "^0.4.3",
|
"@rollup/plugin-terser": "^0.4.3",
|
||||||
"@rollup/plugin-typescript": "^11.0.0",
|
"@rollup/plugin-typescript": "^11.0.0",
|
||||||
"@types/chai": "^4.3.5",
|
"@types/chai": "^4.3.10",
|
||||||
"@types/lodash": "^4.14.194",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/mocha": "^10.0.1",
|
"@types/mocha": "^10.0.1",
|
||||||
"@types/node-forge": "^1.3.2",
|
"@types/node-forge": "^1.3.2",
|
||||||
"@types/uuid": "^9.0.2",
|
"@types/uuid": "^9.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
"@typescript-eslint/eslint-plugin": "^5.59.7",
|
||||||
"@typescript-eslint/parser": "^5.59.7",
|
"@typescript-eslint/parser": "^5.59.7",
|
||||||
"chai": "^4.3.7",
|
"chai": "4.3.10",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"eslint": "^8.41.0",
|
"eslint": "^8.41.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^8.8.0",
|
||||||
"eslint-plugin-import": "^2.27.5",
|
"eslint-plugin-import": "^2.27.5",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
"lodash": "^4.17.21",
|
"iconv-lite": "^0.6.3",
|
||||||
"log4js": "^6.9.1",
|
"log4js": "^6.9.1",
|
||||||
"mocha": "^10.2.0",
|
"mocha": "^10.2.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
@@ -52,10 +50,11 @@
|
|||||||
"rollup-plugin-typescript2": "^0.34.1",
|
"rollup-plugin-typescript2": "^0.34.1",
|
||||||
"rollup-plugin-visualizer": "^5.8.2",
|
"rollup-plugin-visualizer": "^5.8.2",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
|
"tsc-esm-fix": "^3.0.0",
|
||||||
"tslib": "^2.5.2",
|
"tslib": "^2.5.2",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vite": "^4.3.8",
|
"vite": "^4.3.8",
|
||||||
"vue-tsc": "^1.6.5"
|
"vue-tsc": "^1.6.5"
|
||||||
},
|
},
|
||||||
"gitHead": "a31f1c7f5e71fa946de9bf0283e11d6ce049b3e9"
|
"gitHead": "e5da46cfc31b2e30a4903bcb2251b1851265ef41"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
const resolve = require("@rollup/plugin-node-resolve");
|
|
||||||
const commonjs = require("@rollup/plugin-commonjs");
|
|
||||||
//const Typescript = require("rollup-plugin-typescript2");
|
|
||||||
const Typescript = require("@rollup/plugin-typescript");
|
|
||||||
const json = require("@rollup/plugin-json");
|
|
||||||
const terser = require("@rollup/plugin-terser");
|
|
||||||
module.exports = {
|
|
||||||
input: "src/index.ts",
|
|
||||||
output: {
|
|
||||||
file: "dist/bundle.js",
|
|
||||||
format: "cjs",
|
|
||||||
},
|
|
||||||
plugins: [
|
|
||||||
// 解析第三方依赖
|
|
||||||
resolve(),
|
|
||||||
// 识别 commonjs 模式第三方依赖
|
|
||||||
commonjs(),
|
|
||||||
Typescript({
|
|
||||||
target: "esnext",
|
|
||||||
rootDir: "src",
|
|
||||||
declaration: true,
|
|
||||||
declarationDir: "dist/d",
|
|
||||||
exclude: ["./node_modules/**", "./src/**/*.vue"],
|
|
||||||
allowSyntheticDefaultImports: true,
|
|
||||||
}),
|
|
||||||
json(),
|
|
||||||
terser(),
|
|
||||||
],
|
|
||||||
external: [
|
|
||||||
"vue",
|
|
||||||
"lodash",
|
|
||||||
"dayjs",
|
|
||||||
"@certd/acme-client",
|
|
||||||
"@certd/pipeline",
|
|
||||||
"@certd/plugin-cert",
|
|
||||||
"@certd/plugin-aliyun",
|
|
||||||
"@certd/plugin-tencent",
|
|
||||||
"@certd/plugin-huawei",
|
|
||||||
"@certd/plugin-host",
|
|
||||||
"@certd/plugin-tencent",
|
|
||||||
"@certd/plugin-util",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Registrable } from "../registry";
|
import { Registrable } from "../registry/index.js";
|
||||||
import { FormItemProps } from "../d.ts";
|
import { FormItemProps } from "../dt/index.js";
|
||||||
|
|
||||||
export type AccessInputDefine = FormItemProps & {
|
export type AccessInputDefine = FormItemProps & {
|
||||||
title: string;
|
title: string;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// src/decorator/memoryCache.decorator.ts
|
// src/decorator/memoryCache.decorator.ts
|
||||||
import { AccessDefine, AccessInputDefine } from "./api";
|
import { AccessDefine, AccessInputDefine } from "./api.js";
|
||||||
import { Decorator } from "../decorator";
|
import { Decorator } from "../decorator/index.js";
|
||||||
import _ from "lodash";
|
import _ from "lodash-es";
|
||||||
import { accessRegistry } from "./registry";
|
import { accessRegistry } from "./registry.js";
|
||||||
|
|
||||||
// 提供一个唯一 key
|
// 提供一个唯一 key
|
||||||
export const ACCESS_CLASS_KEY = "pipeline:access";
|
export const ACCESS_CLASS_KEY = "pipeline:access";
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
export * from "./api";
|
export * from "./api.js";
|
||||||
export * from "./registry";
|
export * from "./registry.js";
|
||||||
export * from "./decorator";
|
export * from "./decorator.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Registry } from "../registry";
|
import { Registry } from "../registry/index.js";
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
export const accessRegistry = new Registry("access");
|
export const accessRegistry = new Registry("access");
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { AxiosInstance } from "axios";
|
import { AxiosInstance } from "axios";
|
||||||
import { IContext } from "../core";
|
import { IContext } from "../core/index.js";
|
||||||
|
|
||||||
export type HttpClient = AxiosInstance;
|
export type HttpClient = AxiosInstance;
|
||||||
export type UserContext = IContext;
|
export type UserContext = IContext;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { IStorage, MemoryStorage } from "./storage";
|
import { IStorage, MemoryStorage } from "./storage.js";
|
||||||
|
|
||||||
const CONTEXT_VERSION_KEY = "contextVersion";
|
const CONTEXT_VERSION_KEY = "contextVersion";
|
||||||
export interface IContext {
|
export interface IContext {
|
||||||
getInt(key: string): Promise<number>;
|
getInt(key: string): Promise<number>;
|
||||||
@@ -20,13 +21,11 @@ export class ContextFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getContext(scope: string, namespace: string): IContext {
|
getContext(scope: string, namespace: string): IContext {
|
||||||
const context = new StorageContext(scope, namespace, this.storage);
|
return new StorageContext(scope, namespace, this.storage);
|
||||||
return context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getMemoryContext(scope: string, namespace: string): IContext {
|
getMemoryContext(scope: string, namespace: string): IContext {
|
||||||
const context = new StorageContext(scope, namespace, this.memoryStorage);
|
return new StorageContext(scope, namespace, this.memoryStorage);
|
||||||
return context;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { ConcurrencyStrategy, NotificationWhen, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
|
import { ConcurrencyStrategy, NotificationWhen, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../dt/index.js";
|
||||||
import _ from "lodash";
|
import _ from "lodash-es";
|
||||||
import { RunHistory, RunnableCollection } from "./run-history";
|
import { RunHistory, RunnableCollection } from "./run-history.js";
|
||||||
import { AbstractTaskPlugin, PluginDefine, pluginRegistry, TaskInstanceContext } from "../plugin";
|
import { AbstractTaskPlugin, PluginDefine, pluginRegistry, TaskInstanceContext } from "../plugin/index.js";
|
||||||
import { ContextFactory, IContext } from "./context";
|
import { ContextFactory, IContext } from "./context.js";
|
||||||
import { IStorage } from "./storage";
|
import { IStorage } from "./storage.js";
|
||||||
import { logger } from "../utils/util.log";
|
import { logger } from "../utils/util.log.js";
|
||||||
import { Logger } from "log4js";
|
import { Logger } from "log4js";
|
||||||
import { createAxiosService } from "../utils/util.request";
|
import { createAxiosService } from "../utils/util.request.js";
|
||||||
import { IAccessService } from "../access";
|
import { IAccessService } from "../access/index.js";
|
||||||
import { RegistryItem } from "../registry";
|
import { RegistryItem } from "../registry/index.js";
|
||||||
import { Decorator } from "../decorator";
|
import { Decorator } from "../decorator/index.js";
|
||||||
import { IEmailService } from "../service";
|
import { IEmailService } from "../service/index.js";
|
||||||
import { FileStore } from "./file-store";
|
import { FileStore } from "./file-store.js";
|
||||||
import { TimeoutPromise } from "../utils/util.promise";
|
// import { TimeoutPromise } from "../utils/util.promise.js";
|
||||||
|
|
||||||
export type ExecutorOptions = {
|
export type ExecutorOptions = {
|
||||||
userId: any;
|
userId: any;
|
||||||
@@ -33,7 +33,7 @@ export class Executor {
|
|||||||
lastRuntime!: RunHistory;
|
lastRuntime!: RunHistory;
|
||||||
options: ExecutorOptions;
|
options: ExecutorOptions;
|
||||||
canceled = false;
|
canceled = false;
|
||||||
onChanged: (history: RunHistory) => void;
|
onChanged: (history: RunHistory) => Promise<void>;
|
||||||
constructor(options: ExecutorOptions) {
|
constructor(options: ExecutorOptions) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.pipeline = _.cloneDeep(options.pipeline);
|
this.pipeline = _.cloneDeep(options.pipeline);
|
||||||
@@ -110,12 +110,13 @@ export class Executor {
|
|||||||
const intervalFlushLogId = setInterval(async () => {
|
const intervalFlushLogId = setInterval(async () => {
|
||||||
await this.onChanged(this.runtime);
|
await this.onChanged(this.runtime);
|
||||||
}, 5000);
|
}, 5000);
|
||||||
const timeout = runnable.timeout ?? 20 * 60 * 1000;
|
|
||||||
|
// const timeout = runnable.timeout ?? 20 * 60 * 1000;
|
||||||
try {
|
try {
|
||||||
if (this.canceled) {
|
if (this.canceled) {
|
||||||
throw new Error("task canceled");
|
return ResultType.canceled;
|
||||||
}
|
}
|
||||||
await TimeoutPromise(run, timeout);
|
await run();
|
||||||
this.runtime.success(runnable);
|
this.runtime.success(runnable);
|
||||||
return ResultType.success;
|
return ResultType.success;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@@ -142,19 +143,25 @@ export class Executor {
|
|||||||
async runStage(stage: Stage) {
|
async runStage(stage: Stage) {
|
||||||
const runnerList = [];
|
const runnerList = [];
|
||||||
for (const task of stage.tasks) {
|
for (const task of stage.tasks) {
|
||||||
const runner = this.runWithHistory(task, "task", async () => {
|
const runner = async () => {
|
||||||
await this.runTask(task);
|
return this.runWithHistory(task, "task", async () => {
|
||||||
});
|
await this.runTask(task);
|
||||||
|
});
|
||||||
|
};
|
||||||
runnerList.push(runner);
|
runnerList.push(runner);
|
||||||
}
|
}
|
||||||
|
|
||||||
let resList: ResultType[] = [];
|
let resList: ResultType[] = [];
|
||||||
if (stage.concurrency === ConcurrencyStrategy.Parallel) {
|
if (stage.concurrency === ConcurrencyStrategy.Parallel) {
|
||||||
resList = await Promise.all(runnerList);
|
const pList = [];
|
||||||
|
for (const item of runnerList) {
|
||||||
|
pList.push(item());
|
||||||
|
}
|
||||||
|
resList = await Promise.all(pList);
|
||||||
} else {
|
} else {
|
||||||
for (let i = 0; i < runnerList.length; i++) {
|
for (let i = 0; i < runnerList.length; i++) {
|
||||||
const runner = runnerList[i];
|
const runner = runnerList[i];
|
||||||
resList[i] = await runner;
|
resList[i] = await runner();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this.compositionResultType(resList);
|
return this.compositionResultType(resList);
|
||||||
@@ -206,7 +213,12 @@ export class Executor {
|
|||||||
if (contextKey != null) {
|
if (contextKey != null) {
|
||||||
const value = this.runtime.context[contextKey];
|
const value = this.runtime.context[contextKey];
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
currentLogger.warn(`[step init] input ${define.title} is null`);
|
currentLogger.warn(`[step init] input ${define.title} is null,前置任务步骤输出值为空,请按如下步骤排查:`);
|
||||||
|
currentLogger.log(`1、检查前置任务(证书申请任务)是否是配置了成功后跳过,如果是,请改为正常执行`);
|
||||||
|
currentLogger.log(
|
||||||
|
`2、是否曾经删除过前置任务(证书申请任务),然后又重新添加了,如果是,请重新编辑当前任务,重新选择一下前置任务输出的参数(域名证书那一栏)`
|
||||||
|
);
|
||||||
|
currentLogger.log(`3、以上都不是,联系作者吧`);
|
||||||
}
|
}
|
||||||
step.input[key] = value;
|
step.input[key] = value;
|
||||||
}
|
}
|
||||||
@@ -234,18 +246,26 @@ export class Executor {
|
|||||||
|
|
||||||
await instance.onInstance();
|
await instance.onInstance();
|
||||||
await instance.execute();
|
await instance.execute();
|
||||||
|
//执行结果处理
|
||||||
if (instance._result.clearLastStatus) {
|
if (instance._result.clearLastStatus) {
|
||||||
|
//是否需要清除所有状态
|
||||||
this.lastStatusMap.clear();
|
this.lastStatusMap.clear();
|
||||||
}
|
}
|
||||||
//输出到output context
|
//输出上下文变量到output context
|
||||||
_.forEach(define.output, (item, key) => {
|
_.forEach(define.output, (item: any, key: any) => {
|
||||||
step.status!.output[key] = instance[key];
|
step.status!.output[key] = instance[key];
|
||||||
const stepOutputKey = `step.${step.id}.${key}`;
|
const stepOutputKey = `step.${step.id}.${key}`;
|
||||||
this.runtime.context[stepOutputKey] = instance[key];
|
this.runtime.context[stepOutputKey] = instance[key];
|
||||||
});
|
});
|
||||||
|
|
||||||
step.status!.files = instance.getFiles();
|
step.status!.files = instance.getFiles();
|
||||||
|
|
||||||
|
//更新pipeline vars
|
||||||
|
if (Object.keys(instance._result.pipelineVars).length > 0) {
|
||||||
|
// 判断 pipelineVars 有值时更新
|
||||||
|
const vars = this.pipelineContext.getObj("vars");
|
||||||
|
_.merge(vars, instance._result.pipelineVars);
|
||||||
|
await this.pipelineContext.setObj("vars", vars);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async notification(when: NotificationWhen, error?: any) {
|
async notification(when: NotificationWhen, error?: any) {
|
||||||
@@ -275,12 +295,16 @@ export class Executor {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (notification.type === "email") {
|
if (notification.type === "email") {
|
||||||
this.options.emailService?.send({
|
try {
|
||||||
userId: this.pipeline.userId,
|
await this.options.emailService?.send({
|
||||||
subject,
|
userId: this.pipeline.userId,
|
||||||
content,
|
subject,
|
||||||
receivers: notification.options.receivers,
|
content,
|
||||||
});
|
receivers: notification.options.receivers,
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("send email error", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { fileUtils } from "../utils";
|
import { fileUtils } from "../utils/index.js";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { logger } from "../utils";
|
import { logger } from "../utils/index.js";
|
||||||
|
|
||||||
export type FileStoreOptions = {
|
export type FileStoreOptions = {
|
||||||
rootDir?: string;
|
rootDir?: string;
|
||||||
@@ -10,9 +10,17 @@ export type FileStoreOptions = {
|
|||||||
parent: string;
|
parent: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface IFileStore {
|
||||||
|
readFile(filePath: string): Buffer | null;
|
||||||
|
writeFile(filename: string, file: Buffer): string;
|
||||||
|
deleteByParent(scope: string, parent: string): void;
|
||||||
|
}
|
||||||
|
|
||||||
export class FileStore {
|
export class FileStore {
|
||||||
rootDir: string;
|
rootDir: string;
|
||||||
|
// pipelineId
|
||||||
scope: string;
|
scope: string;
|
||||||
|
// historyId
|
||||||
parent: string;
|
parent: string;
|
||||||
constructor(options?: FileStoreOptions) {
|
constructor(options?: FileStoreOptions) {
|
||||||
this.rootDir = fileUtils.getFileRootDir(options?.rootDir);
|
this.rootDir = fileUtils.getFileRootDir(options?.rootDir);
|
||||||
@@ -35,7 +43,7 @@ export class FileStore {
|
|||||||
return localPath;
|
return localPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildFilePath(filename: string) {
|
protected buildFilePath(filename: string) {
|
||||||
const parentDir = path.join(this.rootDir, this.scope + "", this.parent + "", dayjs().format("YYYY-MM-DD"));
|
const parentDir = path.join(this.rootDir, this.scope + "", this.parent + "", dayjs().format("YYYY-MM-DD"));
|
||||||
if (!fs.existsSync(parentDir)) {
|
if (!fs.existsSync(parentDir)) {
|
||||||
fs.mkdirSync(parentDir, { recursive: true });
|
fs.mkdirSync(parentDir, { recursive: true });
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export * from "./executor";
|
export * from "./executor.js";
|
||||||
export * from "./run-history";
|
export * from "./run-history.js";
|
||||||
export * from "./context";
|
export * from "./context.js";
|
||||||
export * from "./storage";
|
export * from "./storage.js";
|
||||||
export * from "./file-store";
|
export * from "./file-store.js";
|
||||||
|
export * from "./license.js";
|
||||||
|
|||||||
15
packages/core/pipeline/src/core/license.spec.ts
Normal file
15
packages/core/pipeline/src/core/license.spec.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { isPlus, verify } from "./license.js";
|
||||||
|
import { equal } from "assert";
|
||||||
|
describe("license", function () {
|
||||||
|
it("#license", async function () {
|
||||||
|
const req = {
|
||||||
|
appKey: "z4nXOeTeSnnpUpnmsV",
|
||||||
|
subjectId: "999",
|
||||||
|
license: "",
|
||||||
|
};
|
||||||
|
const plus = isPlus();
|
||||||
|
equal(plus, false);
|
||||||
|
const res = await verify(req);
|
||||||
|
equal(res, true);
|
||||||
|
});
|
||||||
|
});
|
||||||
85
packages/core/pipeline/src/core/license.ts
Normal file
85
packages/core/pipeline/src/core/license.ts
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
import { createVerify } from "node:crypto";
|
||||||
|
import { logger } from "../utils/index.js";
|
||||||
|
|
||||||
|
const SecreteKey =
|
||||||
|
"LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJDZ0tDQVFFQXY3TGtMaUp1dGM0NzhTU3RaTExjajVGZXh1YjJwR2NLMGxwa0hwVnlZWjhMY29rRFhuUlAKUGQ5UlJSTVRTaGJsbFl2Mzd4QUhOV1ZIQ0ZsWHkrQklVU001bUlBU1NDQTV0azlJNmpZZ2F4bEFDQm1BY0lGMwozKzBjeGZIYVkrVW9YdVluMkZ6YUt2Ym5GdFZIZ0lkMDg4a3d4clZTZzlCT3BDRVZIR1pxR2I5TWN5MXVHVXhUClFTVENCbmpoTWZlZ0p6cXVPYWVOY0ZPSE5tbmtWRWpLTythbTBPeEhNS1lyS3ZnQnVEbzdoVnFENlBFMUd6V3AKZHdwZUV4QXZDSVJxL2pWTkdRK3FtMkRWOVNJZ3U5bmF4MktmSUtFeU50dUFFS1VpekdqL0VmRFhDM1cxMExhegpKaGNYNGw1SUFZU1o3L3JWVmpGbExWSVl0WDU1T054L1Z3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K";
|
||||||
|
const appKey = "z4nXOeTeSnnpUpnmsV";
|
||||||
|
export type LicenseVerifyReq = {
|
||||||
|
subjectId: string;
|
||||||
|
license: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type License = {
|
||||||
|
appKey: string;
|
||||||
|
code: string;
|
||||||
|
subjectId: string;
|
||||||
|
expireTime: number;
|
||||||
|
activeTime: number;
|
||||||
|
duration: number;
|
||||||
|
version: number;
|
||||||
|
secret: string;
|
||||||
|
signature: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LicenseHolder {
|
||||||
|
isPlus = false;
|
||||||
|
}
|
||||||
|
const holder = new LicenseHolder();
|
||||||
|
holder.isPlus = false;
|
||||||
|
|
||||||
|
class LicenseVerifier {
|
||||||
|
checked = false;
|
||||||
|
licenseReq?: LicenseVerifyReq = undefined;
|
||||||
|
async reVerify(req: LicenseVerifyReq) {
|
||||||
|
this.checked = false;
|
||||||
|
return await this.verify(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
setPlus(value: boolean) {
|
||||||
|
holder.isPlus = value;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
async verify(req: LicenseVerifyReq) {
|
||||||
|
this.licenseReq = req;
|
||||||
|
if (this.checked) {
|
||||||
|
return this.setPlus(false);
|
||||||
|
}
|
||||||
|
const license = req?.license;
|
||||||
|
if (!license) {
|
||||||
|
this.checked = true;
|
||||||
|
return this.setPlus(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const licenseJson = Buffer.from(Buffer.from(license, "hex").toString(), "base64").toString();
|
||||||
|
const json: License = JSON.parse(licenseJson);
|
||||||
|
if (json.expireTime < Date.now()) {
|
||||||
|
logger.warn("授权已过期");
|
||||||
|
return this.setPlus(false);
|
||||||
|
}
|
||||||
|
const content = `${appKey},${this.licenseReq.subjectId},${json.code},${json.secret},${json.activeTime},${json.duration},${json.expireTime},${json.version}`;
|
||||||
|
const publicKey = Buffer.from(SecreteKey, "base64").toString();
|
||||||
|
const res = this.verifySignature(content, json.signature, publicKey);
|
||||||
|
this.checked = true;
|
||||||
|
if (!res) {
|
||||||
|
logger.warn("授权校验失败");
|
||||||
|
return this.setPlus(false);
|
||||||
|
}
|
||||||
|
return this.setPlus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
verifySignature(content: string, signature: any, publicKey: string) {
|
||||||
|
const verify = createVerify("RSA-SHA256");
|
||||||
|
verify.update(content);
|
||||||
|
return verify.verify(publicKey, signature, "base64");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifier = new LicenseVerifier();
|
||||||
|
|
||||||
|
export function isPlus() {
|
||||||
|
return holder.isPlus;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function verify(req: LicenseVerifyReq) {
|
||||||
|
return await verifier.reVerify(req);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Context, HistoryResult, Pipeline, ResultType, Runnable, RunnableMap, Stage, Step, Task } from "../d.ts";
|
import { Context, HistoryResult, Pipeline, ResultType, Runnable, RunnableMap, Stage, Step, Task } from "../dt/index.js";
|
||||||
import _ from "lodash";
|
import _ from "lodash-es";
|
||||||
import { buildLogger } from "../utils/util.log";
|
import { buildLogger } from "../utils/util.log.js";
|
||||||
import { Logger } from "log4js";
|
import { Logger } from "log4js";
|
||||||
|
|
||||||
export type HistoryStatus = {
|
export type HistoryStatus = {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { fileUtils } from "../utils/util.file";
|
import { fileUtils } from "../utils/util.file.js";
|
||||||
|
|
||||||
export interface IStorage {
|
export interface IStorage {
|
||||||
get(scope: string, namespace: string, version: string, key: string): Promise<string | null>;
|
get(scope: string, namespace: string, version: string, key: string): Promise<string | null>;
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ export type Runnable = {
|
|||||||
default?: {
|
default?: {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
context?: Context;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EmailOptions = {
|
export type EmailOptions = {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Decorator } from "./index";
|
import { Decorator } from "./index.js";
|
||||||
|
|
||||||
export type AutowireProp = {
|
export type AutowireProp = {
|
||||||
name?: string;
|
name?: string;
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from "./utils";
|
export * from "./utils.js";
|
||||||
export * from "./common";
|
export * from "./common.js";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash-es";
|
||||||
|
|
||||||
const propertyMap: any = {};
|
const propertyMap: any = {};
|
||||||
function attachProperty(target: any, propertyKey: string | symbol) {
|
function attachProperty(target: any, propertyKey: string | symbol) {
|
||||||
@@ -11,7 +11,11 @@ function attachProperty(target: any, propertyKey: string | symbol) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getClassProperties(target: any) {
|
function getClassProperties(target: any) {
|
||||||
return propertyMap[target] || {};
|
//获取父类
|
||||||
|
const parent = Object.getPrototypeOf(target);
|
||||||
|
const parentMap = propertyMap[parent] || {};
|
||||||
|
const current = propertyMap[target] || {};
|
||||||
|
return _.merge({}, parentMap, current);
|
||||||
}
|
}
|
||||||
|
|
||||||
function target(target: any, propertyKey?: string | symbol) {
|
function target(target: any, propertyKey?: string | symbol) {
|
||||||
@@ -25,7 +29,7 @@ function target(target: any, propertyKey?: string | symbol) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function inject(define: any, instance: any, context: any, preHandler?: (item: any, key: string, instance: any, context: any) => void) {
|
function inject(define: any, instance: any, context: any, preHandler?: (item: any, key: string, instance: any, context: any) => void) {
|
||||||
_.forEach(define, (item, key) => {
|
_.forEach(define, (item: any, key: any) => {
|
||||||
if (preHandler) {
|
if (preHandler) {
|
||||||
preHandler(item, key, instance, context);
|
preHandler(item, key, instance, context);
|
||||||
}
|
}
|
||||||
|
|||||||
115
packages/core/pipeline/src/dt/fast-crud.ts
Normal file
115
packages/core/pipeline/src/dt/fast-crud.ts
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* [x]-col的配置
|
||||||
|
*/
|
||||||
|
export type ColProps = {
|
||||||
|
span?: number;
|
||||||
|
[props: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FormItemProps = {
|
||||||
|
/**
|
||||||
|
* 字段label
|
||||||
|
*/
|
||||||
|
title?: string;
|
||||||
|
/**
|
||||||
|
* 表单字段组件配置
|
||||||
|
*/
|
||||||
|
component?: ComponentProps;
|
||||||
|
/**
|
||||||
|
* 表单字段 [a|el|n]-col的配置
|
||||||
|
* 一般用来配置跨列:{span:24} 占满一行
|
||||||
|
*/
|
||||||
|
col?: ColProps;
|
||||||
|
/**
|
||||||
|
* 默认值
|
||||||
|
*/
|
||||||
|
value?: any;
|
||||||
|
/**
|
||||||
|
* 帮助提示配置
|
||||||
|
*/
|
||||||
|
helper?: string | FormItemHelperProps;
|
||||||
|
/**
|
||||||
|
* 排序号
|
||||||
|
*/
|
||||||
|
order?: number;
|
||||||
|
/**
|
||||||
|
* 是否显示此字段
|
||||||
|
*/
|
||||||
|
show?: boolean;
|
||||||
|
/**
|
||||||
|
* 是否是空白占位栏
|
||||||
|
*/
|
||||||
|
blank?: boolean;
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单字段帮助说明配置
|
||||||
|
*/
|
||||||
|
export type FormItemHelperProps = {
|
||||||
|
/**
|
||||||
|
* 自定义渲染帮助说明
|
||||||
|
* @param scope
|
||||||
|
*/
|
||||||
|
render?: (scope: any) => any;
|
||||||
|
/**
|
||||||
|
* 帮助文本
|
||||||
|
*/
|
||||||
|
text?: string;
|
||||||
|
/**
|
||||||
|
* 帮助说明所在的位置,[ undefined | label]
|
||||||
|
*/
|
||||||
|
position?: string;
|
||||||
|
/**
|
||||||
|
* [a|el|n]-tooltip配置
|
||||||
|
*/
|
||||||
|
tooltip?: object;
|
||||||
|
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件配置
|
||||||
|
*/
|
||||||
|
export type ComponentProps = {
|
||||||
|
/**
|
||||||
|
* 组件的名称
|
||||||
|
*/
|
||||||
|
name?: string | object;
|
||||||
|
/**
|
||||||
|
* vmodel绑定的目标属性名
|
||||||
|
*/
|
||||||
|
vModel?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当原始组件名的参数被以上属性名占用时,可以配置在这里
|
||||||
|
* 例如:原始组件有一个叫name的属性,你想要配置它,则可以按如下配置
|
||||||
|
* ```
|
||||||
|
* component:{
|
||||||
|
* name:"组件的名称"
|
||||||
|
* props:{
|
||||||
|
* name:"组件的name属性" <-----------
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
props?: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件事件监听
|
||||||
|
*/
|
||||||
|
on?: {
|
||||||
|
[key: string]: (context?: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 组件其他参数
|
||||||
|
* 事件:onXxx:(event)=>void 组件原始事件监听
|
||||||
|
* on.onXxx:(context)=>void 组件事件监听(对原始事件包装)
|
||||||
|
* 样式:style、class等
|
||||||
|
*/
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
2
packages/core/pipeline/src/dt/index.ts
Normal file
2
packages/core/pipeline/src/dt/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./pipeline.js";
|
||||||
|
export * from "./fast-crud.js";
|
||||||
139
packages/core/pipeline/src/dt/pipeline.ts
Normal file
139
packages/core/pipeline/src/dt/pipeline.ts
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
export enum RunStrategy {
|
||||||
|
AlwaysRun,
|
||||||
|
SkipWhenSucceed,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ConcurrencyStrategy {
|
||||||
|
Serial,
|
||||||
|
Parallel,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum NextStrategy {
|
||||||
|
AllSuccess,
|
||||||
|
OneSuccess,
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum HandlerType {
|
||||||
|
//清空后续任务的状态
|
||||||
|
ClearFollowStatus,
|
||||||
|
SendEmail,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventHandler = {
|
||||||
|
type: HandlerType;
|
||||||
|
params: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RunnableStrategy = {
|
||||||
|
runStrategy?: RunStrategy;
|
||||||
|
onSuccess?: EventHandler[];
|
||||||
|
onError?: EventHandler[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Step = Runnable & {
|
||||||
|
type: string; //插件类型
|
||||||
|
input: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type Task = Runnable & {
|
||||||
|
steps: Step[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Stage = Runnable & {
|
||||||
|
tasks: Task[];
|
||||||
|
concurrency: ConcurrencyStrategy;
|
||||||
|
next: NextStrategy;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Trigger = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
cron: string;
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FileItem = {
|
||||||
|
id: string;
|
||||||
|
filename: string;
|
||||||
|
path: string;
|
||||||
|
};
|
||||||
|
export type Runnable = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
strategy?: RunnableStrategy;
|
||||||
|
runnableType?: string; // pipeline, stage, task , step
|
||||||
|
status?: HistoryResult;
|
||||||
|
timeout?: number;
|
||||||
|
default?: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EmailOptions = {
|
||||||
|
receivers: string[];
|
||||||
|
};
|
||||||
|
export type NotificationWhen = "error" | "success" | "turnToSuccess" | "start";
|
||||||
|
export type NotificationType = "email" | "url";
|
||||||
|
export type Notification = {
|
||||||
|
type: NotificationType;
|
||||||
|
when: NotificationWhen[];
|
||||||
|
options: EmailOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Pipeline = Runnable & {
|
||||||
|
version?: number;
|
||||||
|
userId: any;
|
||||||
|
stages: Stage[];
|
||||||
|
triggers: Trigger[];
|
||||||
|
notifications?: Notification[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Context = {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Log = {
|
||||||
|
title: string;
|
||||||
|
time: number;
|
||||||
|
level: string;
|
||||||
|
text: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum ResultType {
|
||||||
|
start = "start",
|
||||||
|
success = "success",
|
||||||
|
error = "error",
|
||||||
|
canceled = "canceled",
|
||||||
|
skip = "skip",
|
||||||
|
none = "none",
|
||||||
|
}
|
||||||
|
|
||||||
|
export type HistoryResultGroup = {
|
||||||
|
[key: string]: {
|
||||||
|
runnable: Runnable;
|
||||||
|
res: HistoryResult;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
export type HistoryResult = {
|
||||||
|
input: any;
|
||||||
|
output: any;
|
||||||
|
files?: FileItem[];
|
||||||
|
/**
|
||||||
|
* 任务状态
|
||||||
|
*/
|
||||||
|
status: ResultType;
|
||||||
|
startTime: number;
|
||||||
|
endTime?: number;
|
||||||
|
/**
|
||||||
|
* 处理结果
|
||||||
|
*/
|
||||||
|
result?: ResultType; //success, error,skip
|
||||||
|
message?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type RunnableMap = {
|
||||||
|
[id: string]: Runnable;
|
||||||
|
};
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import "util";
|
import "util";
|
||||||
export * from "./core";
|
export * from "./core/index.js";
|
||||||
export * from "./d.ts";
|
export * from "./dt/index.js";
|
||||||
export * from "./access";
|
export * from "./access/index.js";
|
||||||
export * from "./registry";
|
export * from "./registry/index.js";
|
||||||
export * from "./plugin";
|
export * from "./plugin/index.js";
|
||||||
export * from "./utils";
|
export * from "./utils/index.js";
|
||||||
export * from "./context";
|
export * from "./context/index.js";
|
||||||
export * from "./decorator";
|
export * from "./decorator/index.js";
|
||||||
export * from "./service";
|
export * from "./service/index.js";
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { Registrable } from "../registry";
|
import { Registrable } from "../registry/index.js";
|
||||||
import { FileItem, FormItemProps, Pipeline, Runnable, Step } from "../d.ts";
|
import { FileItem, FormItemProps, Pipeline, Runnable, Step } from "../dt/index.js";
|
||||||
import { FileStore } from "../core/file-store";
|
import { FileStore } from "../core/file-store.js";
|
||||||
import { Logger } from "log4js";
|
import { Logger } from "log4js";
|
||||||
import { IAccessService } from "../access";
|
import { IAccessService } from "../access/index.js";
|
||||||
import { IEmailService } from "../service";
|
import { IEmailService } from "../service/index.js";
|
||||||
import { IContext } from "../core";
|
import { IContext } from "../core/index.js";
|
||||||
import { AxiosInstance } from "axios";
|
import { AxiosInstance } from "axios";
|
||||||
import { logger } from "../utils";
|
import { logger } from "../utils/index.js";
|
||||||
|
|
||||||
export enum ContextScope {
|
export enum ContextScope {
|
||||||
global,
|
global,
|
||||||
@@ -23,6 +23,7 @@ export type TaskInputDefine = FormItemProps;
|
|||||||
|
|
||||||
export type PluginDefine = Registrable & {
|
export type PluginDefine = Registrable & {
|
||||||
default?: any;
|
default?: any;
|
||||||
|
group?: string;
|
||||||
input?: {
|
input?: {
|
||||||
[key: string]: TaskInputDefine;
|
[key: string]: TaskInputDefine;
|
||||||
};
|
};
|
||||||
@@ -50,6 +51,7 @@ export type ITaskPlugin = {
|
|||||||
export type TaskResult = {
|
export type TaskResult = {
|
||||||
clearLastStatus?: boolean;
|
clearLastStatus?: boolean;
|
||||||
files?: FileItem[];
|
files?: FileItem[];
|
||||||
|
pipelineVars: Record<string, any>;
|
||||||
};
|
};
|
||||||
export type TaskInstanceContext = {
|
export type TaskInstanceContext = {
|
||||||
pipeline: Pipeline;
|
pipeline: Pipeline;
|
||||||
@@ -65,7 +67,7 @@ export type TaskInstanceContext = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
||||||
_result: TaskResult = { clearLastStatus: false, files: [] };
|
_result: TaskResult = { clearLastStatus: false, files: [], pipelineVars: {} };
|
||||||
ctx!: TaskInstanceContext;
|
ctx!: TaskInstanceContext;
|
||||||
clearLastStatus() {
|
clearLastStatus() {
|
||||||
this._result.clearLastStatus = true;
|
this._result.clearLastStatus = true;
|
||||||
@@ -82,22 +84,23 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
|||||||
randomFileId() {
|
randomFileId() {
|
||||||
return Math.random().toString(36).substring(2, 9);
|
return Math.random().toString(36).substring(2, 9);
|
||||||
}
|
}
|
||||||
linkFile(file: FileItem) {
|
|
||||||
this._result.files!.push({
|
|
||||||
...file,
|
|
||||||
id: this.randomFileId(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
saveFile(filename: string, file: Buffer) {
|
saveFile(filename: string, file: Buffer) {
|
||||||
const filePath = this.ctx.fileStore.writeFile(filename, file);
|
const filePath = this.ctx.fileStore.writeFile(filename, file);
|
||||||
logger.info(`saveFile:${filePath}`);
|
logger.info(`saveFile:${filePath}`);
|
||||||
this._result.files!.push({
|
this._result.files?.push({
|
||||||
id: this.randomFileId(),
|
id: this.randomFileId(),
|
||||||
filename,
|
filename,
|
||||||
path: filePath,
|
path: filePath,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extendsFiles() {
|
||||||
|
if (this._result.files == null) {
|
||||||
|
this._result.files = [];
|
||||||
|
}
|
||||||
|
this._result.files.push(...(this.ctx.lastStatus?.status?.files || []));
|
||||||
|
}
|
||||||
|
|
||||||
get pipeline() {
|
get pipeline() {
|
||||||
return this.ctx.pipeline;
|
return this.ctx.pipeline;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import _ from "lodash";
|
import _ from "lodash-es";
|
||||||
import { pluginRegistry } from "./registry";
|
import { pluginRegistry } from "./registry.js";
|
||||||
import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api";
|
import { PluginDefine, TaskInputDefine, TaskOutputDefine } from "./api.js";
|
||||||
import { Decorator } from "../decorator";
|
import { Decorator } from "../decorator/index.js";
|
||||||
import { AUTOWIRE_KEY } from "../decorator";
|
import { AUTOWIRE_KEY } from "../decorator/index.js";
|
||||||
import "reflect-metadata";
|
import "reflect-metadata";
|
||||||
// 提供一个唯一 key
|
// 提供一个唯一 key
|
||||||
export const PLUGIN_CLASS_KEY = "pipeline:plugin";
|
export const PLUGIN_CLASS_KEY = "pipeline:plugin";
|
||||||
@@ -31,7 +31,24 @@ export function IsTaskPlugin(define: PluginDefine): ClassDecorator {
|
|||||||
outputs[property] = output;
|
outputs[property] = output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_.merge(define, { input: inputs, autowire: autowires, output: outputs });
|
|
||||||
|
// inputs 转换为array,根据order排序,然后再转换为map
|
||||||
|
|
||||||
|
let inputArray = [];
|
||||||
|
for (const key in inputs) {
|
||||||
|
const _input = inputs[key];
|
||||||
|
if (_input.order == null) {
|
||||||
|
_input.order = 0;
|
||||||
|
}
|
||||||
|
inputArray.push([key, _input]);
|
||||||
|
}
|
||||||
|
inputArray = _.sortBy(inputArray, (item: any) => item[1].order);
|
||||||
|
const inputMap: any = {};
|
||||||
|
inputArray.forEach((item: any) => {
|
||||||
|
inputMap[item[0]] = item[1];
|
||||||
|
});
|
||||||
|
|
||||||
|
_.merge(define, { input: inputMap, autowire: autowires, output: outputs });
|
||||||
|
|
||||||
Reflect.defineMetadata(PLUGIN_CLASS_KEY, define, target);
|
Reflect.defineMetadata(PLUGIN_CLASS_KEY, define, target);
|
||||||
|
|
||||||
|
|||||||
25
packages/core/pipeline/src/plugin/group.ts
Normal file
25
packages/core/pipeline/src/plugin/group.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { PluginDefine } from "./api";
|
||||||
|
|
||||||
|
export class PluginGroup {
|
||||||
|
key: string;
|
||||||
|
title: string;
|
||||||
|
desc?: string;
|
||||||
|
order: number;
|
||||||
|
plugins: PluginDefine[];
|
||||||
|
constructor(key: string, title: string, order = 0, desc = "") {
|
||||||
|
this.key = key;
|
||||||
|
this.title = title;
|
||||||
|
this.order = order;
|
||||||
|
this.desc = desc;
|
||||||
|
this.plugins = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pluginGroups = {
|
||||||
|
cert: new PluginGroup("cert", "证书申请", 1),
|
||||||
|
aliyun: new PluginGroup("aliyun", "阿里云", 2),
|
||||||
|
huawei: new PluginGroup("huawei", "华为云", 3),
|
||||||
|
tencent: new PluginGroup("tencent", "腾讯云", 4),
|
||||||
|
host: new PluginGroup("host", "主机", 5),
|
||||||
|
other: new PluginGroup("other", "其他", 7),
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export * from "./api";
|
export * from "./api.js";
|
||||||
export * from "./registry";
|
export * from "./registry.js";
|
||||||
export * from "./decorator";
|
export * from "./decorator.js";
|
||||||
|
export * from "./group.js";
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
import { Registry } from "../registry";
|
import { OnRegisterContext, Registry } from "../registry/index.js";
|
||||||
import { AbstractTaskPlugin } from "./api";
|
import { AbstractTaskPlugin } from "./api.js";
|
||||||
|
import { pluginGroups } from "./group.js";
|
||||||
|
|
||||||
export const pluginRegistry = new Registry<AbstractTaskPlugin>("plugin");
|
const onRegister = ({ key, value }: OnRegisterContext<AbstractTaskPlugin>) => {
|
||||||
|
const group = value?.define?.group as string;
|
||||||
|
if (group) {
|
||||||
|
if (pluginGroups.hasOwnProperty(group)) {
|
||||||
|
// @ts-ignore
|
||||||
|
pluginGroups[group].plugins.push(value.define);
|
||||||
|
} else {
|
||||||
|
pluginGroups.other.plugins.push(value.define);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const pluginRegistry = new Registry<AbstractTaskPlugin>("plugin", onRegister);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ITaskPlugin } from "../api";
|
import { ITaskPlugin } from "../api.js";
|
||||||
import { IsTaskPlugin, TaskInput } from "../decorator";
|
import { IsTaskPlugin, TaskInput } from "../decorator.js";
|
||||||
import { Autowire } from "../../decorator";
|
|
||||||
|
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: "EchoPlugin",
|
name: "EchoPlugin",
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from "./registry";
|
export * from "./registry.js";
|
||||||
|
|||||||
@@ -1,23 +1,34 @@
|
|||||||
import { logger } from "../utils";
|
import { logger } from "../utils/index.js";
|
||||||
|
|
||||||
export type Registrable = {
|
export type Registrable = {
|
||||||
name: string;
|
name: string;
|
||||||
title: string;
|
title: string;
|
||||||
desc?: string;
|
desc?: string;
|
||||||
|
group?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RegistryItem<T> = {
|
export type RegistryItem<T> = {
|
||||||
define: Registrable;
|
define: Registrable;
|
||||||
target: T;
|
target: T;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type OnRegisterContext<T> = {
|
||||||
|
registry: Registry<T>;
|
||||||
|
key: string;
|
||||||
|
value: RegistryItem<T>;
|
||||||
|
};
|
||||||
|
export type OnRegister<T> = (ctx: OnRegisterContext<T>) => void;
|
||||||
export class Registry<T> {
|
export class Registry<T> {
|
||||||
type = "";
|
type = "";
|
||||||
storage: {
|
storage: {
|
||||||
[key: string]: RegistryItem<T>;
|
[key: string]: RegistryItem<T>;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
constructor(type: string) {
|
onRegister?: OnRegister<T>;
|
||||||
|
|
||||||
|
constructor(type: string, onRegister?: OnRegister<T>) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
this.onRegister = onRegister;
|
||||||
}
|
}
|
||||||
|
|
||||||
register(key: string, value: RegistryItem<T>) {
|
register(key: string, value: RegistryItem<T>) {
|
||||||
@@ -25,6 +36,13 @@ export class Registry<T> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.storage[key] = value;
|
this.storage[key] = value;
|
||||||
|
if (this.onRegister) {
|
||||||
|
this.onRegister({
|
||||||
|
registry: this,
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
});
|
||||||
|
}
|
||||||
logger.info(`注册插件:${this.type}:${key}`);
|
logger.info(`注册插件:${this.type}:${key}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from "./email";
|
export * from "./email.js";
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import sleep from "./util.sleep";
|
import sleep from "./util.sleep.js";
|
||||||
import { request } from "./util.request";
|
import { request } from "./util.request.js";
|
||||||
export * from "./util.log";
|
export * from "./util.log.js";
|
||||||
export * from "./util.file";
|
export * from "./util.file.js";
|
||||||
|
export * from "./util.sp.js";
|
||||||
|
export * as promises from "./util.promise.js";
|
||||||
export const utils = {
|
export const utils = {
|
||||||
sleep,
|
sleep,
|
||||||
http: request,
|
http: request,
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { logger } from "./util.log.js";
|
||||||
|
|
||||||
export function TimeoutPromise(callback: () => Promise<void>, ms = 30 * 1000) {
|
export function TimeoutPromise(callback: () => Promise<void>, ms = 30 * 1000) {
|
||||||
let timeout: any;
|
let timeout: any;
|
||||||
return Promise.race([
|
return Promise.race([
|
||||||
@@ -11,3 +13,14 @@ export function TimeoutPromise(callback: () => Promise<void>, ms = 30 * 1000) {
|
|||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function safePromise<T>(callback: (resolve: (ret: T) => void, reject: (ret: any) => void) => void): Promise<T> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
callback(resolve, reject);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error(e);
|
||||||
|
reject(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import qs from "qs";
|
import qs from "qs";
|
||||||
import { logger } from "./util.log";
|
import { logger } from "./util.log.js";
|
||||||
import { Logger } from "log4js";
|
import { Logger } from "log4js";
|
||||||
/**
|
/**
|
||||||
* @description 创建请求实例
|
* @description 创建请求实例
|
||||||
@@ -50,7 +50,7 @@ export function createAxiosService({ logger }: { logger: Logger }) {
|
|||||||
// case 505: error.message = 'HTTP版本不受支持'; break
|
// case 505: error.message = 'HTTP版本不受支持'; break
|
||||||
// default: break
|
// default: break
|
||||||
// }
|
// }
|
||||||
logger.error(`请求出错:url:${error?.response?.config.url},method:${error.response.config.method},status:${error?.response?.status}`);
|
logger.error(`请求出错:url:${error?.response?.config.url},method:${error?.response?.config?.method},status:${error?.response?.status}`);
|
||||||
logger.info("返回数据:", JSON.stringify(error?.response?.data));
|
logger.info("返回数据:", JSON.stringify(error?.response?.data));
|
||||||
delete error.config;
|
delete error.config;
|
||||||
delete error.response;
|
delete error.response;
|
||||||
|
|||||||
110
packages/core/pipeline/src/utils/util.sp.ts
Normal file
110
packages/core/pipeline/src/utils/util.sp.ts
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
//转换为import
|
||||||
|
import childProcess from "child_process";
|
||||||
|
import { safePromise } from "./util.promise.js";
|
||||||
|
import { ILogger, logger } from "./util.log.js";
|
||||||
|
|
||||||
|
export type ExecOption = {
|
||||||
|
cmd: string | string[];
|
||||||
|
env: any;
|
||||||
|
logger?: ILogger;
|
||||||
|
options?: any;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function exec(opts: ExecOption): Promise<string> {
|
||||||
|
let cmd = "";
|
||||||
|
const log = opts.logger || logger;
|
||||||
|
if (opts.cmd instanceof Array) {
|
||||||
|
for (const item of opts.cmd) {
|
||||||
|
if (cmd) {
|
||||||
|
cmd += " && " + item;
|
||||||
|
} else {
|
||||||
|
cmd = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info(`执行命令: ${cmd}`);
|
||||||
|
return safePromise((resolve, reject) => {
|
||||||
|
childProcess.exec(
|
||||||
|
cmd,
|
||||||
|
{
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
...opts.env,
|
||||||
|
},
|
||||||
|
...opts.options,
|
||||||
|
},
|
||||||
|
(error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
log.error(`exec error: ${error}`);
|
||||||
|
reject(error);
|
||||||
|
} else {
|
||||||
|
const res = stdout.toString("utf-8");
|
||||||
|
log.info(`stdout: ${res}`);
|
||||||
|
resolve(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SpawnOption = {
|
||||||
|
cmd: string | string[];
|
||||||
|
onStdout?: (data: string) => void;
|
||||||
|
onStderr?: (data: string) => void;
|
||||||
|
env: any;
|
||||||
|
logger?: ILogger;
|
||||||
|
options?: any;
|
||||||
|
};
|
||||||
|
async function spawn(opts: SpawnOption): Promise<string> {
|
||||||
|
let cmd = "";
|
||||||
|
const log = opts.logger || logger;
|
||||||
|
if (opts.cmd instanceof Array) {
|
||||||
|
for (const item of opts.cmd) {
|
||||||
|
if (cmd) {
|
||||||
|
cmd += " && " + item;
|
||||||
|
} else {
|
||||||
|
cmd = item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info(`执行命令: ${cmd}`);
|
||||||
|
let stdout = "";
|
||||||
|
let stderr = "";
|
||||||
|
return safePromise((resolve, reject) => {
|
||||||
|
const ls = childProcess.spawn(cmd, {
|
||||||
|
shell: process.platform == "win32",
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
...opts.env,
|
||||||
|
},
|
||||||
|
...opts.options,
|
||||||
|
});
|
||||||
|
ls.stdout.on("data", (data) => {
|
||||||
|
log.info(`stdout: ${data}`);
|
||||||
|
stdout += data;
|
||||||
|
});
|
||||||
|
|
||||||
|
ls.stderr.on("data", (data) => {
|
||||||
|
log.error(`stderr: ${data}`);
|
||||||
|
stderr += data;
|
||||||
|
});
|
||||||
|
ls.on("error", (error) => {
|
||||||
|
log.error(`child process error: ${error}`);
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
ls.on("close", (code: number) => {
|
||||||
|
if (code !== 0) {
|
||||||
|
log.error(`child process exited with code ${code}`);
|
||||||
|
reject(new Error(stderr));
|
||||||
|
} else {
|
||||||
|
resolve(stdout);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sp = {
|
||||||
|
spawn,
|
||||||
|
exec,
|
||||||
|
};
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import "mocha";
|
import "mocha";
|
||||||
import { EchoPlugin } from "./echo-plugin";
|
import { EchoPlugin } from "./echo-plugin.js";
|
||||||
describe("task_plugin", function () {
|
describe("task_plugin", function () {
|
||||||
it("#taskplugin", function () {
|
it("#taskplugin", function () {
|
||||||
console.log("before new plugin");
|
console.log("before new plugin");
|
||||||
|
|||||||
1
packages/core/pipeline/test/pipeline/.gitignore
vendored
Normal file
1
packages/core/pipeline/test/pipeline/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
license.*
|
||||||
@@ -1,23 +1,42 @@
|
|||||||
{
|
{
|
||||||
|
"compileOnSave": true,
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"importHelpers": false,
|
|
||||||
"target": "ESNext",
|
"target": "ESNext",
|
||||||
"useDefineForClassFields": true,
|
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "node",
|
||||||
"strict": true,
|
|
||||||
"jsx": "preserve",
|
|
||||||
"sourceMap": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"isolatedModules": true,
|
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"lib": ["ESNext", "DOM"],
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"paths": {
|
"emitDecoratorMetadata": true,
|
||||||
"tslib" : ["./node_modules/tslib/tslib.d.ts"]
|
"inlineSourceMap":true,
|
||||||
}
|
"noImplicitThis": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"stripInternal": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"pretty": true,
|
||||||
|
"declaration": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"typeRoots": [ "./typings", "./node_modules/@types"],
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"composite": true,
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"strict": true,
|
||||||
|
// "sourceMap": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": false,
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
},
|
},
|
||||||
|
"include": [
|
||||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue","test/**/*.ts","rollup.config.ts"],
|
"src/**/*.ts",
|
||||||
|
"src/**/*.d.ts",
|
||||||
|
"src/**/*.json"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"*.js",
|
||||||
|
"*.ts",
|
||||||
|
"*.spec.ts",
|
||||||
|
"dist",
|
||||||
|
"node_modules",
|
||||||
|
"test"
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
import { defineConfig } from "vite";
|
|
||||||
import visualizer from "rollup-plugin-visualizer";
|
|
||||||
import typescript from "@rollup/plugin-typescript";
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [],
|
|
||||||
build: {
|
|
||||||
target: "es2015",
|
|
||||||
lib: {
|
|
||||||
entry: "src/index.ts",
|
|
||||||
name: "CertdPipeline",
|
|
||||||
},
|
|
||||||
rollupOptions: {
|
|
||||||
plugins: [
|
|
||||||
visualizer(),
|
|
||||||
typescript({
|
|
||||||
target: "es2015",
|
|
||||||
rootDir: "src",
|
|
||||||
declaration: true,
|
|
||||||
declarationDir: "dist/d",
|
|
||||||
exclude: ["./node_modules/**", "./src/**/*.vue"],
|
|
||||||
allowSyntheticDefaultImports: true,
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
external: [
|
|
||||||
"vue",
|
|
||||||
"lodash",
|
|
||||||
"dayjs",
|
|
||||||
"@certd/acme-client",
|
|
||||||
"@certd/plugin-cert",
|
|
||||||
"@certd/plugin-aliyun",
|
|
||||||
"@certd/plugin-tencent",
|
|
||||||
"@certd/plugin-huawei",
|
|
||||||
"@certd/plugin-host",
|
|
||||||
"@certd/plugin-tencent",
|
|
||||||
"@certd/plugin-util",
|
|
||||||
"log4js",
|
|
||||||
"@midwayjs/core",
|
|
||||||
"@midwayjs/decorator",
|
|
||||||
],
|
|
||||||
output: {
|
|
||||||
globals: {
|
|
||||||
vue: "Vue",
|
|
||||||
lodash: "_",
|
|
||||||
dayjs: "dayjs",
|
|
||||||
"@certd/plugin-cert": "CertdPluginCert",
|
|
||||||
"@certd/acme-client": "CertdAcmeClient",
|
|
||||||
"@certd/plugin-aliyun": "CertdPluginAliyun",
|
|
||||||
"@certd/plugin-host": "CertdPluginHost",
|
|
||||||
"@certd/plugin-huawei": "CertdPluginHuawei",
|
|
||||||
"@certd/plugin-util": "CertdPluginUtil",
|
|
||||||
log4js: "log4js",
|
|
||||||
"@midwayjs/core": "MidwayjsCore",
|
|
||||||
"@midwayjs/decorator": "MidwayjsDecorator",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -23,4 +23,6 @@ dist-ssr
|
|||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
test/user.secret.ts
|
test/user.secret.ts
|
||||||
|
|
||||||
|
.rollup.cache
|
||||||
3
packages/libs/lib-huawei/.npmignore
Normal file
3
packages/libs/lib-huawei/.npmignore
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
node_modules
|
||||||
|
src
|
||||||
|
.rollup.cache
|
||||||
15
packages/libs/lib-huawei/CHANGELOG.md
Normal file
15
packages/libs/lib-huawei/CHANGELOG.md
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Change Log
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.22.1](https://github.com/certd/certd/compare/v1.22.0...v1.22.1) (2024-07-20)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|
||||||
|
# [1.22.0](https://github.com/certd/certd/compare/v1.21.2...v1.22.0) (2024-07-19)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* 升级midway,支持esm ([485e603](https://github.com/certd/certd/commit/485e603b5165c28bc08694997726eaf2a585ebe7))
|
||||||
|
* 支持postgresql ([3b19bfb](https://github.com/certd/certd/commit/3b19bfb4291e89064b3b407a80dae092d54747d5))
|
||||||
96
packages/libs/lib-huawei/fix-esm-import-paths.js
Normal file
96
packages/libs/lib-huawei/fix-esm-import-paths.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import * as fs from "fs";
|
||||||
|
import * as path from "path";
|
||||||
|
|
||||||
|
// https://gist.github.com/lovasoa/8691344
|
||||||
|
async function* walk(dir) {
|
||||||
|
for await (const d of await fs.promises.opendir(dir)) {
|
||||||
|
const entry = path.join(dir, d.name);
|
||||||
|
if (d.isDirectory()) {
|
||||||
|
yield* walk(entry);
|
||||||
|
} else if (d.isFile()) {
|
||||||
|
yield entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveImportPath(sourceFile, importPath, options) {
|
||||||
|
const sourceFileAbs = path.resolve(process.cwd(), sourceFile);
|
||||||
|
const root = path.dirname(sourceFileAbs);
|
||||||
|
const { moduleFilter = defaultModuleFilter } = options;
|
||||||
|
|
||||||
|
if (moduleFilter(importPath)) {
|
||||||
|
const importPathAbs = path.resolve(root, importPath);
|
||||||
|
let possiblePath = [path.resolve(importPathAbs, "./index.ts"), path.resolve(importPathAbs, "./index.js"), importPathAbs + ".ts", importPathAbs + ".js"];
|
||||||
|
|
||||||
|
if (possiblePath.length) {
|
||||||
|
for (let i = 0; i < possiblePath.length; i++) {
|
||||||
|
let entry = possiblePath[i];
|
||||||
|
if (fs.existsSync(entry)) {
|
||||||
|
const resolved = path.relative(root, entry.replace(/\.ts$/, ".js"));
|
||||||
|
|
||||||
|
if (!resolved.startsWith(".")) {
|
||||||
|
return "./" + resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function replace(filePath, outFilePath, options) {
|
||||||
|
const code = fs.readFileSync(filePath).toString();
|
||||||
|
const newCode = code.replace(/(import|export) (.+?) from ('[^\n']+'|"[^\n"]+");/gs, function (found, action, imported, from) {
|
||||||
|
const importPath = from.slice(1, -1);
|
||||||
|
let resolvedPath = resolveImportPath(filePath, importPath, options);
|
||||||
|
|
||||||
|
if (resolvedPath) {
|
||||||
|
resolvedPath = resolvedPath.replaceAll("\\", "/");
|
||||||
|
console.log("\t", importPath, resolvedPath);
|
||||||
|
return `${action} ${imported} from "${resolvedPath}";`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (code !== newCode) {
|
||||||
|
fs.writeFileSync(outFilePath, newCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then, use it with a simple async for loop
|
||||||
|
async function run(srcDir, options = defaultOptions) {
|
||||||
|
const { sourceFileFilter = defaultSourceFileFilter } = options;
|
||||||
|
|
||||||
|
for await (const entry of walk(srcDir)) {
|
||||||
|
if (sourceFileFilter(entry)) {
|
||||||
|
console.log(entry);
|
||||||
|
replace(entry, entry, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultSourceFileFilter = function (sourceFilePath) {
|
||||||
|
return /\.(js|ts)$/.test(sourceFilePath) && !/node_modules/.test(sourceFilePath);
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultModuleFilter = function (importedModule) {
|
||||||
|
return !path.isAbsolute(importedModule) && !importedModule.startsWith("@") && !importedModule.endsWith(".js");
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultOptions = {
|
||||||
|
sourceFileFilter: defaultSourceFileFilter,
|
||||||
|
moduleFilter: defaultModuleFilter,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Switch this to test on one file or directly run on a directory.
|
||||||
|
const DEBUG = false;
|
||||||
|
|
||||||
|
if (DEBUG) {
|
||||||
|
replace("./src/index.ts", "./out.ts", defaultOptions);
|
||||||
|
} else {
|
||||||
|
await run("./src/", defaultOptions);
|
||||||
|
}
|
||||||
20
packages/libs/lib-huawei/package.json
Normal file
20
packages/libs/lib-huawei/package.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"name": "@certd/lib-huawei",
|
||||||
|
"private": false,
|
||||||
|
"version": "1.22.1",
|
||||||
|
"main": "./dist/bundle.js",
|
||||||
|
"module": "./dist/bundle.js",
|
||||||
|
"types": "./dist/d/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "rollup -c ",
|
||||||
|
"build2": "vue-tsc --noEmit && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@certd/pipeline": "1.21.0",
|
||||||
|
"axios": "^1.7.2",
|
||||||
|
"rollup": "^3.7.4"
|
||||||
|
},
|
||||||
|
"gitHead": "47fe3d5826661f678d081ab53e67c847a3239d88"
|
||||||
|
}
|
||||||
@@ -32,22 +32,5 @@ module.exports = {
|
|||||||
json(),
|
json(),
|
||||||
terser(),
|
terser(),
|
||||||
],
|
],
|
||||||
external: [
|
external: ["vue", "lodash-es", "dayjs", "log4js", "@midwayjs/core", "@certd/pipeline", "axios"],
|
||||||
"vue",
|
|
||||||
"lodash",
|
|
||||||
"dayjs",
|
|
||||||
"@certd/acme-client",
|
|
||||||
"@certd/pipeline",
|
|
||||||
"@certd/plugin-cert",
|
|
||||||
"@certd/plugin-aliyun",
|
|
||||||
"@certd/plugin-tencent",
|
|
||||||
"@certd/plugin-huawei",
|
|
||||||
"@certd/plugin-host",
|
|
||||||
"@certd/plugin-tencent",
|
|
||||||
"@certd/plugin-util",
|
|
||||||
"log4js",
|
|
||||||
"@midwayjs/core",
|
|
||||||
"@midwayjs/decorator",
|
|
||||||
"kubernetes-client",
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
2
packages/libs/lib-huawei/src/index.ts
Normal file
2
packages/libs/lib-huawei/src/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export { HuaweiYunClient } from "./lib/client.js";
|
||||||
|
export { ApiRequestOptions } from "./lib/client.js";
|
||||||
12
packages/libs/lib-huawei/src/lib/client.d.ts
vendored
Normal file
12
packages/libs/lib-huawei/src/lib/client.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { HuaweiAccess } from "../access/index.js";
|
||||||
|
export type ApiRequestOptions = {
|
||||||
|
method: string;
|
||||||
|
url: string;
|
||||||
|
headers?: any;
|
||||||
|
data?: any;
|
||||||
|
};
|
||||||
|
export declare class HuaweiYunClient {
|
||||||
|
access: HuaweiAccess;
|
||||||
|
constructor(access: HuaweiAccess);
|
||||||
|
request(options: ApiRequestOptions): Promise<any>;
|
||||||
|
}
|
||||||
41
packages/libs/lib-huawei/src/lib/client.js
Normal file
41
packages/libs/lib-huawei/src/lib/client.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import { Signer, SigHttpRequest } from "./signer.js";
|
||||||
|
import axios from "axios";
|
||||||
|
export class HuaweiYunClient {
|
||||||
|
access;
|
||||||
|
constructor(access, logger) {
|
||||||
|
this.access = access;
|
||||||
|
}
|
||||||
|
async request(options) {
|
||||||
|
const sig = new Signer(this.access.accessKeyId, this.access.accessKeySecret);
|
||||||
|
//The following example shows how to set the request URL and parameters to query a VPC list.
|
||||||
|
//Specify a request method, such as GET, PUT, POST, DELETE, HEAD, and PATCH.
|
||||||
|
//Set request host.
|
||||||
|
//Set request URI.
|
||||||
|
//Set parameters for the request URL.
|
||||||
|
let body = undefined;
|
||||||
|
if (options.data) {
|
||||||
|
body = JSON.stringify(options.data);
|
||||||
|
}
|
||||||
|
const r = new SigHttpRequest(options.method, options.url, options.headers, body);
|
||||||
|
//Add header parameters, for example, x-domain-id for invoking a global service and x-project-id for invoking a project-level service.
|
||||||
|
r.headers = { "Content-Type": "application/json" };
|
||||||
|
//Add a body if you have specified the PUT or POST method. Special characters, such as the double quotation mark ("), contained in the body must be escaped.
|
||||||
|
// r.body = option;
|
||||||
|
const opt = sig.Sign(r);
|
||||||
|
try {
|
||||||
|
const res = await axios.request({
|
||||||
|
url: options.url,
|
||||||
|
method: options.method,
|
||||||
|
headers: opt.headers,
|
||||||
|
data: body,
|
||||||
|
});
|
||||||
|
return res.data;
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error("华为云接口请求出错:", e?.response?.data);
|
||||||
|
const error = new Error(e?.response?.data.message);
|
||||||
|
error.code = e?.response?.code;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL3BsdWdpbnMvcGx1Z2luLWh1YXdlaS9saWIvY2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLEVBQUUsY0FBYyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBRXJELE9BQU8sS0FBSyxNQUFNLE9BQU8sQ0FBQztBQUMxQixPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFRekMsTUFBTSxPQUFPLGVBQWU7SUFDMUIsTUFBTSxDQUFlO0lBQ3JCLFlBQVksTUFBb0I7UUFDOUIsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7SUFDdkIsQ0FBQztJQUNELEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBMEI7UUFDdEMsTUFBTSxHQUFHLEdBQUcsSUFBSSxNQUFNLENBQ3BCLElBQUksQ0FBQyxNQUFNLENBQUMsV0FBVyxFQUN2QixJQUFJLENBQUMsTUFBTSxDQUFDLGVBQWUsQ0FDNUIsQ0FBQztRQUVGLDRGQUE0RjtRQUM1Riw0RUFBNEU7UUFDNUUsbUJBQW1CO1FBQ25CLGtCQUFrQjtRQUNsQixxQ0FBcUM7UUFDckMsSUFBSSxJQUFJLEdBQUcsU0FBUyxDQUFDO1FBQ3JCLElBQUksT0FBTyxDQUFDLElBQUksRUFBRTtZQUNoQixJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDckM7UUFDRCxNQUFNLENBQUMsR0FBRyxJQUFJLGNBQWMsQ0FDMUIsT0FBTyxDQUFDLE1BQU0sRUFDZCxPQUFPLENBQUMsR0FBRyxFQUNYLE9BQU8sQ0FBQyxPQUFPLEVBQ2YsSUFBSSxDQUNMLENBQUM7UUFDRixzSUFBc0k7UUFDdEksQ0FBQyxDQUFDLE9BQU8sR0FBRyxFQUFFLGNBQWMsRUFBRSxrQkFBa0IsRUFBRSxDQUFDO1FBQ25ELDRKQUE0SjtRQUM1SixtQkFBbUI7UUFDbkIsTUFBTSxHQUFHLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUN4QixJQUFJO1lBQ0YsTUFBTSxHQUFHLEdBQUcsTUFBTSxLQUFLLENBQUMsT0FBTyxDQUFDO2dCQUM5QixHQUFHLEVBQUUsT0FBTyxDQUFDLEdBQUc7Z0JBQ2hCLE1BQU0sRUFBRSxPQUFPLENBQUMsTUFBTTtnQkFDdEIsT0FBTyxFQUFFLEdBQUcsQ0FBQyxPQUFPO2dCQUNwQixJQUFJLEVBQUUsSUFBSTthQUNYLENBQUMsQ0FBQztZQUNILE9BQU8sR0FBRyxDQUFDLElBQUksQ0FBQztTQUNqQjtRQUFDLE9BQU8sQ0FBTSxFQUFFO1lBQ2YsTUFBTSxDQUFDLEtBQUssQ0FBQyxZQUFZLEVBQUUsQ0FBQyxFQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsQ0FBQztZQUM5QyxNQUFNLEtBQUssR0FBUSxJQUFJLEtBQUssQ0FBQyxDQUFDLEVBQUUsUUFBUSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUN4RCxLQUFLLENBQUMsSUFBSSxHQUFHLENBQUMsRUFBRSxRQUFRLEVBQUUsSUFBSSxDQUFDO1lBQy9CLE1BQU0sS0FBSyxDQUFDO1NBQ2I7SUFDSCxDQUFDO0NBQ0YifQ==
|
||||||
20
packages/libs/lib-huawei/src/lib/signer.d.ts
vendored
Normal file
20
packages/libs/lib-huawei/src/lib/signer.d.ts
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
export declare class SigHttpRequest {
|
||||||
|
method: string;
|
||||||
|
host: string;
|
||||||
|
uri: string;
|
||||||
|
query: any;
|
||||||
|
headers: any;
|
||||||
|
body: string;
|
||||||
|
constructor(method: any, url: any, headers: any, body: any);
|
||||||
|
}
|
||||||
|
export declare class Signer {
|
||||||
|
Key: string;
|
||||||
|
Secret: string;
|
||||||
|
constructor(Key: any, Secret: any);
|
||||||
|
Sign(r: any): {
|
||||||
|
hostname: any;
|
||||||
|
path: string;
|
||||||
|
method: any;
|
||||||
|
headers: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
459
packages/libs/lib-huawei/src/lib/signer.js
Normal file
459
packages/libs/lib-huawei/src/lib/signer.js
Normal file
File diff suppressed because one or more lines are too long
41
packages/libs/lib-huawei/tsconfig.json
Normal file
41
packages/libs/lib-huawei/tsconfig.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"compileOnSave": true,
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"inlineSourceMap":true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"stripInternal": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"pretty": true,
|
||||||
|
"declaration": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"typeRoots": [ "./typings", "./node_modules/@types"],
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"composite": true,
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"strict": false,
|
||||||
|
"sourceMap": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": false,
|
||||||
|
"lib": ["ESNext", "DOM"],
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts",
|
||||||
|
"src/**/*.d.ts",
|
||||||
|
"src/**/*.js",
|
||||||
|
"src/**/*.json"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"*.ts",
|
||||||
|
"dist",
|
||||||
|
"node_modules",
|
||||||
|
"test"
|
||||||
|
],
|
||||||
|
}
|
||||||
23
packages/libs/lib-k8s/.eslintrc
Normal file
23
packages/libs/lib-k8s/.eslintrc
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:prettier/recommended",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"mocha": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-var-requires": "off",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
|
"@typescript-eslint/ban-ts-ignore": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
|
// "no-unused-expressions": "off",
|
||||||
|
"max-len": [0, 160, 2, { "ignoreUrls": true }]
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user