mirror of
https://github.com/certd/certd.git
synced 2026-04-10 02:10:59 +08:00
Compare commits
73 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9749fc817d | ||
|
|
e6600f2c43 | ||
|
|
a664931e7a | ||
|
|
a2ba965600 | ||
|
|
65255dbb50 | ||
|
|
a5cb8761a5 | ||
|
|
e3930e0717 | ||
|
|
afd59e9933 | ||
|
|
8087524bef | ||
|
|
605440812f | ||
|
|
b10c6eb615 | ||
|
|
a96264ff6a | ||
|
|
deb3893820 | ||
|
|
9b1d822b5b | ||
|
|
5cd61c4c02 | ||
|
|
586fa70eac | ||
|
|
9b420ad33f | ||
|
|
5891290672 | ||
|
|
72a7b51d47 | ||
|
|
2943e0e58d | ||
|
|
5abce916a8 | ||
|
|
89d4be8a0a | ||
|
|
b7113bda23 | ||
|
|
0088929622 | ||
|
|
b3468cf7f2 | ||
|
|
f88c5c8528 | ||
|
|
687fdda7f7 | ||
|
|
aec51e514c | ||
|
|
308d4600ef | ||
|
|
50a5fa15bb | ||
|
|
7d96a57d73 | ||
|
|
162ebfd4e0 | ||
|
|
a586a92d5e | ||
|
|
3df20a924f | ||
|
|
ddcf466e4e | ||
|
|
5d10cbf18d | ||
|
|
8d9afa7592 | ||
|
|
95e05336c2 | ||
|
|
a188385817 | ||
|
|
0a6baf331b | ||
|
|
0e29e052d5 | ||
|
|
d8d255980e | ||
|
|
dc5a5fa543 | ||
|
|
8638fc91ff | ||
|
|
96a0900edc | ||
|
|
abea80e3ab | ||
|
|
42dfe936b7 | ||
|
|
8385bcc2d7 | ||
|
|
9b8f60b64b | ||
|
|
474114236e | ||
|
|
238b0b421a | ||
|
|
8abe62886a | ||
|
|
78cc9cffe4 | ||
|
|
59a5dd713f | ||
|
|
a39024ff03 | ||
|
|
72bfbd93a8 | ||
|
|
c9a3e3d9d2 | ||
|
|
8387708901 | ||
|
|
b565b4b3b9 | ||
|
|
893dcd4f24 | ||
|
|
d613aa8f3e | ||
|
|
5750bb7067 | ||
|
|
0e07ae6ce8 | ||
|
|
02b6351e13 | ||
|
|
78367af830 | ||
|
|
dc05cd481f | ||
|
|
7daad5477a | ||
|
|
45cdfbfae8 | ||
|
|
3fb5c38571 | ||
|
|
59f80ebc47 | ||
|
|
e5a7ada3cf | ||
|
|
b76fdd7fe4 | ||
|
|
91ffb0820a |
59
CHANGELOG.md
59
CHANGELOG.md
@@ -3,6 +3,65 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复二次认证登录进入错误账号的bug ([e3930e0](https://github.com/certd/certd/commit/e3930e07172dd7903cb0f6ff26e0e3e828ba3e77))
|
||||
|
||||
### Features
|
||||
|
||||
* 从yaml文件注册插件 ([deb3893](https://github.com/certd/certd/commit/deb38938204b29543f36d3266249958faaaa6b66))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 优化cdnfly插件,支持自动匹配域名部署 ([afd59e9](https://github.com/certd/certd/commit/afd59e9933b2650f41c5d47684c171b93b962065))
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 服务器时间获取不准确的bug ([5d10cbf](https://github.com/certd/certd/commit/5d10cbf18daf94a90a7551641a3b13e3c5fec611))
|
||||
* 修复复制流水线无效的bug ([3df20a9](https://github.com/certd/certd/commit/3df20a924f32970b052e2588ea20de095f0ea693))
|
||||
* 修复http上传方式无法清除记录文件的bug ([72a7b51](https://github.com/certd/certd/commit/72a7b51d479602b2c54c6c3ac8d8a0dcb9664e73))
|
||||
* 修复token过期后,疯狂打印token过期信息的bug ([50a5fa1](https://github.com/certd/certd/commit/50a5fa15bb240a125bbc91d2ce1ff3c835888a77))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 从域名的soa获取主域名,子域名托管无需额外配置 ([a586a92](https://github.com/certd/certd/commit/a586a92d5e32ea846ac37be52a7ad8c328d89966))
|
||||
* 七牛oss支持删除过期备份 ([b7113bd](https://github.com/certd/certd/commit/b7113bda2378116d6c116dc583f563cce7cf9f00))
|
||||
* 数据库备份支持oss ([308d460](https://github.com/certd/certd/commit/308d4600efe2002f199c33b4594d3071784e58ea))
|
||||
* 支持阿里云中文域名申请 ([b3468cf](https://github.com/certd/certd/commit/b3468cf7f28228d7c9cf68de6b5a9bbeb67f2c6d))
|
||||
* 支持反向代理增加contextPath路径 ([0088929](https://github.com/certd/certd/commit/0088929622160cc922995de9a563e8061686ff34))
|
||||
* 支持中文域名 ([162ebfd](https://github.com/certd/certd/commit/162ebfd4e0c25727efb33952d3bbf7420a02e2c3))
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 添加部署证书至火山 Live ([abea80e](https://github.com/certd/certd/commit/abea80e3ab9b1672aebe1c5d5e856693b29931a8))
|
||||
* 优化首页插件列表展示 ([9b8f60b](https://github.com/certd/certd/commit/9b8f60b64b5f9a3db7dfa9b3dcbd9201984358d0))
|
||||
* 证书申请支持51dns ([8638fc9](https://github.com/certd/certd/commit/8638fc91ff34fccaf12ff9874fd3fa9d2a8c18b7))
|
||||
* 支持51dns ([96a0900](https://github.com/certd/certd/commit/96a0900edc95dcfd9acccf9d13592f12f5a09b3d))
|
||||
* ssh PTY模式登录设置 ([8385bcc](https://github.com/certd/certd/commit/8385bcc2d7f2411a07748bb5c53f9eaf4d38d7cc))
|
||||
* ssh伪终端模式优化,windows下不开启 ([42dfe93](https://github.com/certd/certd/commit/42dfe936b773b7bdd82ca3378363252ffffd7b71))
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 上传商用证书,直接粘贴文本报错的问题;修复无法上传ec加密证书的bug ([5750bb7](https://github.com/certd/certd/commit/5750bb706779da274d8e7a87e71416cb64d2df79))
|
||||
* 修复下载证书时提示token已过期的问题 ([0e07ae6](https://github.com/certd/certd/commit/0e07ae6ce84dcb9279d3c44060d621566afa593c))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 更新license时同时绑定url ([78367af](https://github.com/certd/certd/commit/78367af8307f801e778c76d49f0918c21ffe032f))
|
||||
* 切换到不同的分组后再打开创建对话框,会自动选择分组 ([893dcd4](https://github.com/certd/certd/commit/893dcd4f2487891199ed3e5a3d47a79a75efc942))
|
||||
* 新增部署到火山引擎ALB/CLB、上传到证书中心 ([c9a3e3d](https://github.com/certd/certd/commit/c9a3e3d9d26f964c7af7b56667936f1414fbf42a))
|
||||
* 优化/api缓存为0 ([dc05cd4](https://github.com/certd/certd/commit/dc05cd481f186b13375192be965000e6b4b429a5))
|
||||
* 优化华为cdn插件引用ccm证书 ([b565b4b](https://github.com/certd/certd/commit/b565b4b3b919b71b98ea2517670bc1ef00e00dc9))
|
||||
* 优化证书流水线创建,支持选择分组 ([d613aa8](https://github.com/certd/certd/commit/d613aa8f3e85d8dc475ef1b62d49394ce7fd7d24))
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -1 +1 @@
|
||||
23:49
|
||||
01:58
|
||||
|
||||
@@ -3,6 +3,59 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 服务器时间获取不准确的bug ([5d10cbf](https://github.com/certd/certd/commit/5d10cbf18daf94a90a7551641a3b13e3c5fec611))
|
||||
* 修复复制流水线无效的bug ([3df20a9](https://github.com/certd/certd/commit/3df20a924f32970b052e2588ea20de095f0ea693))
|
||||
* 修复http上传方式无法清除记录文件的bug ([72a7b51](https://github.com/certd/certd/commit/72a7b51d479602b2c54c6c3ac8d8a0dcb9664e73))
|
||||
* 修复token过期后,疯狂打印token过期信息的bug ([50a5fa1](https://github.com/certd/certd/commit/50a5fa15bb240a125bbc91d2ce1ff3c835888a77))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 从域名的soa获取主域名,子域名托管无需额外配置 ([a586a92](https://github.com/certd/certd/commit/a586a92d5e32ea846ac37be52a7ad8c328d89966))
|
||||
* 七牛oss支持删除过期备份 ([b7113bd](https://github.com/certd/certd/commit/b7113bda2378116d6c116dc583f563cce7cf9f00))
|
||||
* 数据库备份支持oss ([308d460](https://github.com/certd/certd/commit/308d4600efe2002f199c33b4594d3071784e58ea))
|
||||
* 支持阿里云中文域名申请 ([b3468cf](https://github.com/certd/certd/commit/b3468cf7f28228d7c9cf68de6b5a9bbeb67f2c6d))
|
||||
* 支持反向代理增加contextPath路径 ([0088929](https://github.com/certd/certd/commit/0088929622160cc922995de9a563e8061686ff34))
|
||||
* 支持中文域名 ([162ebfd](https://github.com/certd/certd/commit/162ebfd4e0c25727efb33952d3bbf7420a02e2c3))
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 添加部署证书至火山 Live ([abea80e](https://github.com/certd/certd/commit/abea80e3ab9b1672aebe1c5d5e856693b29931a8))
|
||||
* 优化首页插件列表展示 ([9b8f60b](https://github.com/certd/certd/commit/9b8f60b64b5f9a3db7dfa9b3dcbd9201984358d0))
|
||||
* 证书申请支持51dns ([8638fc9](https://github.com/certd/certd/commit/8638fc91ff34fccaf12ff9874fd3fa9d2a8c18b7))
|
||||
* 支持51dns ([96a0900](https://github.com/certd/certd/commit/96a0900edc95dcfd9acccf9d13592f12f5a09b3d))
|
||||
* ssh PTY模式登录设置 ([8385bcc](https://github.com/certd/certd/commit/8385bcc2d7f2411a07748bb5c53f9eaf4d38d7cc))
|
||||
* ssh伪终端模式优化,windows下不开启 ([42dfe93](https://github.com/certd/certd/commit/42dfe936b773b7bdd82ca3378363252ffffd7b71))
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 上传商用证书,直接粘贴文本报错的问题;修复无法上传ec加密证书的bug ([5750bb7](https://github.com/certd/certd/commit/5750bb706779da274d8e7a87e71416cb64d2df79))
|
||||
* 修复下载证书时提示token已过期的问题 ([0e07ae6](https://github.com/certd/certd/commit/0e07ae6ce84dcb9279d3c44060d621566afa593c))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 更新license时同时绑定url ([78367af](https://github.com/certd/certd/commit/78367af8307f801e778c76d49f0918c21ffe032f))
|
||||
* 切换到不同的分组后再打开创建对话框,会自动选择分组 ([893dcd4](https://github.com/certd/certd/commit/893dcd4f2487891199ed3e5a3d47a79a75efc942))
|
||||
* 新增部署到火山引擎ALB/CLB、上传到证书中心 ([c9a3e3d](https://github.com/certd/certd/commit/c9a3e3d9d26f964c7af7b56667936f1414fbf42a))
|
||||
* 优化/api缓存为0 ([dc05cd4](https://github.com/certd/certd/commit/dc05cd481f186b13375192be965000e6b4b429a5))
|
||||
* 优化华为cdn插件引用ccm证书 ([b565b4b](https://github.com/certd/certd/commit/b565b4b3b919b71b98ea2517670bc1ef00e00dc9))
|
||||
* 优化证书流水线创建,支持选择分组 ([d613aa8](https://github.com/certd/certd/commit/d613aa8f3e85d8dc475ef1b62d49394ce7fd7d24))
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 登录支持双重认证 ([48aef25](https://github.com/certd/certd/commit/48aef25b3f6499d674ca4e4ef16f4c62399fb735))
|
||||
* 多重认证登录 ([0f82cf4](https://github.com/certd/certd/commit/0f82cf409bc60706ab07e4ca4f272b9a1ca7eecb))
|
||||
* 优化部署到华为云CDN,支持先上传到ccm,再使用证书id部署,修复offline状态下导致部署报错的bug ([79df39a](https://github.com/certd/certd/commit/79df39acabab10ae7e1864dadcdc186bb007a3c5))
|
||||
|
||||
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
BIN
docs/guide/install/baota/images/network.png
Normal file
BIN
docs/guide/install/baota/images/network.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
@@ -13,7 +13,7 @@
|
||||
|
||||
#### 2.1 应用商店一键部署【推荐】
|
||||
|
||||
* 在应用商店中找到`certd`(要先点右上角更新应用)
|
||||
* 在宝塔Docker应用商店中找到`certd`(要先点右上角更新应用)
|
||||
* 点击安装,配置域名等基本信息即可完成安装
|
||||
|
||||
> 需要宝塔9.2.0及以上版本才支持
|
||||
@@ -70,3 +70,12 @@ admin/123456
|
||||
## 五、备份恢复
|
||||
|
||||
将备份的`db.sqlite`及同目录下的其他文件一起覆盖到原来的位置,重启certd即可
|
||||
|
||||
|
||||
## 六、宝塔部署相关问题排查
|
||||
|
||||
### 1. 无法访问Certd
|
||||
1. 确认服务器的安全规则,是否放开了对应端口
|
||||
2. 确认宝塔防火墙是否放开对应端口
|
||||
3. 尝试将Certd容器加入宝塔的`bridge`网络
|
||||

|
||||
@@ -55,6 +55,11 @@ https://your_server_ip:7002
|
||||
|
||||
## 二、升级
|
||||
|
||||
::: warning
|
||||
如果您是第一次升级certd版本,切记切记先备份一下数据
|
||||
:::
|
||||
|
||||
|
||||
### 如果使用固定版本号
|
||||
1. 修改`docker-compose.yaml`中的镜像版本号
|
||||
2. 运行`docker compose up -d` 即可
|
||||
|
||||
@@ -44,6 +44,11 @@ kill -9 $(lsof -t -i:7001)
|
||||
./start.sh
|
||||
```
|
||||
|
||||
::: warning
|
||||
升级certd版本前,切记切记先备份一下数据
|
||||
:::
|
||||
|
||||
|
||||
## 三、数据备份
|
||||
> 数据默认保存在 `./packages/ui/certd-server/data` 目录下
|
||||
> 建议配置一条[数据库备份流水线](../../use/backup/) 自动备份
|
||||
|
||||
@@ -8,5 +8,9 @@
|
||||
3. [1Panel面板方式部署升级](./1panel/#三、升级)
|
||||
4. [源码方式部署](./source/#二、升级)
|
||||
|
||||
::: warning
|
||||
如果您是第一次升级certd版本,切记切记先备份一下数据
|
||||
:::
|
||||
|
||||
## 升级日志
|
||||
[CHANGELOG](../changelogs/CHANGELOG.md)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# 数据库自动备份
|
||||
|
||||
# 数据库备份
|
||||
* 两种备份方法: 1、手动备份 2、自动备份
|
||||
* 本文仅限sqlite数据库。
|
||||
## 一、手动备份
|
||||
数据库文件根据不同的部署方式保存的位置不一样,您可以手动复制出来进行备份
|
||||
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
}
|
||||
},
|
||||
"npmClient": "pnpm",
|
||||
"version": "1.33.5"
|
||||
"version": "1.34.0"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,29 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/publishlab/node-acme-client/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.33.8](https://github.com/publishlab/node-acme-client/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复http上传方式无法清除记录文件的bug ([72a7b51](https://github.com/publishlab/node-acme-client/commit/72a7b51d479602b2c54c6c3ac8d8a0dcb9664e73))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 从域名的soa获取主域名,子域名托管无需额外配置 ([a586a92](https://github.com/publishlab/node-acme-client/commit/a586a92d5e32ea846ac37be52a7ad8c328d89966))
|
||||
* 七牛oss支持删除过期备份 ([b7113bd](https://github.com/publishlab/node-acme-client/commit/b7113bda2378116d6c116dc583f563cce7cf9f00))
|
||||
|
||||
## [1.33.7](https://github.com/publishlab/node-acme-client/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.33.6](https://github.com/publishlab/node-acme-client/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.33.5](https://github.com/publishlab/node-acme-client/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "Simple and unopinionated ACME client",
|
||||
"private": false,
|
||||
"author": "nmorsman",
|
||||
"version": "1.33.5",
|
||||
"version": "1.34.0",
|
||||
"type": "module",
|
||||
"module": "scr/index.js",
|
||||
"main": "src/index.js",
|
||||
@@ -18,7 +18,7 @@
|
||||
"types"
|
||||
],
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.33.5",
|
||||
"@certd/basic": "^1.34.0",
|
||||
"@peculiar/x509": "^1.11.0",
|
||||
"asn1js": "^3.0.5",
|
||||
"axios": "^1.7.2",
|
||||
@@ -26,7 +26,8 @@
|
||||
"http-proxy-agent": "^7.0.2",
|
||||
"https-proxy-agent": "^7.0.5",
|
||||
"lodash-es": "^4.17.21",
|
||||
"node-forge": "^1.3.1"
|
||||
"node-forge": "^1.3.1",
|
||||
"punycode": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.14.10",
|
||||
@@ -67,5 +68,5 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||
},
|
||||
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f"
|
||||
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||
}
|
||||
|
||||
@@ -117,12 +117,12 @@ export default async (client, userOpts) => {
|
||||
|
||||
log(`[auto] [${d}] Trigger challengeCreateFn()`);
|
||||
try {
|
||||
const { recordReq, recordRes, dnsProvider, challenge, keyAuthorization } = await opts.challengeCreateFn(authz, keyAuthorizationGetter);
|
||||
const { recordReq, recordRes, dnsProvider, challenge, keyAuthorization ,httpUploader} = await opts.challengeCreateFn(authz, keyAuthorizationGetter);
|
||||
clearTasks.push(async () => {
|
||||
/* Trigger challengeRemoveFn(), suppress errors */
|
||||
log(`[auto] [${d}] Trigger challengeRemoveFn()`);
|
||||
try {
|
||||
await opts.challengeRemoveFn(authz, challenge, keyAuthorization, recordReq, recordRes, dnsProvider);
|
||||
await opts.challengeRemoveFn(authz, challenge, keyAuthorization, recordReq, recordRes, dnsProvider,httpUploader);
|
||||
} catch (e) {
|
||||
log(`[auto] [${d}] challengeRemoveFn threw error: ${e.message}`);
|
||||
}
|
||||
|
||||
@@ -46,3 +46,5 @@ export * from './axios.js'
|
||||
export * from './logger.js'
|
||||
export * from './verify.js'
|
||||
export * from './error.js'
|
||||
|
||||
export * from './util.js'
|
||||
@@ -340,5 +340,6 @@ export {
|
||||
formatResponseError,
|
||||
getAuthoritativeDnsResolver,
|
||||
retrieveTlsAlpnCertificate,
|
||||
resolveDomainBySoaRecord
|
||||
};
|
||||
|
||||
|
||||
6
packages/core/acme-client/types/index.d.ts
vendored
6
packages/core/acme-client/types/index.d.ts
vendored
@@ -59,7 +59,7 @@ export interface ClientExternalAccountBindingOptions {
|
||||
export interface ClientAutoOptions {
|
||||
csr: CsrBuffer | CsrString;
|
||||
challengeCreateFn: (authz: Authorization, keyAuthorization: (challenge:rfc8555.Challenge)=>Promise<string>) => Promise<{recordReq?:any,recordRes?:any,dnsProvider?:any,challenge: rfc8555.Challenge,keyAuthorization:string}>;
|
||||
challengeRemoveFn: (authz: Authorization, challenge: rfc8555.Challenge, keyAuthorization: string,recordReq:any, recordRes:any,dnsProvider:any) => Promise<any>;
|
||||
challengeRemoveFn: (authz: Authorization, challenge: rfc8555.Challenge, keyAuthorization: string,recordReq:any, recordRes:any,dnsProvider:any,httpUploader:any) => Promise<any>;
|
||||
email?: string;
|
||||
termsOfServiceAgreed?: boolean;
|
||||
skipChallengeVerification?: boolean;
|
||||
@@ -204,4 +204,6 @@ export function setLogger(fn: (message: any, ...args: any[]) => void): void;
|
||||
|
||||
export function walkTxtRecord(record: any): Promise<string[]>;
|
||||
|
||||
export const CancelError: typeof CancelError;
|
||||
export const CancelError: typeof CancelError;
|
||||
|
||||
export function resolveDomainBySoaRecord(domain: string): Promise<string>;
|
||||
@@ -1,137 +0,0 @@
|
||||
"use strict";
|
||||
/**
|
||||
* acme-client type definition tests
|
||||
*/
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var acme = require("acme-client");
|
||||
(function () { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var accountKey, client, order, authorizations, authorization, challenge, _a, certKey, certCsr;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0: return [4 /*yield*/, acme.crypto.createPrivateKey()];
|
||||
case 1:
|
||||
accountKey = _b.sent();
|
||||
client = new acme.Client({
|
||||
accountKey: accountKey,
|
||||
directoryUrl: acme.directory.letsencrypt.staging
|
||||
});
|
||||
/* Account */
|
||||
return [4 /*yield*/, client.createAccount({
|
||||
termsOfServiceAgreed: true,
|
||||
contact: ['mailto:test@example.com']
|
||||
})];
|
||||
case 2:
|
||||
/* Account */
|
||||
_b.sent();
|
||||
return [4 /*yield*/, client.createOrder({
|
||||
identifiers: [
|
||||
{ type: 'dns', value: 'example.com' },
|
||||
{ type: 'dns', value: '*.example.com' },
|
||||
]
|
||||
})];
|
||||
case 3:
|
||||
order = _b.sent();
|
||||
return [4 /*yield*/, client.getOrder(order)];
|
||||
case 4:
|
||||
_b.sent();
|
||||
return [4 /*yield*/, client.getAuthorizations(order)];
|
||||
case 5:
|
||||
authorizations = _b.sent();
|
||||
authorization = authorizations[0];
|
||||
challenge = authorization.challenges[0];
|
||||
return [4 /*yield*/, client.getChallengeKeyAuthorization(challenge)];
|
||||
case 6:
|
||||
_b.sent();
|
||||
return [4 /*yield*/, client.verifyChallenge(authorization, challenge)];
|
||||
case 7:
|
||||
_b.sent();
|
||||
return [4 /*yield*/, client.completeChallenge(challenge)];
|
||||
case 8:
|
||||
_b.sent();
|
||||
return [4 /*yield*/, client.waitForValidStatus(challenge)];
|
||||
case 9:
|
||||
_b.sent();
|
||||
return [4 /*yield*/, acme.crypto.createCsr({
|
||||
commonName: 'example.com',
|
||||
altNames: ['example.com', '*.example.com']
|
||||
})];
|
||||
case 10:
|
||||
_a = _b.sent(), certKey = _a[0], certCsr = _a[1];
|
||||
return [4 /*yield*/, client.finalizeOrder(order, certCsr)];
|
||||
case 11:
|
||||
_b.sent();
|
||||
return [4 /*yield*/, client.getCertificate(order)];
|
||||
case 12:
|
||||
_b.sent();
|
||||
return [4 /*yield*/, client.getCertificate(order, 'DST Root CA X3')];
|
||||
case 13:
|
||||
_b.sent();
|
||||
/* Auto */
|
||||
return [4 /*yield*/, client.auto({
|
||||
csr: certCsr,
|
||||
challengeCreateFn: function (authz, challenge, keyAuthorization) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
||||
return [2 /*return*/];
|
||||
}); }); },
|
||||
challengeRemoveFn: function (authz, challenge, keyAuthorization) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
||||
return [2 /*return*/];
|
||||
}); }); }
|
||||
})];
|
||||
case 14:
|
||||
/* Auto */
|
||||
_b.sent();
|
||||
return [4 /*yield*/, client.auto({
|
||||
csr: certCsr,
|
||||
email: 'test@example.com',
|
||||
termsOfServiceAgreed: false,
|
||||
skipChallengeVerification: false,
|
||||
challengePriority: ['http-01', 'dns-01'],
|
||||
preferredChain: 'DST Root CA X3',
|
||||
challengeCreateFn: function (authz, challenge, keyAuthorization) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
||||
return [2 /*return*/];
|
||||
}); }); },
|
||||
challengeRemoveFn: function (authz, challenge, keyAuthorization) { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
|
||||
return [2 /*return*/];
|
||||
}); }); }
|
||||
})];
|
||||
case 15:
|
||||
_b.sent();
|
||||
return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
}); })();
|
||||
@@ -3,6 +3,24 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 支持51dns ([96a0900](https://github.com/certd/certd/commit/96a0900edc95dcfd9acccf9d13592f12f5a09b3d))
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -1 +1 @@
|
||||
23:32
|
||||
17:24
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/basic",
|
||||
"private": false,
|
||||
"version": "1.33.5",
|
||||
"version": "1.34.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -44,5 +44,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f"
|
||||
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import axios, { AxiosHeaders, AxiosRequestConfig } from 'axios';
|
||||
import { ILogger, logger } from './util.log.js';
|
||||
import { Logger } from 'log4js';
|
||||
import { HttpProxyAgent } from 'http-proxy-agent';
|
||||
import { HttpsProxyAgent } from 'https-proxy-agent';
|
||||
import nodeHttp from 'http';
|
||||
import * as https from 'node:https';
|
||||
import { merge } from 'lodash-es';
|
||||
import { safePromise } from './util.promise.js';
|
||||
import fs from 'fs';
|
||||
import axios, { AxiosHeaders, AxiosRequestConfig } from "axios";
|
||||
import { ILogger, logger } from "./util.log.js";
|
||||
import { Logger } from "log4js";
|
||||
import { HttpProxyAgent } from "http-proxy-agent";
|
||||
import { HttpsProxyAgent } from "https-proxy-agent";
|
||||
import nodeHttp from "http";
|
||||
import * as https from "node:https";
|
||||
import { merge } from "lodash-es";
|
||||
import { safePromise } from "./util.promise.js";
|
||||
import fs from "fs";
|
||||
export class HttpError extends Error {
|
||||
status?: number;
|
||||
statusText?: string;
|
||||
@@ -22,10 +22,10 @@ export class HttpError extends Error {
|
||||
super(error.message || error.response?.statusText);
|
||||
|
||||
const message = error?.message;
|
||||
if (message && typeof message === 'string') {
|
||||
if (message.indexOf && message.indexOf('ssl3_get_record:wrong version number') >= 0) {
|
||||
if (message && typeof message === "string") {
|
||||
if (message.indexOf && message.indexOf("ssl3_get_record:wrong version number") >= 0) {
|
||||
this.message = `${message}(http协议错误,服务端要求http协议,请检查是否使用了https请求)`;
|
||||
} else if (message.indexOf('getaddrinfo EAI_AGAIN') >= 0) {
|
||||
} else if (message.indexOf("getaddrinfo EAI_AGAIN") >= 0) {
|
||||
this.message = `${message}(无法解析域名,请检查网络连接或dns配置,更换docker-compose.yaml中dns配置)`;
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ export class HttpError extends Error {
|
||||
};
|
||||
let url = error.config?.url;
|
||||
if (error.config?.baseURL) {
|
||||
url = (error.config?.baseURL || '') + url;
|
||||
url = (error.config?.baseURL || "") + url;
|
||||
}
|
||||
if (url) {
|
||||
this.message = `${this.message} 【${url}】`;
|
||||
@@ -73,7 +73,7 @@ export const HttpCommonError = HttpError;
|
||||
let defaultAgents = createAgent();
|
||||
|
||||
export function setGlobalProxy(opts: { httpProxy?: string; httpsProxy?: string }) {
|
||||
logger.info('setGlobalProxy:', opts);
|
||||
logger.info("setGlobalProxy:", opts);
|
||||
defaultAgents = createAgent(opts);
|
||||
}
|
||||
|
||||
@@ -102,12 +102,12 @@ export function createAxiosService({ logger }: { logger: Logger }) {
|
||||
if (config.skipSslVerify || config.httpProxy) {
|
||||
let rejectUnauthorized = true;
|
||||
if (config.skipSslVerify) {
|
||||
logger.info('跳过SSL验证');
|
||||
logger.info("跳过SSL验证");
|
||||
rejectUnauthorized = false;
|
||||
}
|
||||
const proxy: any = {};
|
||||
if (config.httpProxy) {
|
||||
logger.info('使用自定义http代理:', config.httpProxy);
|
||||
logger.info("使用自定义http代理:", config.httpProxy);
|
||||
proxy.httpProxy = config.httpProxy;
|
||||
proxy.httpsProxy = config.httpProxy;
|
||||
}
|
||||
@@ -128,7 +128,7 @@ export function createAxiosService({ logger }: { logger: Logger }) {
|
||||
},
|
||||
(error: Error) => {
|
||||
// 发送失败
|
||||
logger.error('接口请求失败:', error);
|
||||
logger.error("接口请求失败:", error);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
@@ -143,7 +143,7 @@ export function createAxiosService({ logger }: { logger: Logger }) {
|
||||
|
||||
logger.info(`http response : status=${response?.status},data=${resData}`);
|
||||
} else {
|
||||
logger.info('http response status:', response?.status);
|
||||
logger.info("http response status:", response?.status);
|
||||
}
|
||||
if (response?.config?.returnResponse) {
|
||||
return response;
|
||||
@@ -154,53 +154,51 @@ export function createAxiosService({ logger }: { logger: Logger }) {
|
||||
const status = error.response?.status;
|
||||
switch (status) {
|
||||
case 400:
|
||||
error.message = '请求错误';
|
||||
error.message = "请求错误";
|
||||
break;
|
||||
case 401:
|
||||
error.message = '认证/登录失败';
|
||||
error.message = "认证/登录失败";
|
||||
break;
|
||||
case 403:
|
||||
error.message = '拒绝访问';
|
||||
error.message = "拒绝访问";
|
||||
break;
|
||||
case 404:
|
||||
error.message = `请求地址出错`;
|
||||
break;
|
||||
case 408:
|
||||
error.message = '请求超时';
|
||||
error.message = "请求超时";
|
||||
break;
|
||||
case 500:
|
||||
error.message = '服务器内部错误';
|
||||
error.message = "服务器内部错误";
|
||||
break;
|
||||
case 501:
|
||||
error.message = '服务未实现';
|
||||
error.message = "服务未实现";
|
||||
break;
|
||||
case 502:
|
||||
error.message = '网关错误';
|
||||
error.message = "网关错误";
|
||||
break;
|
||||
case 503:
|
||||
error.message = '服务不可用';
|
||||
error.message = "服务不可用";
|
||||
break;
|
||||
case 504:
|
||||
error.message = '网关超时';
|
||||
error.message = "网关超时";
|
||||
break;
|
||||
case 505:
|
||||
error.message = 'HTTP版本不受支持';
|
||||
error.message = "HTTP版本不受支持";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
logger.error(
|
||||
`请求出错:status:${error.response?.status},statusText:${error.response?.statusText},url:${error.config?.url},method:${error.config?.method}。`
|
||||
);
|
||||
logger.error('返回数据:', JSON.stringify(error.response?.data));
|
||||
logger.error(`请求出错:status:${error.response?.status},statusText:${error.response?.statusText},url:${error.config?.url},method:${error.config?.method}。`);
|
||||
logger.error("返回数据:", JSON.stringify(error.response?.data));
|
||||
if (error.response?.data) {
|
||||
const message = error.response.data.message || error.response.data.msg || error.response.data.error;
|
||||
if (typeof message === 'string') {
|
||||
if (typeof message === "string") {
|
||||
error.message = message;
|
||||
}
|
||||
}
|
||||
if (error instanceof AggregateError) {
|
||||
logger.error('AggregateError', error);
|
||||
logger.error("AggregateError", error);
|
||||
}
|
||||
const err = new HttpError(error);
|
||||
return Promise.reject(err);
|
||||
@@ -244,24 +242,24 @@ export function createAgent(opts: CreateAgentOptions = {}) {
|
||||
if (httpProxy) {
|
||||
process.env.HTTP_PROXY = httpProxy;
|
||||
process.env.http_proxy = httpProxy;
|
||||
logger.info('use httpProxy:', httpProxy);
|
||||
logger.info("use httpProxy:", httpProxy);
|
||||
httpAgent = new HttpProxyAgent(httpProxy, opts as any);
|
||||
merge(httpAgent.options, opts);
|
||||
} else {
|
||||
process.env.HTTP_PROXY = '';
|
||||
process.env.http_proxy = '';
|
||||
process.env.HTTP_PROXY = "";
|
||||
process.env.http_proxy = "";
|
||||
httpAgent = new nodeHttp.Agent(opts);
|
||||
}
|
||||
const httpsProxy = opts.httpsProxy;
|
||||
if (httpsProxy) {
|
||||
process.env.HTTPS_PROXY = httpsProxy;
|
||||
process.env.https_proxy = httpsProxy;
|
||||
logger.info('use httpsProxy:', httpsProxy);
|
||||
logger.info("use httpsProxy:", httpsProxy);
|
||||
httpsAgent = new HttpsProxyAgent(httpsProxy, opts as any);
|
||||
merge(httpsAgent.options, opts);
|
||||
} else {
|
||||
process.env.HTTPS_PROXY = '';
|
||||
process.env.https_proxy = '';
|
||||
process.env.HTTPS_PROXY = "";
|
||||
process.env.https_proxy = "";
|
||||
httpsAgent = new https.Agent(opts);
|
||||
}
|
||||
return {
|
||||
@@ -276,27 +274,27 @@ export async function download(req: { http: HttpClient; config: HttpRequestConfi
|
||||
http
|
||||
.request({
|
||||
logRes: false,
|
||||
responseType: 'stream',
|
||||
responseType: "stream",
|
||||
...config,
|
||||
})
|
||||
.then(res => {
|
||||
const writer = fs.createWriteStream(savePath);
|
||||
res.pipe(writer);
|
||||
writer.on('close', () => {
|
||||
logger.info('文件下载成功');
|
||||
writer.on("close", () => {
|
||||
logger.info("文件下载成功");
|
||||
resolve(true);
|
||||
});
|
||||
//error
|
||||
writer.on('error', err => {
|
||||
logger.error('下载失败', err);
|
||||
writer.on("error", err => {
|
||||
logger.error("下载失败", err);
|
||||
reject(err);
|
||||
});
|
||||
//进度条打印
|
||||
const totalLength = res.headers['content-length'];
|
||||
const totalLength = res.headers["content-length"];
|
||||
let currentLength = 0;
|
||||
// 每5%打印一次
|
||||
const step = (totalLength / 100) * 5;
|
||||
res.on('data', (chunk: any) => {
|
||||
res.on("data", (chunk: any) => {
|
||||
currentLength += chunk.length;
|
||||
if (currentLength % step < chunk.length) {
|
||||
const percent = ((currentLength / totalLength) * 100).toFixed(2);
|
||||
@@ -305,19 +303,19 @@ export async function download(req: { http: HttpClient; config: HttpRequestConfi
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
logger.info('下载失败', err);
|
||||
logger.info("下载失败", err);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function getCookie(response: any, name: string) {
|
||||
const cookies = response.headers['set-cookie'];
|
||||
const cookies = response.headers["set-cookie"];
|
||||
//根据name 返回对应的cookie
|
||||
const found = cookies.find((cookie: any) => cookie.includes(name));
|
||||
if (!found) {
|
||||
return null;
|
||||
}
|
||||
const cookie = found.split(';')[0];
|
||||
return cookie.substring(cookie.indexOf('=') + 1);
|
||||
const cookie = found.split(";")[0];
|
||||
return cookie.substring(cookie.indexOf("=") + 1);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/pipeline",
|
||||
"private": false,
|
||||
"version": "1.33.5",
|
||||
"version": "1.34.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -16,8 +16,8 @@
|
||||
"test": "mocha --loader=ts-node/esm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.33.5",
|
||||
"@certd/plus-core": "^1.33.5",
|
||||
"@certd/basic": "^1.34.0",
|
||||
"@certd/plus-core": "^1.34.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"lodash-es": "^4.17.21",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
@@ -43,5 +43,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f"
|
||||
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-huawei",
|
||||
"private": false,
|
||||
"version": "1.33.5",
|
||||
"version": "1.34.0",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
"types": "./dist/d/index.d.ts",
|
||||
@@ -23,5 +23,5 @@
|
||||
"prettier": "^2.8.8",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f"
|
||||
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 更新license时同时绑定url ([78367af](https://github.com/certd/certd/commit/78367af8307f801e778c76d49f0918c21ffe032f))
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-iframe",
|
||||
"private": false,
|
||||
"version": "1.33.5",
|
||||
"version": "1.34.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -30,5 +30,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f"
|
||||
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export class IframeClient {
|
||||
return window.self !== window.top;
|
||||
}
|
||||
|
||||
register<T = any>(action: string, handler: (data: IframeMessageData<T>) => Promise<void>) {
|
||||
register<T = any>(action: string, handler: (data: IframeMessageData<T>) => Promise<any>) {
|
||||
this.handlers[action] = handler;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/jdcloud",
|
||||
"version": "1.33.5",
|
||||
"version": "1.34.0",
|
||||
"description": "jdcloud openApi sdk",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
@@ -60,5 +60,5 @@
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f"
|
||||
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-k8s",
|
||||
"private": false,
|
||||
"version": "1.33.5",
|
||||
"version": "1.34.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -16,7 +16,7 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.33.5",
|
||||
"@certd/basic": "^1.34.0",
|
||||
"@kubernetes/client-node": "0.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -31,5 +31,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f"
|
||||
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/lib-server",
|
||||
"version": "1.33.5",
|
||||
"version": "1.34.0",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -27,10 +27,10 @@
|
||||
],
|
||||
"license": "AGPL",
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.33.5",
|
||||
"@certd/basic": "^1.33.5",
|
||||
"@certd/pipeline": "^1.33.5",
|
||||
"@certd/plus-core": "^1.33.5",
|
||||
"@certd/acme-client": "^1.34.0",
|
||||
"@certd/basic": "^1.34.0",
|
||||
"@certd/pipeline": "^1.34.0",
|
||||
"@certd/plus-core": "^1.34.0",
|
||||
"@midwayjs/cache": "~3.14.0",
|
||||
"@midwayjs/core": "~3.20.3",
|
||||
"@midwayjs/i18n": "~3.20.3",
|
||||
@@ -61,5 +61,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f"
|
||||
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/midway-flyway-js",
|
||||
"version": "1.33.5",
|
||||
"version": "1.34.0",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -46,5 +46,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f"
|
||||
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,31 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复http上传方式无法清除记录文件的bug ([72a7b51](https://github.com/certd/certd/commit/72a7b51d479602b2c54c6c3ac8d8a0dcb9664e73))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 从域名的soa获取主域名,子域名托管无需额外配置 ([a586a92](https://github.com/certd/certd/commit/a586a92d5e32ea846ac37be52a7ad8c328d89966))
|
||||
* 数据库备份支持oss ([308d460](https://github.com/certd/certd/commit/308d4600efe2002f199c33b4594d3071784e58ea))
|
||||
* 支持阿里云中文域名申请 ([b3468cf](https://github.com/certd/certd/commit/b3468cf7f28228d7c9cf68de6b5a9bbeb67f2c6d))
|
||||
* 支持中文域名 ([162ebfd](https://github.com/certd/certd/commit/162ebfd4e0c25727efb33952d3bbf7420a02e2c3))
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-cert",
|
||||
"private": false,
|
||||
"version": "1.33.5",
|
||||
"version": "1.34.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -15,15 +15,16 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.33.5",
|
||||
"@certd/basic": "^1.33.5",
|
||||
"@certd/pipeline": "^1.33.5",
|
||||
"@certd/plugin-lib": "^1.33.5",
|
||||
"@certd/acme-client": "^1.34.0",
|
||||
"@certd/basic": "^1.34.0",
|
||||
"@certd/pipeline": "^1.34.0",
|
||||
"@certd/plugin-lib": "^1.34.0",
|
||||
"@google-cloud/publicca": "^1.3.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"jszip": "^3.10.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"psl": "^1.9.0",
|
||||
"punycode": "^2.3.1",
|
||||
"rimraf": "^5.0.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -41,5 +42,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f"
|
||||
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ export interface IDnsProvider<T = any> {
|
||||
createRecord(options: CreateRecordOptions): Promise<T>;
|
||||
removeRecord(options: RemoveRecordOptions<T>): Promise<void>;
|
||||
setCtx(ctx: DnsProviderContext): void;
|
||||
//中文域名是否需要punycode转码,如果返回True,则使用punycode来添加解析记录,否则使用中文域名添加解析记录
|
||||
usePunyCode(): boolean;
|
||||
}
|
||||
|
||||
export interface ISubDomainsGetter {
|
||||
|
||||
@@ -8,6 +8,10 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
||||
http!: HttpClient;
|
||||
logger!: ILogger;
|
||||
|
||||
usePunyCode(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
setCtx(ctx: DnsProviderContext) {
|
||||
this.ctx = ctx;
|
||||
this.logger = ctx.logger;
|
||||
@@ -28,7 +32,7 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
||||
export async function createDnsProvider(opts: { dnsProviderType: string; context: DnsProviderContext }): Promise<IDnsProvider> {
|
||||
const { dnsProviderType, context } = opts;
|
||||
const dnsProviderPlugin = dnsProviderRegistry.get(dnsProviderType);
|
||||
const DnsProviderClass = dnsProviderPlugin.target;
|
||||
const DnsProviderClass = await dnsProviderPlugin.target();
|
||||
const dnsProviderDefine = dnsProviderPlugin.define as DnsProviderDefine;
|
||||
if (dnsProviderDefine.deprecated) {
|
||||
context.logger.warn(dnsProviderDefine.deprecated);
|
||||
|
||||
@@ -24,7 +24,9 @@ export function IsDnsProvider(define: DnsProviderDefine): ClassDecorator {
|
||||
target.define = define;
|
||||
dnsProviderRegistry.register(define.name, {
|
||||
define,
|
||||
target,
|
||||
target: async () => {
|
||||
return target;
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { IDomainParser, ISubDomainsGetter } from "./api";
|
||||
//@ts-ignore
|
||||
import psl from "psl";
|
||||
import { resolveDomainBySoaRecord } from "@certd/acme-client";
|
||||
import { logger, utils } from "@certd/basic";
|
||||
|
||||
export class DomainParser implements IDomainParser {
|
||||
subDomainsGetter: ISubDomainsGetter;
|
||||
@@ -17,16 +19,38 @@ export class DomainParser implements IDomainParser {
|
||||
}
|
||||
|
||||
async parse(fullDomain: string) {
|
||||
const subDomains = await this.subDomainsGetter.getSubDomains();
|
||||
if (subDomains && subDomains.length > 0) {
|
||||
for (const subDomain of subDomains) {
|
||||
if (fullDomain.endsWith(subDomain)) {
|
||||
//找到子域名托管
|
||||
return subDomain;
|
||||
}
|
||||
logger.info(`查找主域名:${fullDomain}`);
|
||||
const cacheKey = `domain_parse:${fullDomain}`;
|
||||
const value = utils.cache.get(cacheKey);
|
||||
if (value) {
|
||||
logger.info(`从缓存获取到主域名:${fullDomain}->${value}`);
|
||||
return value;
|
||||
}
|
||||
try {
|
||||
const mainDomain = await resolveDomainBySoaRecord(fullDomain);
|
||||
if (mainDomain) {
|
||||
utils.cache.set(cacheKey, mainDomain, {
|
||||
ttl: 2 * 60 * 1000,
|
||||
});
|
||||
logger.info(`获取到主域名:${fullDomain}->${mainDomain}`);
|
||||
return mainDomain;
|
||||
}
|
||||
} catch (e) {
|
||||
logger.error("从SOA获取主域名失败", e.message);
|
||||
}
|
||||
|
||||
return this.parseDomain(fullDomain);
|
||||
// const subDomains = await this.subDomainsGetter.getSubDomains();
|
||||
// if (subDomains && subDomains.length > 0) {
|
||||
// for (const subDomain of subDomains) {
|
||||
// if (fullDomain.endsWith(subDomain)) {
|
||||
// //找到子域名托管
|
||||
// return subDomain;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
const res = this.parseDomain(fullDomain);
|
||||
logger.info(`从psl获取主域名:${fullDomain}->${res}`);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ import { Challenge } from "@certd/acme-client/types/rfc8555";
|
||||
import { IContext } from "@certd/pipeline";
|
||||
import { ILogger, utils } from "@certd/basic";
|
||||
import { IDnsProvider, IDomainParser } from "../../dns-provider/index.js";
|
||||
import { HttpChallengeUploader } from "./uploads/api.js";
|
||||
|
||||
import punycode from "node:punycode";
|
||||
import { IOssClient } from "@certd/plugin-lib";
|
||||
export type CnameVerifyPlan = {
|
||||
type?: string;
|
||||
domain: string;
|
||||
@@ -18,7 +18,7 @@ export type CnameVerifyPlan = {
|
||||
export type HttpVerifyPlan = {
|
||||
type: string;
|
||||
domain: string;
|
||||
httpUploader: HttpChallengeUploader;
|
||||
httpUploader: IOssClient;
|
||||
};
|
||||
|
||||
export type DomainVerifyPlan = {
|
||||
@@ -35,7 +35,6 @@ export type DomainsVerifyPlan = {
|
||||
export type Providers = {
|
||||
dnsProvider?: IDnsProvider;
|
||||
domainsVerifyPlan?: DomainsVerifyPlan;
|
||||
httpUploader?: HttpChallengeUploader;
|
||||
};
|
||||
|
||||
export type CertInfo = {
|
||||
@@ -184,7 +183,7 @@ export class AcmeService {
|
||||
return authz.challenges.find((c: any) => c.type === type);
|
||||
};
|
||||
|
||||
const doHttpVerify = async (challenge: any, httpUploader: HttpChallengeUploader) => {
|
||||
const doHttpVerify = async (challenge: any, httpUploader: IOssClient) => {
|
||||
const keyAuthorization = await keyAuthorizationGetter(challenge);
|
||||
this.logger.info("http校验");
|
||||
const filePath = `.well-known/acme-challenge/${challenge.token}`;
|
||||
@@ -203,14 +202,16 @@ export class AcmeService {
|
||||
this.logger.info("dns校验");
|
||||
const keyAuthorization = await keyAuthorizationGetter(challenge);
|
||||
|
||||
const mainDomain = dnsProvider.usePunyCode() ? domain : punycode.toUnicode(domain);
|
||||
fullRecord = dnsProvider.usePunyCode() ? fullRecord : punycode.toUnicode(fullRecord);
|
||||
const recordValue = keyAuthorization;
|
||||
let hostRecord = fullRecord.replace(`${domain}`, "");
|
||||
let hostRecord = fullRecord.replace(`${mainDomain}`, "");
|
||||
if (hostRecord.endsWith(".")) {
|
||||
hostRecord = hostRecord.substring(0, hostRecord.length - 1);
|
||||
}
|
||||
|
||||
const recordReq = {
|
||||
domain,
|
||||
domain: mainDomain,
|
||||
fullRecord,
|
||||
hostRecord,
|
||||
type: "TXT",
|
||||
@@ -286,7 +287,7 @@ export class AcmeService {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
async challengeRemoveFn(authz: any, challenge: any, keyAuthorization: string, recordReq: any, recordRes: any, dnsProvider?: IDnsProvider, httpUploader?: HttpChallengeUploader) {
|
||||
async challengeRemoveFn(authz: any, challenge: any, keyAuthorization: string, recordReq: any, recordRes: any, dnsProvider?: IDnsProvider, httpUploader?: IOssClient) {
|
||||
this.logger.info("执行清理");
|
||||
|
||||
/* http-01 */
|
||||
@@ -321,9 +322,16 @@ export class AcmeService {
|
||||
isTest?: boolean;
|
||||
privateKeyType?: string;
|
||||
}): Promise<CertInfo> {
|
||||
const { email, isTest, domains, csrInfo, dnsProvider, domainsVerifyPlan, httpUploader } = options;
|
||||
const { email, isTest, csrInfo, dnsProvider, domainsVerifyPlan } = options;
|
||||
const client: acme.Client = await this.getAcmeClient(email, isTest);
|
||||
|
||||
let domains = options.domains;
|
||||
const encodingDomains = [];
|
||||
for (const domain of domains) {
|
||||
encodingDomains.push(punycode.toASCII(domain));
|
||||
}
|
||||
domains = encodingDomains;
|
||||
|
||||
/* Create CSR */
|
||||
const { commonName, altNames } = this.buildCommonNameByDomains(domains);
|
||||
let privateKey = null;
|
||||
@@ -361,14 +369,13 @@ export class AcmeService {
|
||||
privateKey
|
||||
);
|
||||
|
||||
if (dnsProvider == null && domainsVerifyPlan == null && httpUploader == null) {
|
||||
throw new Error("dnsProvider 、 domainsVerifyPlan 、 httpUploader不能都为空");
|
||||
if (dnsProvider == null && domainsVerifyPlan == null) {
|
||||
throw new Error("dnsProvider 、 domainsVerifyPlan不能都为空");
|
||||
}
|
||||
|
||||
const providers: Providers = {
|
||||
dnsProvider,
|
||||
domainsVerifyPlan,
|
||||
httpUploader,
|
||||
};
|
||||
/* 自动申请证书 */
|
||||
const crt = await client.auto({
|
||||
@@ -383,7 +390,7 @@ export class AcmeService {
|
||||
): Promise<{ recordReq?: any; recordRes?: any; dnsProvider?: any; challenge: Challenge; keyAuthorization: string }> => {
|
||||
return await this.challengeCreateFn(authz, keyAuthorizationGetter, providers);
|
||||
},
|
||||
challengeRemoveFn: async (authz: acme.Authorization, challenge: Challenge, keyAuthorization: string, recordReq: any, recordRes: any, dnsProvider: IDnsProvider): Promise<any> => {
|
||||
challengeRemoveFn: async (authz: acme.Authorization, challenge: Challenge, keyAuthorization: string, recordReq: any, recordRes: any, dnsProvider: IDnsProvider, httpUploader: IOssClient): Promise<any> => {
|
||||
return await this.challengeRemoveFn(authz, challenge, keyAuthorization, recordReq, recordRes, dnsProvider, httpUploader);
|
||||
},
|
||||
signal: this.options.signal,
|
||||
|
||||
@@ -27,8 +27,7 @@ export abstract class CertApplyBaseConvertPlugin extends AbstractTaskPlugin {
|
||||
"1、支持多个域名打到一个证书上,例如: foo.com,*.foo.com,*.bar.com\n" +
|
||||
"2、子域名被通配符包含的不要填写,例如:www.foo.com已经被*.foo.com包含,不要填写www.foo.com\n" +
|
||||
"3、泛域名只能通配*号那一级(*.foo.com的证书不能用于xxx.yyy.foo.com、不能用于foo.com)\n" +
|
||||
"4、输入一个,空格之后,再输入下一个\n" +
|
||||
"5、如果你配置了子域托管解析,请先[设置托管子域名](#/certd/pipeline/subDomain)",
|
||||
"4、输入一个,空格之后,再输入下一个",
|
||||
})
|
||||
domains!: string[];
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ import { CertReader } from "./cert-reader.js";
|
||||
import { CertApplyBasePlugin } from "./base.js";
|
||||
import { GoogleClient } from "../../libs/google.js";
|
||||
import { EabAccess } from "../../access";
|
||||
import { httpChallengeUploaderFactory } from "./uploads/factory.js";
|
||||
import { DomainParser } from "../../dns-provider/domain-parser.js";
|
||||
import { ossClientFactory } from "@certd/plugin-lib";
|
||||
export * from "./base.js";
|
||||
export type { CertInfo };
|
||||
export * from "./cert-reader.js";
|
||||
@@ -115,6 +115,7 @@ HTTP文件验证:不支持泛域名,需要配置网站文件上传`,
|
||||
})
|
||||
dnsProviderType!: string;
|
||||
|
||||
// dns解析授权类型,勿删
|
||||
dnsProviderAccessType!: string;
|
||||
|
||||
@TaskInput({
|
||||
@@ -446,7 +447,7 @@ HTTP文件验证:不支持泛域名,需要配置网站文件上传`,
|
||||
rootDir = rootDir + "/";
|
||||
}
|
||||
this.logger.info("上传方式", httpRecord.httpUploaderType);
|
||||
const httpUploader = await httpChallengeUploaderFactory.createUploaderByType(httpRecord.httpUploaderType, {
|
||||
const httpUploader = await ossClientFactory.createOssClientByType(httpRecord.httpUploaderType, {
|
||||
access,
|
||||
rootDir: rootDir,
|
||||
ctx: httpUploaderContext,
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
import { IAccessService } from "@certd/pipeline";
|
||||
import { ILogger, utils } from "@certd/basic";
|
||||
|
||||
export type HttpChallengeUploader = {
|
||||
upload: (fileName: string, fileContent: Buffer) => Promise<void>;
|
||||
remove: (fileName: string) => Promise<void>;
|
||||
};
|
||||
|
||||
export type HttpChallengeUploadContext = {
|
||||
accessService: IAccessService;
|
||||
logger: ILogger;
|
||||
utils: typeof utils;
|
||||
};
|
||||
|
||||
export abstract class BaseHttpChallengeUploader<A> implements HttpChallengeUploader {
|
||||
rootDir: string;
|
||||
access: A = null;
|
||||
logger: ILogger;
|
||||
utils: typeof utils;
|
||||
ctx: HttpChallengeUploadContext;
|
||||
protected constructor(opts: { rootDir: string; access: A }) {
|
||||
this.rootDir = opts.rootDir;
|
||||
this.access = opts.access;
|
||||
}
|
||||
|
||||
async setCtx(ctx: any) {
|
||||
// set context
|
||||
this.ctx = ctx;
|
||||
this.logger = ctx.logger;
|
||||
this.utils = ctx.utils;
|
||||
}
|
||||
|
||||
abstract remove(fileName: string): Promise<void>;
|
||||
abstract upload(fileName: string, fileContent: Buffer): Promise<void>;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { BaseHttpChallengeUploader } from "../api.js";
|
||||
import { AliossAccess, AliyunAccess } from "@certd/plugin-lib";
|
||||
import { AliossClient } from "@certd/plugin-lib";
|
||||
|
||||
export class AliossHttpChallengeUploader extends BaseHttpChallengeUploader<AliossAccess> {
|
||||
async upload(filePath: string, fileContent: Buffer) {
|
||||
const aliyunAccess = await this.ctx.accessService.getById<AliyunAccess>(this.access.accessId);
|
||||
const client = new AliossClient({
|
||||
access: aliyunAccess,
|
||||
bucket: this.access.bucket,
|
||||
region: this.access.region,
|
||||
});
|
||||
|
||||
const key = this.rootDir + filePath;
|
||||
this.logger.info(`开始上传文件: ${key}`);
|
||||
await client.uploadFile(key, fileContent);
|
||||
|
||||
this.logger.info(`校验文件上传成功: ${filePath}`);
|
||||
}
|
||||
|
||||
async remove(filePath: string) {
|
||||
const key = this.rootDir + filePath;
|
||||
// remove file from alioss
|
||||
const client = await this.getAliossClient();
|
||||
await client.removeFile(key);
|
||||
this.logger.info(`文件删除成功: ${key}`);
|
||||
}
|
||||
|
||||
private async getAliossClient() {
|
||||
const aliyunAccess = await this.ctx.accessService.getById<AliyunAccess>(this.access.accessId);
|
||||
const client = new AliossClient({
|
||||
access: aliyunAccess,
|
||||
bucket: this.access.bucket,
|
||||
region: this.access.region,
|
||||
});
|
||||
await client.init();
|
||||
return client;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { BaseHttpChallengeUploader } from "../api.js";
|
||||
import { FtpAccess, FtpClient } from "@certd/plugin-lib";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
import fs from "fs";
|
||||
|
||||
export class FtpHttpChallengeUploader extends BaseHttpChallengeUploader<FtpAccess> {
|
||||
async upload(filePath: string, fileContent: Buffer) {
|
||||
const client = new FtpClient({
|
||||
access: this.access,
|
||||
logger: this.logger,
|
||||
});
|
||||
await client.connect(async (client) => {
|
||||
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
|
||||
const dir = path.dirname(tmpFilePath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(tmpFilePath, fileContent);
|
||||
try {
|
||||
// Write file to temp path
|
||||
const path = this.rootDir + filePath;
|
||||
await client.upload(path, tmpFilePath);
|
||||
} finally {
|
||||
// Remove temp file
|
||||
fs.unlinkSync(tmpFilePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async remove(filePath: string) {
|
||||
const client = new FtpClient({
|
||||
access: this.access,
|
||||
logger: this.logger,
|
||||
});
|
||||
await client.connect(async (client) => {
|
||||
const path = this.rootDir + filePath;
|
||||
await client.client.remove(path);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { BaseHttpChallengeUploader } from "../api.js";
|
||||
import { QiniuOssAccess, QiniuClient, QiniuAccess } from "@certd/plugin-lib";
|
||||
|
||||
export class QiniuOssHttpChallengeUploader extends BaseHttpChallengeUploader<QiniuOssAccess> {
|
||||
async upload(filePath: string, fileContent: Buffer) {
|
||||
const qiniuAccess = await this.ctx.accessService.getById<QiniuAccess>(this.access.accessId);
|
||||
const client = new QiniuClient({
|
||||
access: qiniuAccess,
|
||||
logger: this.logger,
|
||||
http: this.ctx.utils.http,
|
||||
});
|
||||
if (this.rootDir.endsWith("/")) {
|
||||
this.rootDir = this.rootDir.slice(0, -1);
|
||||
}
|
||||
await client.uploadFile(this.access.bucket, this.rootDir + filePath, fileContent);
|
||||
}
|
||||
|
||||
async remove(filePath: string) {
|
||||
const qiniuAccess = await this.ctx.accessService.getById<QiniuAccess>(this.access.accessId);
|
||||
const client = new QiniuClient({
|
||||
access: qiniuAccess,
|
||||
logger: this.logger,
|
||||
http: this.ctx.utils.http,
|
||||
});
|
||||
|
||||
if (this.rootDir.endsWith("/")) {
|
||||
this.rootDir = this.rootDir.slice(0, -1);
|
||||
}
|
||||
await client.removeFile(this.access.bucket, this.rootDir + filePath);
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import { BaseHttpChallengeUploader } from "../api.js";
|
||||
import { SshAccess, SshClient } from "@certd/plugin-lib";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
import fs from "fs";
|
||||
import { SftpAccess } from "@certd/plugin-lib";
|
||||
|
||||
export class SftpHttpChallengeUploader extends BaseHttpChallengeUploader<SftpAccess> {
|
||||
async upload(filePath: string, fileContent: Buffer) {
|
||||
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
|
||||
|
||||
// Write file to temp path
|
||||
const dir = path.dirname(tmpFilePath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(tmpFilePath, fileContent);
|
||||
|
||||
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
|
||||
const key = this.rootDir + filePath;
|
||||
try {
|
||||
const client = new SshClient(this.logger);
|
||||
await client.uploadFiles({
|
||||
connectConf: access,
|
||||
mkdirs: true,
|
||||
transports: [
|
||||
{
|
||||
localPath: tmpFilePath,
|
||||
remotePath: key,
|
||||
},
|
||||
],
|
||||
opts: {
|
||||
mode: this.access?.fileMode ?? undefined,
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
// Remove temp file
|
||||
fs.unlinkSync(tmpFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
async remove(filePath: string) {
|
||||
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
|
||||
const client = new SshClient(this.logger);
|
||||
const key = this.rootDir + filePath;
|
||||
await client.removeFiles({
|
||||
connectConf: access,
|
||||
files: [key],
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { BaseHttpChallengeUploader } from "../api.js";
|
||||
import { TencentAccess, TencentCosAccess, TencentCosClient } from "@certd/plugin-lib";
|
||||
|
||||
export class TencentCosHttpChallengeUploader extends BaseHttpChallengeUploader<TencentCosAccess> {
|
||||
async upload(filePath: string, fileContent: Buffer) {
|
||||
const access = await this.ctx.accessService.getById<TencentAccess>(this.access.accessId);
|
||||
const client = new TencentCosClient({
|
||||
access: access,
|
||||
logger: this.logger,
|
||||
region: this.access.region,
|
||||
bucket: this.access.bucket,
|
||||
});
|
||||
const key = this.rootDir + filePath;
|
||||
await client.uploadFile(key, fileContent);
|
||||
}
|
||||
|
||||
async remove(filePath: string) {
|
||||
const access = await this.ctx.accessService.getById<TencentAccess>(this.access.accessId);
|
||||
const client = new TencentCosClient({
|
||||
access: access,
|
||||
logger: this.logger,
|
||||
region: this.access.region,
|
||||
bucket: this.access.bucket,
|
||||
});
|
||||
const key = this.rootDir + filePath;
|
||||
await client.removeFile(key);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,34 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 优化cdnfly插件,支持自动匹配域名部署 ([afd59e9](https://github.com/certd/certd/commit/afd59e9933b2650f41c5d47684c171b93b962065))
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复http上传方式无法清除记录文件的bug ([72a7b51](https://github.com/certd/certd/commit/72a7b51d479602b2c54c6c3ac8d8a0dcb9664e73))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 七牛oss支持删除过期备份 ([b7113bd](https://github.com/certd/certd/commit/b7113bda2378116d6c116dc583f563cce7cf9f00))
|
||||
* 数据库备份支持oss ([308d460](https://github.com/certd/certd/commit/308d4600efe2002f199c33b4594d3071784e58ea))
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* ssh PTY模式登录设置 ([8385bcc](https://github.com/certd/certd/commit/8385bcc2d7f2411a07748bb5c53f9eaf4d38d7cc))
|
||||
* ssh伪终端模式优化,windows下不开启 ([42dfe93](https://github.com/certd/certd/commit/42dfe936b773b7bdd82ca3378363252ffffd7b71))
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-lib",
|
||||
"private": false,
|
||||
"version": "1.33.5",
|
||||
"version": "1.34.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -16,10 +16,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@certd/basic": "^1.33.5",
|
||||
"@certd/pipeline": "^1.33.5",
|
||||
"@aws-sdk/client-s3": "^3.787.0",
|
||||
"@certd/basic": "^1.34.0",
|
||||
"@certd/pipeline": "^1.34.0",
|
||||
"@kubernetes/client-node": "0.21.0",
|
||||
"ali-oss": "^6.21.0",
|
||||
"ali-oss": "^6.22.0",
|
||||
"basic-ftp": "^5.0.5",
|
||||
"cos-nodejs-sdk-v5": "^2.14.6",
|
||||
"dayjs": "^1.11.7",
|
||||
@@ -48,5 +49,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f"
|
||||
"gitHead": "9b420ad33ff4c36fc99d643c18be9ec7e29f220d"
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline";
|
||||
title: "阿里云授权",
|
||||
desc: "",
|
||||
icon: "ant-design:aliyun-outlined",
|
||||
order: 0,
|
||||
})
|
||||
export class AliyunAccess extends BaseAccess {
|
||||
@AccessInput({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AliyunAccess } from "../access";
|
||||
import { AliyunAccess } from "../access/index.js";
|
||||
|
||||
export class AliossClient {
|
||||
access: AliyunAccess;
|
||||
@@ -52,7 +52,7 @@ export class AliossClient {
|
||||
}
|
||||
}
|
||||
|
||||
async uploadFile(filePath: string, content: Buffer) {
|
||||
async uploadFile(filePath: string, content: Buffer | string) {
|
||||
await this.init();
|
||||
return await this.client.put(filePath, content);
|
||||
}
|
||||
@@ -61,4 +61,23 @@ export class AliossClient {
|
||||
await this.init();
|
||||
return await this.client.delete(filePath);
|
||||
}
|
||||
|
||||
async downloadFile(key: string, savePath: string) {
|
||||
await this.init();
|
||||
return await this.client.get(key, savePath);
|
||||
}
|
||||
|
||||
async listDir(dirKey: string) {
|
||||
await this.init();
|
||||
const res = await this.client.listV2({
|
||||
prefix: dirKey,
|
||||
// max-keys: 100,
|
||||
// continuation-token: "token",
|
||||
// delimiter: "/",
|
||||
// marker: "marker",
|
||||
// encoding-type: "url",
|
||||
});
|
||||
|
||||
return res.objects;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export function createRemoteSelectInputDefine(opts?: {
|
||||
watches: [certDomainsInputKey, accessIdInputKey, ...watches],
|
||||
},
|
||||
rules: opts?.rules,
|
||||
required: true,
|
||||
required: opts.required ?? true,
|
||||
mergeScript: `
|
||||
return {
|
||||
component:{
|
||||
|
||||
@@ -11,7 +11,7 @@ export class FtpClient {
|
||||
this.logger = opts.logger;
|
||||
}
|
||||
|
||||
async connect(callback: (client: FtpClient) => Promise<void>) {
|
||||
async connect(callback: (client: FtpClient) => Promise<any>) {
|
||||
const ftp = await import("basic-ftp");
|
||||
const Client = ftp.Client;
|
||||
const client = new Client();
|
||||
@@ -21,7 +21,7 @@ export class FtpClient {
|
||||
this.logger.info("FTP连接成功");
|
||||
this.client = client;
|
||||
try {
|
||||
await callback(this);
|
||||
return await callback(this);
|
||||
} finally {
|
||||
if (client) {
|
||||
client.close();
|
||||
@@ -44,4 +44,20 @@ export class FtpClient {
|
||||
this.logger.info(`开始删除文件${filePath}`);
|
||||
await this.client.remove(filePath, true);
|
||||
}
|
||||
|
||||
async listDir(dir: string): Promise<any[]> {
|
||||
if (!dir) {
|
||||
return [];
|
||||
}
|
||||
if (!dir.endsWith("/")) {
|
||||
dir = dir + "/";
|
||||
}
|
||||
this.logger.info(`开始列出目录${dir}`);
|
||||
return await this.client.list(dir);
|
||||
}
|
||||
|
||||
async download(filePath: string, savePath: string): Promise<void> {
|
||||
this.logger.info(`开始下载文件${filePath} -> ${savePath}`);
|
||||
await this.client.downloadTo(savePath, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,5 @@ export * from "./ftp/index.js";
|
||||
export * from "./tencent/index.js";
|
||||
export * from "./qiniu/index.js";
|
||||
export * from "./ctyun/index.js";
|
||||
export * from "./oss/index.js";
|
||||
export * from "./s3/index.js";
|
||||
|
||||
90
packages/plugins/plugin-lib/src/oss/api.ts
Normal file
90
packages/plugins/plugin-lib/src/oss/api.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { IAccessService } from "@certd/pipeline";
|
||||
import { ILogger, utils } from "@certd/basic";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export type OssClientRemoveByOpts = {
|
||||
dir?: string;
|
||||
//删除多少天前的文件
|
||||
beforeDays?: number;
|
||||
};
|
||||
|
||||
export type OssFileItem = {
|
||||
//文件全路径
|
||||
path: string;
|
||||
size: number;
|
||||
//毫秒时间戳
|
||||
lastModified: number;
|
||||
};
|
||||
|
||||
export type IOssClient = {
|
||||
upload: (fileName: string, fileContent: Buffer) => Promise<void>;
|
||||
remove: (fileName: string, opts?: { joinRootDir?: boolean }) => Promise<void>;
|
||||
|
||||
download: (fileName: string, savePath: string) => Promise<void>;
|
||||
|
||||
removeBy: (removeByOpts: OssClientRemoveByOpts) => Promise<void>;
|
||||
|
||||
listDir: (dir: string) => Promise<OssFileItem[]>;
|
||||
};
|
||||
|
||||
export type OssClientContext = {
|
||||
accessService: IAccessService;
|
||||
logger: ILogger;
|
||||
utils: typeof utils;
|
||||
};
|
||||
|
||||
export abstract class BaseOssClient<A> implements IOssClient {
|
||||
rootDir: string = "";
|
||||
access: A = null;
|
||||
logger: ILogger;
|
||||
utils: typeof utils;
|
||||
ctx: OssClientContext;
|
||||
|
||||
protected constructor(opts: { rootDir?: string; access: A }) {
|
||||
this.rootDir = opts.rootDir || "";
|
||||
this.access = opts.access;
|
||||
}
|
||||
|
||||
join(...strs: string[]) {
|
||||
let res = "";
|
||||
for (const item of strs) {
|
||||
if (item) {
|
||||
if (!res) {
|
||||
res = item;
|
||||
} else {
|
||||
res += "/" + item;
|
||||
}
|
||||
}
|
||||
}
|
||||
res = res.replace(/[\\/]+/g, "/");
|
||||
return res;
|
||||
}
|
||||
|
||||
async setCtx(ctx: any) {
|
||||
// set context
|
||||
this.ctx = ctx;
|
||||
this.logger = ctx.logger;
|
||||
this.utils = ctx.utils;
|
||||
await this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
abstract remove(fileName: string, opts?: { joinRootDir?: boolean }): Promise<void>;
|
||||
abstract upload(fileName: string, fileContent: Buffer): Promise<void>;
|
||||
abstract download(fileName: string, savePath: string): Promise<void>;
|
||||
abstract listDir(dir: string): Promise<OssFileItem[]>;
|
||||
|
||||
async removeBy(removeByOpts: OssClientRemoveByOpts): Promise<void> {
|
||||
const list = await this.listDir(removeByOpts.dir);
|
||||
// removeByOpts.beforeDays = 0;
|
||||
const beforeDate = dayjs().subtract(removeByOpts.beforeDays, "day");
|
||||
for (const item of list) {
|
||||
if (item.lastModified && item.lastModified < beforeDate.valueOf()) {
|
||||
await this.remove(item.path, { joinRootDir: false });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +1,33 @@
|
||||
import { HttpChallengeUploadContext } from "./api";
|
||||
import { OssClientContext } from "./api";
|
||||
|
||||
export class HttpChallengeUploaderFactory {
|
||||
export class OssClientFactory {
|
||||
async getClassByType(type: string) {
|
||||
if (type === "alioss") {
|
||||
const module = await import("./impls/alioss.js");
|
||||
return module.AliossHttpChallengeUploader;
|
||||
return module.default;
|
||||
} else if (type === "ssh") {
|
||||
const module = await import("./impls/ssh.js");
|
||||
return module.SshHttpChallengeUploader;
|
||||
return module.default;
|
||||
} else if (type === "sftp") {
|
||||
const module = await import("./impls/sftp.js");
|
||||
return module.SftpHttpChallengeUploader;
|
||||
return module.default;
|
||||
} else if (type === "ftp") {
|
||||
const module = await import("./impls/ftp.js");
|
||||
return module.FtpHttpChallengeUploader;
|
||||
return module.default;
|
||||
} else if (type === "tencentcos") {
|
||||
const module = await import("./impls/tencentcos.js");
|
||||
return module.TencentCosHttpChallengeUploader;
|
||||
return module.default;
|
||||
} else if (type === "qiniuoss") {
|
||||
const module = await import("./impls/qiniuoss.js");
|
||||
return module.QiniuOssHttpChallengeUploader;
|
||||
return module.default;
|
||||
} else if (type === "s3") {
|
||||
const module = await import("./impls/s3.js");
|
||||
return module.default;
|
||||
} else {
|
||||
throw new Error(`暂不支持此文件上传方式: ${type}`);
|
||||
}
|
||||
}
|
||||
async createUploaderByType(type: string, opts: { rootDir: string; access: any; ctx: HttpChallengeUploadContext }) {
|
||||
async createOssClientByType(type: string, opts: { rootDir?: string; access: any; ctx: OssClientContext }) {
|
||||
const cls = await this.getClassByType(type);
|
||||
if (cls) {
|
||||
// @ts-ignore
|
||||
@@ -35,4 +38,4 @@ export class HttpChallengeUploaderFactory {
|
||||
}
|
||||
}
|
||||
|
||||
export const httpChallengeUploaderFactory = new HttpChallengeUploaderFactory();
|
||||
export const ossClientFactory = new OssClientFactory();
|
||||
57
packages/plugins/plugin-lib/src/oss/impls/alioss.ts
Normal file
57
packages/plugins/plugin-lib/src/oss/impls/alioss.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { BaseOssClient, OssFileItem } from "../api.js";
|
||||
import { AliossAccess, AliossClient, AliyunAccess } from "../../aliyun/index.js";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export default class AliOssClientImpl extends BaseOssClient<AliossAccess> {
|
||||
client: AliossClient;
|
||||
join(...strs: string[]) {
|
||||
const str = super.join(...strs);
|
||||
if (str.startsWith("/")) {
|
||||
return str.substring(1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
async init() {
|
||||
const aliyunAccess = await this.ctx.accessService.getById<AliyunAccess>(this.access.accessId);
|
||||
const client = new AliossClient({
|
||||
access: aliyunAccess,
|
||||
bucket: this.access.bucket,
|
||||
region: this.access.region,
|
||||
});
|
||||
await client.init();
|
||||
this.client = client;
|
||||
}
|
||||
async download(filePath: string, savePath: string): Promise<void> {
|
||||
const key = this.join(this.rootDir, filePath);
|
||||
await this.client.downloadFile(key, savePath);
|
||||
}
|
||||
async listDir(dir: string): Promise<OssFileItem[]> {
|
||||
const dirKey = this.join(this.rootDir, dir) + "/";
|
||||
const list = await this.client.listDir(dirKey);
|
||||
this.logger.info(`列出目录: ${dirKey},文件数:${list.length}`);
|
||||
return list.map(item => {
|
||||
return {
|
||||
path: item.name,
|
||||
lastModified: dayjs(item.lastModified).valueOf(),
|
||||
size: item.size,
|
||||
};
|
||||
});
|
||||
}
|
||||
async upload(filePath: string, fileContent: Buffer | string) {
|
||||
const key = this.join(this.rootDir, filePath);
|
||||
this.logger.info(`开始上传文件: ${key}`);
|
||||
await this.client.uploadFile(key, fileContent);
|
||||
|
||||
this.logger.info(`文件上传成功: ${filePath}`);
|
||||
}
|
||||
|
||||
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||
if (opts?.joinRootDir !== false) {
|
||||
filePath = this.join(this.rootDir, filePath);
|
||||
}
|
||||
const key = filePath;
|
||||
// remove file from alioss
|
||||
await this.client.removeFile(key);
|
||||
this.logger.info(`文件删除成功: ${key}`);
|
||||
}
|
||||
}
|
||||
78
packages/plugins/plugin-lib/src/oss/impls/ftp.ts
Normal file
78
packages/plugins/plugin-lib/src/oss/impls/ftp.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { BaseOssClient } from "../api.js";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
import fs from "fs";
|
||||
import { FtpAccess, FtpClient } from "../../ftp/index.js";
|
||||
|
||||
export default class FtpOssClientImpl extends BaseOssClient<FtpAccess> {
|
||||
join(...strs: string[]) {
|
||||
const str = super.join(...strs);
|
||||
if (!str.startsWith("/")) {
|
||||
return "/" + str;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
async download(fileName: string, savePath: string) {
|
||||
const client = this.getFtpClient();
|
||||
await client.connect(async client => {
|
||||
const path = this.join(this.rootDir, fileName);
|
||||
await client.download(path, savePath);
|
||||
});
|
||||
}
|
||||
async listDir(dir: string) {
|
||||
const client = this.getFtpClient();
|
||||
return await client.connect(async (client: FtpClient) => {
|
||||
const path = this.join(this.rootDir, dir);
|
||||
const res = await client.listDir(path);
|
||||
return res.map(item => {
|
||||
return {
|
||||
path: this.join(path, item.name),
|
||||
size: item.size,
|
||||
lastModified: item.modifiedAt.getTime(),
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
async upload(filePath: string, fileContent: Buffer | string) {
|
||||
const client = this.getFtpClient();
|
||||
await client.connect(async client => {
|
||||
let tmpFilePath = fileContent as string;
|
||||
if (typeof fileContent !== "string") {
|
||||
tmpFilePath = path.join(os.tmpdir(), "cert", "oss", filePath);
|
||||
const dir = path.dirname(tmpFilePath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(tmpFilePath, fileContent);
|
||||
}
|
||||
|
||||
try {
|
||||
// Write file to temp path
|
||||
const path = this.join(this.rootDir, filePath);
|
||||
await client.upload(tmpFilePath, path);
|
||||
} finally {
|
||||
// Remove temp file
|
||||
fs.unlinkSync(tmpFilePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getFtpClient() {
|
||||
return new FtpClient({
|
||||
access: this.access,
|
||||
logger: this.logger,
|
||||
});
|
||||
}
|
||||
|
||||
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||
if (opts?.joinRootDir !== false) {
|
||||
filePath = this.join(this.rootDir, filePath);
|
||||
}
|
||||
const client = this.getFtpClient();
|
||||
await client.connect(async client => {
|
||||
await client.client.remove(filePath);
|
||||
this.logger.info(`删除文件成功: ${filePath}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
50
packages/plugins/plugin-lib/src/oss/impls/qiniuoss.ts
Normal file
50
packages/plugins/plugin-lib/src/oss/impls/qiniuoss.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { QiniuAccess, QiniuClient, QiniuOssAccess } from "../../qiniu/index.js";
|
||||
import { BaseOssClient, OssFileItem } from "../api.js";
|
||||
|
||||
export default class QiniuOssClientImpl extends BaseOssClient<QiniuOssAccess> {
|
||||
client: QiniuClient;
|
||||
|
||||
join(...strs: string[]) {
|
||||
const str = super.join(...strs);
|
||||
if (str.startsWith("/")) {
|
||||
return str.substring(1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
async init() {
|
||||
const qiniuAccess = await this.ctx.accessService.getById<QiniuAccess>(this.access.accessId);
|
||||
this.client = new QiniuClient({
|
||||
access: qiniuAccess,
|
||||
logger: this.logger,
|
||||
http: this.ctx.utils.http,
|
||||
});
|
||||
}
|
||||
|
||||
async download(fileName: string, savePath: string): Promise<void> {
|
||||
const path = this.join(this.rootDir, fileName);
|
||||
await this.client.downloadFile(this.access.bucket, path, savePath);
|
||||
}
|
||||
async listDir(dir: string): Promise<OssFileItem[]> {
|
||||
const path = this.join(this.rootDir, dir);
|
||||
const res = await this.client.listDir(this.access.bucket, path);
|
||||
return res.items.map(item => {
|
||||
return {
|
||||
path: item.key,
|
||||
size: item.fsize,
|
||||
//ns ,纳秒,去掉低4位 为毫秒
|
||||
lastModified: Math.floor(item.putTime / 10000),
|
||||
};
|
||||
});
|
||||
}
|
||||
async upload(filePath: string, fileContent: Buffer | string) {
|
||||
const path = this.join(this.rootDir, filePath);
|
||||
await this.client.uploadFile(this.access.bucket, path, fileContent);
|
||||
}
|
||||
|
||||
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||
if (opts?.joinRootDir !== false) {
|
||||
filePath = this.join(this.rootDir, filePath);
|
||||
}
|
||||
await this.client.removeFile(this.access.bucket, filePath);
|
||||
}
|
||||
}
|
||||
98
packages/plugins/plugin-lib/src/oss/impls/s3.ts
Normal file
98
packages/plugins/plugin-lib/src/oss/impls/s3.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { BaseOssClient, OssFileItem } from "../api.js";
|
||||
import path from "node:path";
|
||||
import { S3Access } from "../../s3/access.js";
|
||||
import fs from "fs";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export default class S3OssClientImpl extends BaseOssClient<S3Access> {
|
||||
client: any;
|
||||
join(...strs: string[]) {
|
||||
const str = super.join(...strs);
|
||||
if (str.startsWith("/")) {
|
||||
return str.substring(1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
async init() {
|
||||
// import { S3Client } from "@aws-sdk/client-s3";
|
||||
//@ts-ignore
|
||||
const { S3Client } = await import("@aws-sdk/client-s3");
|
||||
this.client = new S3Client({
|
||||
forcePathStyle: true,
|
||||
//@ts-ignore
|
||||
s3ForcePathStyle: true,
|
||||
credentials: {
|
||||
accessKeyId: this.access.accessKeyId, // 默认 MinIO 访问密钥
|
||||
secretAccessKey: this.access.secretAccessKey, // 默认 MinIO 秘密密钥
|
||||
},
|
||||
region: "us-east-1",
|
||||
endpoint: this.access.endpoint,
|
||||
});
|
||||
}
|
||||
|
||||
async download(filePath: string, savePath: string): Promise<void> {
|
||||
// @ts-ignore
|
||||
const { GetObjectCommand } = await import("@aws-sdk/client-s3");
|
||||
const key = path.join(this.rootDir, filePath);
|
||||
const params = {
|
||||
Bucket: this.access.bucket, // The name of the bucket. For example, 'sample_bucket_101'.
|
||||
Key: key, // The name of the object. For example, 'sample_upload.txt'.
|
||||
};
|
||||
const res = await this.client.send(new GetObjectCommand({ ...params }));
|
||||
const fileContent = fs.createWriteStream(savePath);
|
||||
res.Body.pipe(fileContent);
|
||||
|
||||
this.logger.info(`文件下载成功: ${savePath}`);
|
||||
}
|
||||
|
||||
async listDir(dir: string): Promise<OssFileItem[]> {
|
||||
// @ts-ignore
|
||||
const { ListObjectsCommand } = await import("@aws-sdk/client-s3");
|
||||
const dirKey = this.join(this.rootDir, dir);
|
||||
const params = {
|
||||
Bucket: this.access.bucket, // The name of the bucket. For example, 'sample_bucket_101'.
|
||||
Prefix: dirKey, // The name of the object. For example, 'sample_upload.txt'.
|
||||
};
|
||||
const res = await this.client.send(new ListObjectsCommand({ ...params }));
|
||||
return res.Contents.map(item => {
|
||||
return {
|
||||
path: item.Key,
|
||||
size: item.Size,
|
||||
lastModified: dayjs(item.LastModified).valueOf(),
|
||||
};
|
||||
});
|
||||
}
|
||||
async upload(filePath: string, fileContent: Buffer | string) {
|
||||
// @ts-ignore
|
||||
const { PutObjectCommand } = await import("@aws-sdk/client-s3");
|
||||
const key = path.join(this.rootDir, filePath);
|
||||
this.logger.info(`开始上传文件: ${key}`);
|
||||
const params = {
|
||||
Bucket: this.access.bucket, // The name of the bucket. For example, 'sample_bucket_101'.
|
||||
Key: key, // The name of the object. For example, 'sample_upload.txt'.
|
||||
};
|
||||
if (typeof fileContent === "string") {
|
||||
fileContent = fs.createReadStream(fileContent) as any;
|
||||
}
|
||||
await this.client.send(new PutObjectCommand({ Body: fileContent, ...params }));
|
||||
|
||||
this.logger.info(`文件上传成功: ${filePath}`);
|
||||
}
|
||||
|
||||
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||
if (opts?.joinRootDir !== false) {
|
||||
filePath = this.join(this.rootDir, filePath);
|
||||
}
|
||||
const key = filePath;
|
||||
// @ts-ignore
|
||||
const { DeleteObjectCommand } = await import("@aws-sdk/client-s3");
|
||||
await this.client.send(
|
||||
new DeleteObjectCommand({
|
||||
Bucket: this.access.bucket,
|
||||
Key: key,
|
||||
})
|
||||
);
|
||||
|
||||
this.logger.info(`文件删除成功: ${key}`);
|
||||
}
|
||||
}
|
||||
82
packages/plugins/plugin-lib/src/oss/impls/sftp.ts
Normal file
82
packages/plugins/plugin-lib/src/oss/impls/sftp.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { BaseOssClient, OssFileItem } from "../api.js";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
import fs from "fs";
|
||||
import { SftpAccess, SshAccess, SshClient } from "../../ssh/index.js";
|
||||
|
||||
export default class SftpOssClientImpl extends BaseOssClient<SftpAccess> {
|
||||
async download(fileName: string, savePath: string): Promise<void> {
|
||||
const path = this.join(this.rootDir, fileName);
|
||||
const client = new SshClient(this.logger);
|
||||
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
|
||||
await client.download({
|
||||
connectConf: access,
|
||||
filePath: path,
|
||||
savePath,
|
||||
});
|
||||
}
|
||||
|
||||
async listDir(dir: string): Promise<OssFileItem[]> {
|
||||
const path = this.join(this.rootDir, dir);
|
||||
const client = new SshClient(this.logger);
|
||||
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
|
||||
const res = await client.listDir({
|
||||
connectConf: access,
|
||||
dir: path,
|
||||
});
|
||||
|
||||
return res.map(item => {
|
||||
return {
|
||||
path: this.join(path, item.filename),
|
||||
size: item.size,
|
||||
lastModified: item.attrs.atime * 1000,
|
||||
};
|
||||
});
|
||||
}
|
||||
async upload(filePath: string, fileContent: Buffer | string) {
|
||||
let tmpFilePath = fileContent as string;
|
||||
if (typeof fileContent !== "string") {
|
||||
tmpFilePath = path.join(os.tmpdir(), "cert", "oss", filePath);
|
||||
const dir = path.dirname(tmpFilePath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
}
|
||||
fs.writeFileSync(tmpFilePath, fileContent);
|
||||
}
|
||||
|
||||
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
|
||||
const key = this.join(this.rootDir, filePath);
|
||||
try {
|
||||
const client = new SshClient(this.logger);
|
||||
await client.uploadFiles({
|
||||
connectConf: access,
|
||||
mkdirs: true,
|
||||
transports: [
|
||||
{
|
||||
localPath: tmpFilePath,
|
||||
remotePath: key,
|
||||
},
|
||||
],
|
||||
uploadType: "sftp",
|
||||
opts: {
|
||||
mode: this.access?.fileMode ?? undefined,
|
||||
},
|
||||
});
|
||||
} finally {
|
||||
// Remove temp file
|
||||
fs.unlinkSync(tmpFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||
const access = await this.ctx.accessService.getById<SshAccess>(this.access.sshAccess);
|
||||
const client = new SshClient(this.logger);
|
||||
if (opts?.joinRootDir !== false) {
|
||||
filePath = this.join(this.rootDir, filePath);
|
||||
}
|
||||
await client.removeFiles({
|
||||
connectConf: access,
|
||||
files: [filePath],
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,20 @@
|
||||
import { BaseHttpChallengeUploader } from "../api.js";
|
||||
import { SshAccess, SshClient } from "@certd/plugin-lib";
|
||||
import { BaseOssClient, OssClientRemoveByOpts, OssFileItem } from "../api.js";
|
||||
import path from "path";
|
||||
import os from "os";
|
||||
import fs from "fs";
|
||||
import { SshAccess, SshClient } from "../../ssh/index.js";
|
||||
|
||||
export class SshHttpChallengeUploader extends BaseHttpChallengeUploader<SshAccess> {
|
||||
//废弃
|
||||
export default class SshOssClientImpl extends BaseOssClient<SshAccess> {
|
||||
download(fileName: string, savePath: string): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
removeBy(removeByOpts: OssClientRemoveByOpts): Promise<void> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
listDir(dir: string): Promise<OssFileItem[]> {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
async upload(filePath: string, fileContent: Buffer) {
|
||||
const tmpFilePath = path.join(os.tmpdir(), "cert", "http", filePath);
|
||||
|
||||
@@ -34,12 +44,14 @@ export class SshHttpChallengeUploader extends BaseHttpChallengeUploader<SshAcces
|
||||
}
|
||||
}
|
||||
|
||||
async remove(filePath: string) {
|
||||
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||
if (opts?.joinRootDir !== false) {
|
||||
filePath = this.join(this.rootDir, filePath);
|
||||
}
|
||||
const client = new SshClient(this.logger);
|
||||
const key = this.rootDir + filePath;
|
||||
await client.removeFiles({
|
||||
connectConf: this.access,
|
||||
files: [key],
|
||||
files: [filePath],
|
||||
});
|
||||
}
|
||||
}
|
||||
54
packages/plugins/plugin-lib/src/oss/impls/tencentcos.ts
Normal file
54
packages/plugins/plugin-lib/src/oss/impls/tencentcos.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import dayjs from "dayjs";
|
||||
import { TencentAccess, TencentCosAccess, TencentCosClient } from "../../tencent/index.js";
|
||||
import { BaseOssClient, OssFileItem } from "../api.js";
|
||||
|
||||
export default class TencentOssClientImpl extends BaseOssClient<TencentCosAccess> {
|
||||
client: TencentCosClient;
|
||||
|
||||
join(...strs: string[]) {
|
||||
const str = super.join(...strs);
|
||||
if (str.startsWith("/")) {
|
||||
return str.substring(1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
async init() {
|
||||
const access = await this.ctx.accessService.getById<TencentAccess>(this.access.accessId);
|
||||
this.client = new TencentCosClient({
|
||||
access: access,
|
||||
logger: this.logger,
|
||||
region: this.access.region,
|
||||
bucket: this.access.bucket,
|
||||
});
|
||||
}
|
||||
async download(filePath: string, savePath: string): Promise<void> {
|
||||
const key = this.join(this.rootDir, filePath);
|
||||
await this.client.downloadFile(key, savePath);
|
||||
}
|
||||
|
||||
async listDir(dir: string): Promise<OssFileItem[]> {
|
||||
const dirKey = this.join(this.rootDir, dir) + "/";
|
||||
// @ts-ignore
|
||||
const res: any[] = await this.client.listDir(dirKey);
|
||||
return res.map(item => {
|
||||
return {
|
||||
path: item.Key,
|
||||
size: item.Size,
|
||||
lastModified: dayjs(item.LastModified).valueOf(),
|
||||
};
|
||||
});
|
||||
}
|
||||
async upload(filePath: string, fileContent: Buffer | string) {
|
||||
const key = this.join(this.rootDir, filePath);
|
||||
await this.client.uploadFile(key, fileContent);
|
||||
this.logger.info(`文件上传成功: ${filePath}`);
|
||||
}
|
||||
|
||||
async remove(filePath: string, opts?: { joinRootDir?: boolean }) {
|
||||
if (opts?.joinRootDir !== false) {
|
||||
filePath = this.join(this.rootDir, filePath);
|
||||
}
|
||||
await this.client.removeFile(filePath);
|
||||
this.logger.info(`文件删除成功: ${filePath}`);
|
||||
}
|
||||
}
|
||||
2
packages/plugins/plugin-lib/src/oss/index.ts
Normal file
2
packages/plugins/plugin-lib/src/oss/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./factory.js";
|
||||
export * from "./api.js";
|
||||
@@ -1,5 +1,6 @@
|
||||
import { HttpClient, ILogger } from "@certd/basic";
|
||||
import { HttpClient, ILogger, utils } from "@certd/basic";
|
||||
import { QiniuAccess } from "../access.js";
|
||||
import fs from "fs";
|
||||
|
||||
export type QiniuCertInfo = {
|
||||
key: string;
|
||||
@@ -98,7 +99,7 @@ export class QiniuClient {
|
||||
});
|
||||
}
|
||||
|
||||
async uploadFile(bucket: string, key: string, content: Buffer) {
|
||||
async uploadFile(bucket: string, key: string, content: Buffer | string) {
|
||||
const sdk = await import("qiniu");
|
||||
const qiniu = sdk.default;
|
||||
const mac = new qiniu.auth.digest.Mac(this.access.accessKey, this.access.secretKey);
|
||||
@@ -111,8 +112,15 @@ export class QiniuClient {
|
||||
const config = new qiniu.conf.Config();
|
||||
const formUploader = new qiniu.form_up.FormUploader(config);
|
||||
const putExtra = new qiniu.form_up.PutExtra();
|
||||
// 文件上传
|
||||
const { data, resp } = await formUploader.put(uploadToken, key, content, putExtra);
|
||||
let res: any = {};
|
||||
if (typeof content === "string") {
|
||||
const readableStream = fs.createReadStream(content);
|
||||
res = await formUploader.putStream(uploadToken, key, readableStream, putExtra);
|
||||
} else {
|
||||
// 文件上传
|
||||
res = await formUploader.put(uploadToken, key, content, putExtra);
|
||||
}
|
||||
const { data, resp } = res;
|
||||
if (resp.statusCode === 200) {
|
||||
this.logger.info("文件上传成功:" + key);
|
||||
return data;
|
||||
@@ -123,12 +131,7 @@ export class QiniuClient {
|
||||
}
|
||||
|
||||
async removeFile(bucket: string, key: string) {
|
||||
const sdk = await import("qiniu");
|
||||
const qiniu = sdk.default;
|
||||
const mac = new qiniu.auth.digest.Mac(this.access.accessKey, this.access.secretKey);
|
||||
const config = new qiniu.conf.Config();
|
||||
config.useHttpsDomain = true;
|
||||
const bucketManager = new qiniu.rs.BucketManager(mac, config);
|
||||
const bucketManager = await this.getBucketManager();
|
||||
|
||||
const { resp } = await bucketManager.delete(bucket, key);
|
||||
|
||||
@@ -139,4 +142,39 @@ export class QiniuClient {
|
||||
throw new Error("删除失败:" + JSON.stringify(resp));
|
||||
}
|
||||
}
|
||||
|
||||
async downloadFile(bucket: string, path: string, savePath: string) {
|
||||
const bucketManager = await this.getBucketManager();
|
||||
const privateBucketDomain = `http://${bucket}.qiniudn.com`;
|
||||
const deadline = Math.floor(Date.now() / 1000) + 3600; // 1小时过期
|
||||
const privateDownloadUrl = bucketManager.privateDownloadUrl(privateBucketDomain, path, deadline);
|
||||
|
||||
await utils.request.download({
|
||||
http: this.http,
|
||||
logger: this.logger,
|
||||
config: {
|
||||
url: privateDownloadUrl,
|
||||
method: "get",
|
||||
},
|
||||
savePath,
|
||||
});
|
||||
}
|
||||
|
||||
private async getBucketManager() {
|
||||
const sdk = await import("qiniu");
|
||||
const qiniu = sdk.default;
|
||||
const mac = new qiniu.auth.digest.Mac(this.access.accessKey, this.access.secretKey);
|
||||
const config = new qiniu.conf.Config();
|
||||
config.useHttpsDomain = true;
|
||||
return new qiniu.rs.BucketManager(mac, config);
|
||||
}
|
||||
|
||||
async listDir(bucket: string, path: string) {
|
||||
const bucketManager = await this.getBucketManager();
|
||||
const res = await bucketManager.listPrefix(bucket, {
|
||||
prefix: path,
|
||||
limit: 1000,
|
||||
});
|
||||
return res.data;
|
||||
}
|
||||
}
|
||||
|
||||
87
packages/plugins/plugin-lib/src/s3/access.ts
Normal file
87
packages/plugins/plugin-lib/src/s3/access.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline";
|
||||
|
||||
/**
|
||||
* 这个注解将注册一个授权配置
|
||||
* 在certd的后台管理系统中,用户可以选择添加此类型的授权
|
||||
*/
|
||||
@IsAccess({
|
||||
name: "s3",
|
||||
title: "s3/minio授权",
|
||||
desc: "S3/minio oss授权",
|
||||
icon: "mdi:folder-upload-outline",
|
||||
})
|
||||
export class S3Access extends BaseAccess {
|
||||
@AccessInput({
|
||||
title: "endpoint",
|
||||
component: {
|
||||
placeholder: "http://xxxxxx:9000",
|
||||
name: "a-input",
|
||||
vModel: "value",
|
||||
},
|
||||
helper: "Minio的地址,如果是aws s3 则无需填写",
|
||||
required: false,
|
||||
})
|
||||
endpoint!: string;
|
||||
|
||||
/**
|
||||
* const minioClient = new S3Client({
|
||||
* endpoint: "http://localhost:9000",
|
||||
* forcePathStyle: true,
|
||||
* credentials: {
|
||||
* accessKeyId: "minioadmin", // 默认 MinIO 访问密钥
|
||||
* secretAccessKey: "minioadmin", // 默认 MinIO 秘密密钥
|
||||
* },
|
||||
* region: "us-east-1",
|
||||
* });
|
||||
*/
|
||||
|
||||
@AccessInput({
|
||||
title: "accessKeyId",
|
||||
component: {
|
||||
placeholder: "accessKeyId",
|
||||
},
|
||||
helper: "accessKeyId",
|
||||
required: true,
|
||||
})
|
||||
accessKeyId!: string;
|
||||
|
||||
@AccessInput({
|
||||
title: "secretAccessKey",
|
||||
component: {
|
||||
placeholder: "secretAccessKey",
|
||||
component: {
|
||||
name: "a-input",
|
||||
vModel: "value",
|
||||
},
|
||||
},
|
||||
helper: "secretAccessKey",
|
||||
encrypt: true,
|
||||
required: true,
|
||||
})
|
||||
secretAccessKey!: string;
|
||||
|
||||
@AccessInput({
|
||||
title: "地区",
|
||||
value: "us-east-1",
|
||||
component: {
|
||||
name: "a-input",
|
||||
vModel: "value",
|
||||
},
|
||||
helper: "region",
|
||||
required: true,
|
||||
})
|
||||
region!: string;
|
||||
|
||||
@AccessInput({
|
||||
title: "存储桶",
|
||||
component: {
|
||||
name: "a-input",
|
||||
vModel: "value",
|
||||
},
|
||||
helper: "bucket 名称",
|
||||
required: true,
|
||||
})
|
||||
bucket!: string;
|
||||
}
|
||||
|
||||
new S3Access();
|
||||
1
packages/plugins/plugin-lib/src/s3/index.ts
Normal file
1
packages/plugins/plugin-lib/src/s3/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./access.js";
|
||||
@@ -63,6 +63,16 @@ export class SshAccess extends BaseAccess {
|
||||
})
|
||||
passphrase!: string;
|
||||
|
||||
@AccessInput({
|
||||
title: "伪终端",
|
||||
helper: "如果登录报错:all authentication methods failed,可以尝试开启伪终端模式进行keyboard-interactive方式登录\n开启后对日志输出有一定的影响",
|
||||
component: {
|
||||
name: "a-switch",
|
||||
vModel: "checked",
|
||||
},
|
||||
})
|
||||
pty!: boolean;
|
||||
|
||||
@AccessInput({
|
||||
title: "socks代理",
|
||||
helper: "socks代理配置,格式:socks5://user:password@host:port",
|
||||
|
||||
@@ -136,6 +136,20 @@ export class AsyncSsh2Client {
|
||||
});
|
||||
}
|
||||
|
||||
async listDir(options: { sftp: any; remotePath: string }) {
|
||||
const { sftp, remotePath } = options;
|
||||
return new Promise((resolve, reject) => {
|
||||
this.logger.info(`listDir:${remotePath}`);
|
||||
sftp.readdir(remotePath, (err: Error, list: any) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(list);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async unlink(options: { sftp: any; remotePath: string }) {
|
||||
const { sftp, remotePath } = options;
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -170,7 +184,10 @@ export class AsyncSsh2Client {
|
||||
// }
|
||||
return new Promise((resolve, reject) => {
|
||||
this.logger.info(`执行命令:[${this.connConf.host}][exec]: \n` + script);
|
||||
this.conn.exec(script, { pty: true, env: opts.env }, (err: Error, stream: any) => {
|
||||
// pty 伪终端,window下的输出会带上conhost.exe之类的多余的字符串,影响返回结果判断
|
||||
// linux下 当使用keyboard-interactive 登录时,需要pty
|
||||
const pty = this.connConf.pty; //linux下开启伪终端,windows下不开启
|
||||
this.conn.exec(script, { pty, env: opts.env }, (err: Error, stream: any) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
@@ -280,6 +297,28 @@ export class AsyncSsh2Client {
|
||||
}
|
||||
return proxy;
|
||||
}
|
||||
|
||||
async download(param: { remotePath: string; savePath: string; sftp: any }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { remotePath, savePath, sftp } = param;
|
||||
sftp.fastGet(
|
||||
remotePath,
|
||||
savePath,
|
||||
{
|
||||
step: (transferred: any, chunk: any, total: any) => {
|
||||
this.logger.info(`${transferred} / ${total}`);
|
||||
},
|
||||
},
|
||||
(err: any) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve({});
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class SshClient {
|
||||
@@ -326,17 +365,17 @@ export class SshClient {
|
||||
}
|
||||
}
|
||||
|
||||
if (options.uploadType === "sftp") {
|
||||
const sftp = await conn.getSftp();
|
||||
for (const transport of transports) {
|
||||
await conn.fastPut({ sftp, ...transport, opts });
|
||||
}
|
||||
} else {
|
||||
if (options.uploadType === "scp") {
|
||||
//scp
|
||||
for (const transport of transports) {
|
||||
await this.scpUpload({ conn, ...transport, opts });
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
} else {
|
||||
const sftp = await conn.getSftp();
|
||||
for (const transport of transports) {
|
||||
await conn.fastPut({ sftp, ...transport, opts });
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.info("文件全部上传成功");
|
||||
@@ -356,25 +395,29 @@ export class SshClient {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
// 准备 SCP 协议头
|
||||
const fileStats = fs.statSync(localPath);
|
||||
const fileName = path.basename(localPath);
|
||||
try {
|
||||
// 准备 SCP 协议头
|
||||
const fileStats = fs.statSync(localPath);
|
||||
const fileName = path.basename(localPath);
|
||||
|
||||
// SCP 协议格式:C[权限] [文件大小] [文件名]\n
|
||||
stream.write(`C0644 ${fileStats.size} ${fileName}\n`);
|
||||
// SCP 协议格式:C[权限] [文件大小] [文件名]\n
|
||||
stream.write(`C0644 ${fileStats.size} ${fileName}\n`);
|
||||
|
||||
// 通过管道传输文件
|
||||
fs.createReadStream(localPath)
|
||||
.on("error", e => {
|
||||
this.logger.info("read stream error", e);
|
||||
reject(e);
|
||||
})
|
||||
.pipe(stream)
|
||||
.on("finish", async () => {
|
||||
this.logger.info(`上传完成:${localPath} => ${remotePath}`);
|
||||
resolve(true);
|
||||
})
|
||||
.on("error", reject);
|
||||
// 通过管道传输文件
|
||||
fs.createReadStream(localPath)
|
||||
.on("error", e => {
|
||||
this.logger.info("read stream error", e);
|
||||
reject(e);
|
||||
})
|
||||
.pipe(stream)
|
||||
.on("finish", async () => {
|
||||
this.logger.info(`上传完成:${localPath} => ${remotePath}`);
|
||||
resolve(true);
|
||||
})
|
||||
.on("error", reject);
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
@@ -475,7 +518,7 @@ export class SshClient {
|
||||
script = envScripts.join(newLine) + newLine + script;
|
||||
}
|
||||
}
|
||||
return await conn.exec(script as string, { env: options.env });
|
||||
return await conn.exec(script as string, {});
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -523,4 +566,31 @@ export class SshClient {
|
||||
conn.end();
|
||||
}
|
||||
}
|
||||
|
||||
async listDir(param: { connectConf: any; dir: string }) {
|
||||
return await this._call<any>({
|
||||
connectConf: param.connectConf,
|
||||
callable: async (conn: AsyncSsh2Client) => {
|
||||
const sftp = await conn.getSftp();
|
||||
return await conn.listDir({
|
||||
sftp,
|
||||
remotePath: param.dir,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async download(param: { connectConf: any; filePath: string; savePath: string }) {
|
||||
return await this._call<any>({
|
||||
connectConf: param.connectConf,
|
||||
callable: async (conn: AsyncSsh2Client) => {
|
||||
const sftp = await conn.getSftp();
|
||||
return await conn.download({
|
||||
sftp,
|
||||
remotePath: param.filePath,
|
||||
savePath: param.savePath,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { TencentAccess } from "../access.js";
|
||||
import { ILogger } from "@certd/basic";
|
||||
import fs from "fs";
|
||||
|
||||
export class TencentCosClient {
|
||||
access: TencentAccess;
|
||||
@@ -23,15 +24,19 @@ export class TencentCosClient {
|
||||
return new sdk.default(clientConfig);
|
||||
}
|
||||
|
||||
async uploadFile(key: string, file: Buffer) {
|
||||
async uploadFile(key: string, file: Buffer | string) {
|
||||
const cos = await this.getCosClient();
|
||||
return new Promise((resolve, reject) => {
|
||||
let readableStream = file as any;
|
||||
if (typeof file === "string") {
|
||||
readableStream = fs.createReadStream(file);
|
||||
}
|
||||
cos.putObject(
|
||||
{
|
||||
Bucket: this.bucket /* 必须 */,
|
||||
Region: this.region /* 必须 */,
|
||||
Key: key /* 必须 */,
|
||||
Body: file, // 上传文件对象
|
||||
Body: readableStream, // 上传文件对象
|
||||
onProgress: function (progressData) {
|
||||
console.log(JSON.stringify(progressData));
|
||||
},
|
||||
@@ -66,4 +71,47 @@ export class TencentCosClient {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async downloadFile(key: string, savePath: string) {
|
||||
const cos = await this.getCosClient();
|
||||
const writeStream = fs.createWriteStream(savePath);
|
||||
return new Promise((resolve, reject) => {
|
||||
cos.getObject(
|
||||
{
|
||||
Bucket: this.bucket,
|
||||
Region: this.region,
|
||||
Key: key,
|
||||
Output: writeStream,
|
||||
},
|
||||
function (err, data) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(data);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
async listDir(dirKey: string) {
|
||||
const cos = await this.getCosClient();
|
||||
return new Promise((resolve, reject) => {
|
||||
cos.getBucket(
|
||||
{
|
||||
Bucket: this.bucket,
|
||||
Region: this.region,
|
||||
Prefix: dirKey,
|
||||
MaxKeys: 1000,
|
||||
},
|
||||
function (err, data) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(data.Contents);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
VITE_APP_API=/api
|
||||
VITE_APP_API=api
|
||||
#登录与权限关闭
|
||||
VITE_APP_PM_ENABLED=true
|
||||
VITE_APP_TITLE=Certd
|
||||
VITE_APP_SLOGAN=让你的证书永不过期
|
||||
VITE_APP_COPYRIGHT_YEAR=2021-2024
|
||||
VITE_APP_COPYRIGHT_YEAR=2021-2025
|
||||
VITE_APP_COPYRIGHT_NAME=handsfree.work
|
||||
VITE_APP_COPYRIGHT_URL=https://certd.handsfree.work
|
||||
VITE_APP_LOGO=/static/images/logo/logo.svg
|
||||
VITE_APP_LOGIN_LOGO=/static/images/logo/rect-black.svg
|
||||
VITE_APP_LOGO=static/images/logo/logo.svg
|
||||
VITE_APP_LOGIN_LOGO=static/images/logo/rect-black.svg
|
||||
VITE_APP_PROJECT_PATH=https://github.com/certd/certd
|
||||
VITE_APP_NAMESPACE=fs
|
||||
@@ -1,3 +1,3 @@
|
||||
VITE_APP_API=/api
|
||||
VITE_APP_API=api
|
||||
#登录与权限开启
|
||||
VITE_APP_PM_ENABLED=true
|
||||
|
||||
@@ -3,6 +3,45 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.34.0](https://github.com/certd/certd/compare/v1.33.8...v1.34.0) (2025-04-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复二次认证登录进入错误账号的bug ([e3930e0](https://github.com/certd/certd/commit/e3930e07172dd7903cb0f6ff26e0e3e828ba3e77))
|
||||
|
||||
## [1.33.8](https://github.com/certd/certd/compare/v1.33.7...v1.33.8) (2025-04-26)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 服务器时间获取不准确的bug ([5d10cbf](https://github.com/certd/certd/commit/5d10cbf18daf94a90a7551641a3b13e3c5fec611))
|
||||
* 修复复制流水线无效的bug ([3df20a9](https://github.com/certd/certd/commit/3df20a924f32970b052e2588ea20de095f0ea693))
|
||||
* 修复token过期后,疯狂打印token过期信息的bug ([50a5fa1](https://github.com/certd/certd/commit/50a5fa15bb240a125bbc91d2ce1ff3c835888a77))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 数据库备份支持oss ([308d460](https://github.com/certd/certd/commit/308d4600efe2002f199c33b4594d3071784e58ea))
|
||||
* 支持反向代理增加contextPath路径 ([0088929](https://github.com/certd/certd/commit/0088929622160cc922995de9a563e8061686ff34))
|
||||
|
||||
## [1.33.7](https://github.com/certd/certd/compare/v1.33.6...v1.33.7) (2025-04-22)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 优化首页插件列表展示 ([9b8f60b](https://github.com/certd/certd/commit/9b8f60b64b5f9a3db7dfa9b3dcbd9201984358d0))
|
||||
|
||||
## [1.33.6](https://github.com/certd/certd/compare/v1.33.5...v1.33.6) (2025-04-20)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 上传商用证书,直接粘贴文本报错的问题;修复无法上传ec加密证书的bug ([5750bb7](https://github.com/certd/certd/commit/5750bb706779da274d8e7a87e71416cb64d2df79))
|
||||
* 修复下载证书时提示token已过期的问题 ([0e07ae6](https://github.com/certd/certd/commit/0e07ae6ce84dcb9279d3c44060d621566afa593c))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 更新license时同时绑定url ([78367af](https://github.com/certd/certd/commit/78367af8307f801e778c76d49f0918c21ffe032f))
|
||||
* 切换到不同的分组后再打开创建对话框,会自动选择分组 ([893dcd4](https://github.com/certd/certd/commit/893dcd4f2487891199ed3e5a3d47a79a75efc942))
|
||||
* 优化/api缓存为0 ([dc05cd4](https://github.com/certd/certd/commit/dc05cd481f186b13375192be965000e6b4b429a5))
|
||||
* 优化证书流水线创建,支持选择分组 ([d613aa8](https://github.com/certd/certd/commit/d613aa8f3e85d8dc475ef1b62d49394ce7fd7d24))
|
||||
|
||||
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<link rel="icon" href="/api/app/favicon"/>
|
||||
<link rel="icon" href="api/app/favicon"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Loading</title>
|
||||
<script src="/static/icons/iconfont.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/index.css"/>
|
||||
<script src="static/icons/iconfont.js?v=<%=version%>"></script>
|
||||
<link rel="stylesheet" type="text/css" href="static/index.css?v=<%=version%>"/>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-client",
|
||||
"version": "1.33.5",
|
||||
"version": "1.34.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --open",
|
||||
@@ -101,8 +101,8 @@
|
||||
"zod-defaults": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/lib-iframe": "^1.33.5",
|
||||
"@certd/pipeline": "^1.33.5",
|
||||
"@certd/lib-iframe": "^1.34.0",
|
||||
"@certd/pipeline": "^1.34.0",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/chai": "^4.3.12",
|
||||
|
||||
@@ -49,33 +49,32 @@ function createService() {
|
||||
}
|
||||
|
||||
// 这个状态码是和后端约定的
|
||||
const { code } = dataAxios;
|
||||
// 根据 code 进行判断
|
||||
if (code === undefined) {
|
||||
if (dataAxios?.code === undefined) {
|
||||
// 如果没有 code 代表这不是项目后端开发的接口
|
||||
errorCreate(`非标准返回:${dataAxios}, ${response.config.url}`);
|
||||
return dataAxios;
|
||||
} else {
|
||||
// 有 code 代表这是一个后端接口 可以进行进一步的判断
|
||||
switch (code) {
|
||||
case 0:
|
||||
// [ 示例 ] code === 0 代表没有错误
|
||||
}
|
||||
const { code } = dataAxios;
|
||||
// 有 code 代表这是一个后端接口 可以进行进一步的判断
|
||||
switch (code) {
|
||||
case 0:
|
||||
// [ 示例 ] code === 0 代表没有错误
|
||||
// @ts-ignore
|
||||
return dataAxios?.data;
|
||||
default:
|
||||
// 不是正确的 code
|
||||
const errorMessage = dataAxios.msg || dataAxios.message || "未知错误";
|
||||
// @ts-ignore
|
||||
if (response?.config?.onError) {
|
||||
const err = new CodeError(errorMessage, dataAxios.code, dataAxios.data);
|
||||
// @ts-ignore
|
||||
return dataAxios.data;
|
||||
default:
|
||||
// 不是正确的 code
|
||||
const errorMessage = dataAxios.msg || dataAxios.message || "未知错误";
|
||||
// @ts-ignore
|
||||
if (response?.config?.onError) {
|
||||
const err = new CodeError(errorMessage, dataAxios.code, dataAxios.data);
|
||||
response.config.onError(err);
|
||||
return;
|
||||
}
|
||||
//@ts-ignore
|
||||
const showErrorNotify = response?.config?.showErrorNotify;
|
||||
errorCreate(`${errorMessage}: ${response.config.url}`, showErrorNotify, dataAxios);
|
||||
return dataAxios;
|
||||
}
|
||||
response.config.onError(err);
|
||||
return;
|
||||
}
|
||||
//@ts-ignore
|
||||
const showErrorNotify = response?.config?.showErrorNotify;
|
||||
errorCreate(`${errorMessage}: ${response.config.url}`, showErrorNotify, dataAxios);
|
||||
return dataAxios;
|
||||
}
|
||||
},
|
||||
error => {
|
||||
@@ -121,7 +120,7 @@ function createService() {
|
||||
errorLog(error, error?.response?.config?.showErrorNotify);
|
||||
if (status === 401) {
|
||||
const userStore = useUserStore();
|
||||
userStore.logout();
|
||||
userStore.logout(true, true);
|
||||
}
|
||||
|
||||
if (error?.config?.onError) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="pem-input">
|
||||
<FileInput v-bind="fileInput" class="mb-5" type="primary" text="选择文件" @change="onChange" />
|
||||
<a-textarea v-bind="textarea" v-model:value="textRef"></a-textarea>
|
||||
<a-textarea v-bind="textarea" :value="modelValue" @update:value="emitValue"></a-textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -17,7 +17,6 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(["update:modelValue"]);
|
||||
const textRef = ref();
|
||||
|
||||
function emitValue(value: string) {
|
||||
emit("update:modelValue", value);
|
||||
@@ -39,16 +38,6 @@ function onChange(e: any) {
|
||||
};
|
||||
fileReader.readAsText(file); // 以文本形式读取文件
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
value => {
|
||||
textRef.value = value;
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
@@ -21,6 +21,6 @@ const props = defineProps<{
|
||||
}>();
|
||||
|
||||
const nslookupCmd = computed(() => {
|
||||
return `nslookup -qa=txt _acme-challenge.${props.record.domain}`;
|
||||
return `nslookup -q=txt _acme-challenge.${props.record.domain}`;
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -75,6 +75,7 @@ const uploaderTypeDict = dict({
|
||||
{ label: "阿里云OSS", value: "alioss" },
|
||||
{ label: "腾讯云COS", value: "tencentcos" },
|
||||
{ label: "七牛OSS", value: "qiniuoss" },
|
||||
{ label: "S3/Minio", value: "s3" },
|
||||
{ label: "SSH(已废弃,请选择SFTP方式)", value: "ssh", disabled: true },
|
||||
],
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ const props = withDefaults(
|
||||
mode?: "comm" | "button" | "nav" | "icon";
|
||||
}>(),
|
||||
{
|
||||
mode: "button"
|
||||
mode: "button",
|
||||
}
|
||||
);
|
||||
type Text = {
|
||||
@@ -40,57 +40,57 @@ const text = computed<Text>(() => {
|
||||
isComm: {
|
||||
comm: {
|
||||
name: `${vipLabel}已开通`,
|
||||
title: "到期时间:" + expireTime.value
|
||||
title: "到期时间:" + expireTime.value,
|
||||
},
|
||||
button: {
|
||||
name: `${vipLabel}已开通`,
|
||||
title: "到期时间:" + expireTime.value
|
||||
title: "到期时间:" + expireTime.value,
|
||||
},
|
||||
icon: {
|
||||
name: "",
|
||||
title: `${vipLabel}已开通`
|
||||
title: `${vipLabel}已开通`,
|
||||
},
|
||||
nav: {
|
||||
name: `${vipLabel}`,
|
||||
title: "到期时间:" + expireTime.value
|
||||
}
|
||||
title: "到期时间:" + expireTime.value,
|
||||
},
|
||||
},
|
||||
isPlus: {
|
||||
comm: {
|
||||
name: "商业版功能",
|
||||
title: "升级商业版,获取商业授权"
|
||||
title: "升级商业版,获取商业授权",
|
||||
},
|
||||
button: {
|
||||
name: `${vipLabel}已开通`,
|
||||
title: "到期时间:" + expireTime.value
|
||||
title: "到期时间:" + expireTime.value,
|
||||
},
|
||||
icon: {
|
||||
name: "",
|
||||
title: `${vipLabel}已开通`
|
||||
title: `${vipLabel}已开通`,
|
||||
},
|
||||
nav: {
|
||||
name: `${vipLabel}`,
|
||||
title: "到期时间:" + expireTime.value
|
||||
}
|
||||
title: "到期时间:" + expireTime.value,
|
||||
},
|
||||
},
|
||||
free: {
|
||||
comm: {
|
||||
name: "商业版功能",
|
||||
title: "升级商业版,获取商业授权"
|
||||
title: "升级商业版,获取商业授权",
|
||||
},
|
||||
button: {
|
||||
name: "专业版功能",
|
||||
title: "升级专业版,享受更多VIP特权"
|
||||
title: "升级专业版,享受更多VIP特权",
|
||||
},
|
||||
icon: {
|
||||
name: "",
|
||||
title: "专业版功能"
|
||||
title: "专业版功能",
|
||||
},
|
||||
nav: {
|
||||
name: "基础版",
|
||||
title: "升级专业版,享受更多VIP特权"
|
||||
}
|
||||
}
|
||||
title: "升级专业版,享受更多VIP特权",
|
||||
},
|
||||
},
|
||||
};
|
||||
if (settingStore.isComm) {
|
||||
return map.isComm[props.mode];
|
||||
@@ -119,7 +119,7 @@ const expiredDays = computed(() => {
|
||||
|
||||
const formState = reactive({
|
||||
code: "",
|
||||
inviteCode: ""
|
||||
inviteCode: "",
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
@@ -143,10 +143,10 @@ async function doActive() {
|
||||
content: "绑定账号后,可以避免License丢失,强烈建议绑定",
|
||||
onOk() {
|
||||
router.push("/sys/account");
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -183,7 +183,7 @@ function openTrialModal() {
|
||||
<div>点击确认,即可获取7天专业版试用</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ function openStarModal() {
|
||||
<img class="ml-5" src="https://img.shields.io/github/stars/certd/certd?logo=github" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -226,13 +226,14 @@ function openUpgrade() {
|
||||
title = "续期专业版/升级商业版";
|
||||
}
|
||||
|
||||
const productInfo = settingStore.productInfo;
|
||||
const vipTypeDefine = {
|
||||
free: {
|
||||
title: "基础版",
|
||||
desc: "社区免费版",
|
||||
type: "free",
|
||||
icon: "lucide:package-open",
|
||||
privilege: ["证书申请无限制", "域名数量无限制", "证书流水线数量无限制", "常用的主机、云平台、cdn等部署插件", "邮件、webhook通知方式"]
|
||||
privilege: ["证书申请无限制", "域名数量无限制", "证书流水线数量无限制", "常用的主机、云平台、cdn等部署插件", "邮件、webhook通知方式"],
|
||||
},
|
||||
plus: {
|
||||
title: "专业版",
|
||||
@@ -243,10 +244,12 @@ function openUpgrade() {
|
||||
title: "点击获取7天试用",
|
||||
click: () => {
|
||||
openStarModal();
|
||||
}
|
||||
},
|
||||
},
|
||||
icon: "stash:thumb-up",
|
||||
price: 29.9,
|
||||
price: productInfo.plus.price,
|
||||
price3: `¥${productInfo.plus.price3}/3年`,
|
||||
tooltip: productInfo.plus.tooltip,
|
||||
get() {
|
||||
return (
|
||||
<a-tooltip title="爱发电赞助“VIP会员”后获取一年期专业版激活码,开源需要您的支持">
|
||||
@@ -255,7 +258,7 @@ function openUpgrade() {
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
comm: {
|
||||
title: "商业版",
|
||||
@@ -263,11 +266,13 @@ function openUpgrade() {
|
||||
type: "comm",
|
||||
icon: "vaadin:handshake",
|
||||
privilege: ["拥有专业版所有特权", "允许商用,可修改logo、标题", "数据统计", "插件管理", "多用户无限制", "支持用户支付"],
|
||||
price: 399,
|
||||
price: productInfo.comm.price,
|
||||
price3: `¥${productInfo.comm.price3}/3年`,
|
||||
tooltip: productInfo.comm.tooltip,
|
||||
get() {
|
||||
return <a-button size="small">请联系作者获取</a-button>;
|
||||
}
|
||||
}
|
||||
return <a-button size="small">请联系作者获取试用</a-button>;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const modalRef = modal.confirm({
|
||||
@@ -321,9 +326,12 @@ function openUpgrade() {
|
||||
<div class="footer flex-between flex-vc">
|
||||
<div class="price-show">
|
||||
{item.price && (
|
||||
<span>
|
||||
<span class="price-text">¥{item.price}</span>
|
||||
/年
|
||||
<span class="flex">
|
||||
<span class="-text">¥{item.price}</span>
|
||||
<span>/年</span>
|
||||
<a-tooltip class="ml-5" title={item.price3}>
|
||||
<fs-icon class="pointer color-red" icon="ic:outline-discount"></fs-icon>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
)}
|
||||
{!item.price && (
|
||||
@@ -340,6 +348,11 @@ function openUpgrade() {
|
||||
}
|
||||
return (
|
||||
<div class="mt-10 mb-10 vip-active-modal">
|
||||
{productInfo.notice && (
|
||||
<div class="mb-10">
|
||||
<a-alert type="error" message={productInfo.notice}></a-alert>
|
||||
</div>
|
||||
)}
|
||||
<div class="vip-type-vs">
|
||||
<a-row gutter={20}>{slots}</a-row>
|
||||
</div>
|
||||
@@ -365,7 +378,7 @@ function openUpgrade() {
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
onMounted(() => {
|
||||
|
||||
@@ -3,12 +3,12 @@ export default {
|
||||
crud: { i18n: { name: "姓名", city: "城市", status: "状态" } },
|
||||
login: {
|
||||
logoutTip: "确认",
|
||||
logoutMessage: "确定要注销登录吗?"
|
||||
}
|
||||
logoutMessage: "确定要注销登录吗?",
|
||||
},
|
||||
},
|
||||
fs: {
|
||||
rowHandle: {
|
||||
title: "操作列"
|
||||
}
|
||||
}
|
||||
title: "操作列",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ import { useI18n } from "vue-i18n";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
defineOptions({
|
||||
name: "FsUserInfo"
|
||||
name: "FsUserInfo",
|
||||
});
|
||||
const userStore = useUserStore();
|
||||
const { t } = useI18n();
|
||||
@@ -38,7 +38,7 @@ function doLogout() {
|
||||
content: t("app.login.logoutMessage"),
|
||||
onOk: async () => {
|
||||
await userStore.logout(true);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -30,7 +30,7 @@ const avatar = computed(() => {
|
||||
});
|
||||
|
||||
async function handleLogout() {
|
||||
userStore.logout(true);
|
||||
await userStore.logout(true);
|
||||
}
|
||||
|
||||
const settingStore = useSettingStore();
|
||||
|
||||
@@ -98,17 +98,17 @@ export const certdResources = [
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "子域名托管设置",
|
||||
name: "SubDomain",
|
||||
path: "/certd/pipeline/subDomain",
|
||||
component: "/certd/pipeline/sub-domain/index.vue",
|
||||
meta: {
|
||||
icon: "material-symbols:approval-delegation-outline",
|
||||
auth: true,
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// title: "子域名托管设置",
|
||||
// name: "SubDomain",
|
||||
// path: "/certd/pipeline/subDomain",
|
||||
// component: "/certd/pipeline/sub-domain/index.vue",
|
||||
// meta: {
|
||||
// icon: "material-symbols:approval-delegation-outline",
|
||||
// auth: true,
|
||||
// keepAlive: true,
|
||||
// },
|
||||
// },
|
||||
{
|
||||
title: "流水线分组管理",
|
||||
name: "PipelineGroupManager",
|
||||
|
||||
@@ -109,3 +109,11 @@ export async function sendEmailCode(data: any): Promise<any> {
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getProductInfo(): Promise<any> {
|
||||
return await request({
|
||||
url: "/basic/settings/productInfo",
|
||||
method: "get",
|
||||
silent: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,7 +9,8 @@ import { env } from "/@/utils/util.env";
|
||||
import { updatePreferences } from "/@/vben/preferences";
|
||||
import { useTitle } from "@vueuse/core";
|
||||
import { utils } from "/@/utils";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
import { cloneDeep, merge } from "lodash-es";
|
||||
|
||||
export interface SettingState {
|
||||
sysPublic?: SysPublicSetting;
|
||||
installInfo?: {
|
||||
@@ -31,6 +32,21 @@ export interface SettingState {
|
||||
time?: number;
|
||||
deltaTime?: number;
|
||||
};
|
||||
productInfo: {
|
||||
notice?: string;
|
||||
plus: {
|
||||
name: string;
|
||||
price: number;
|
||||
price3: number;
|
||||
tooltip?: string;
|
||||
};
|
||||
comm: {
|
||||
name: string;
|
||||
price: number;
|
||||
price3: number;
|
||||
tooltip?: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const defaultSiteInfo: SiteInfo = {
|
||||
@@ -79,6 +95,19 @@ export const useSettingStore = defineStore({
|
||||
time: 0,
|
||||
deltaTime: 0,
|
||||
},
|
||||
productInfo: {
|
||||
notice: "",
|
||||
plus: {
|
||||
name: "专业版",
|
||||
price: 29.9,
|
||||
price3: 89.9,
|
||||
},
|
||||
comm: {
|
||||
name: "商业版",
|
||||
price: 399,
|
||||
price3: 899,
|
||||
},
|
||||
},
|
||||
}),
|
||||
getters: {
|
||||
getSysPublic(): SysPublicSetting {
|
||||
@@ -160,10 +189,10 @@ export const useSettingStore = defineStore({
|
||||
//@ts-ignore
|
||||
if (this.isComm) {
|
||||
if (siteInfo.logo) {
|
||||
siteInfo.logo = `/api/basic/file/download?key=${siteInfo.logo}`;
|
||||
siteInfo.logo = `api/basic/file/download?key=${siteInfo.logo}`;
|
||||
}
|
||||
if (siteInfo.loginLogo) {
|
||||
siteInfo.loginLogo = `/api/basic/file/download?key=${siteInfo.loginLogo}`;
|
||||
siteInfo.loginLogo = `api/basic/file/download?key=${siteInfo.loginLogo}`;
|
||||
}
|
||||
}
|
||||
this.siteInfo = _.merge({}, defaultSiteInfo, siteInfo);
|
||||
@@ -184,6 +213,17 @@ export const useSettingStore = defineStore({
|
||||
useTitle(this.siteInfo.title);
|
||||
}
|
||||
},
|
||||
getBaseUrl() {
|
||||
let url = window.location.href;
|
||||
//只要hash前面的部分
|
||||
url = url.split("#")[0];
|
||||
return url;
|
||||
},
|
||||
async doBindUrl() {
|
||||
const url = this.getBaseUrl();
|
||||
await basicApi.bindUrl({ url });
|
||||
await this.loadSysSettings();
|
||||
},
|
||||
async checkUrlBound() {
|
||||
const userStore = useUserStore();
|
||||
const settingStore = useSettingStore();
|
||||
@@ -193,22 +233,9 @@ export const useSettingStore = defineStore({
|
||||
|
||||
const bindUrl = this.installInfo.bindUrl;
|
||||
|
||||
function getBaseUrl() {
|
||||
let url = window.location.href;
|
||||
//只要hash前面的部分
|
||||
url = url.split("#")[0];
|
||||
return url;
|
||||
}
|
||||
|
||||
const doBindUrl = async (url: string) => {
|
||||
await basicApi.bindUrl({ url });
|
||||
await this.loadSysSettings();
|
||||
};
|
||||
|
||||
const baseUrl = getBaseUrl();
|
||||
if (!bindUrl) {
|
||||
//绑定url
|
||||
await doBindUrl(baseUrl);
|
||||
await this.doBindUrl();
|
||||
} else {
|
||||
//检查当前url 是否与绑定的url一致
|
||||
const url = window.location.href;
|
||||
@@ -217,7 +244,7 @@ export const useSettingStore = defineStore({
|
||||
title: "URL地址有变化",
|
||||
content: "以后都用这个新地址访问本系统吗?",
|
||||
onOk: async () => {
|
||||
await doBindUrl(baseUrl);
|
||||
await this.doBindUrl();
|
||||
},
|
||||
okText: "是的,继续",
|
||||
cancelText: "不是,回到原来的地址",
|
||||
@@ -228,6 +255,14 @@ export const useSettingStore = defineStore({
|
||||
}
|
||||
}
|
||||
},
|
||||
async loadProductInfo() {
|
||||
try {
|
||||
const productInfo = await basicApi.getProductInfo();
|
||||
merge(this.productInfo, productInfo);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
async init() {
|
||||
await this.loadSysSettings();
|
||||
},
|
||||
@@ -236,6 +271,7 @@ export const useSettingStore = defineStore({
|
||||
return;
|
||||
}
|
||||
await this.init();
|
||||
this.loadProductInfo();
|
||||
this.inited = true;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -41,6 +41,12 @@ export async function register(user: RegisterReq): Promise<UserInfoRes> {
|
||||
data: user,
|
||||
});
|
||||
}
|
||||
export async function logout() {
|
||||
return await request({
|
||||
url: "/logout",
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
export async function login(data: LoginReq): Promise<LoginRes> {
|
||||
//如果开启了登录与权限模块,则真实登录
|
||||
|
||||
@@ -108,9 +108,12 @@ export const useUserStore = defineStore({
|
||||
/**
|
||||
* @description: logout
|
||||
*/
|
||||
logout(goLogin = true) {
|
||||
async logout(goLogin = true, from401 = false) {
|
||||
this.resetState();
|
||||
resetAllStores();
|
||||
if (!from401) {
|
||||
await UserApi.logout(); //主要是清空cookie
|
||||
}
|
||||
goLogin && router.push("/login");
|
||||
mitter.emit("app.logout");
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import { routerUtils } from "./util.router";
|
||||
import { treeUtils } from "./util.tree";
|
||||
import { hashUtils } from "./util.hash";
|
||||
import { amountUtils } from "./util.amount";
|
||||
import { cache } from "./util.cache";
|
||||
export const util = {
|
||||
...envs,
|
||||
...sites,
|
||||
@@ -17,5 +18,6 @@ export const util = {
|
||||
tree: treeUtils,
|
||||
hash: hashUtils,
|
||||
amount: amountUtils,
|
||||
cache,
|
||||
};
|
||||
export const utils = util;
|
||||
|
||||
17
packages/ui/certd-client/src/utils/util.cache.ts
Normal file
17
packages/ui/certd-client/src/utils/util.cache.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export class Cache {
|
||||
bucket: Record<string, any> = {};
|
||||
|
||||
async get(key: string) {
|
||||
return this.bucket[key];
|
||||
}
|
||||
|
||||
async set(key: string, value: any, ttl?: number) {
|
||||
this.bucket[key] = value;
|
||||
}
|
||||
|
||||
async del(key: string) {
|
||||
delete this.bucket[key];
|
||||
}
|
||||
}
|
||||
|
||||
export const cache = new Cache();
|
||||
@@ -127,6 +127,7 @@ const doAuthenticatorSave = async (form: any) => {
|
||||
message: "保存成功",
|
||||
});
|
||||
authenticatorForm.open = false;
|
||||
formState.authenticator.verified = true;
|
||||
};
|
||||
|
||||
function onAuthenticatorEnabledChanged(value: any) {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<a-descriptions title="" bordered>
|
||||
<a-descriptions-item label="用户名">{{ userInfo.username }}</a-descriptions-item>
|
||||
<a-descriptions-item label="头像">
|
||||
<a-avatar v-if="userInfo.avatar" size="large" :src="'/api/basic/file/download?&key=' + userInfo.avatar" style="background-color: #eee"> </a-avatar>
|
||||
<a-avatar v-if="userInfo.avatar" size="large" :src="'api/basic/file/download?&key=' + userInfo.avatar" style="background-color: #eee"> </a-avatar>
|
||||
<a-avatar v-else size="large" style="background-color: #00b4f5">
|
||||
{{ userInfo.username }}
|
||||
</a-avatar>
|
||||
|
||||
@@ -7,7 +7,7 @@ export function createNotificationApi() {
|
||||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query
|
||||
data: query,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -15,7 +15,7 @@ export function createNotificationApi() {
|
||||
return await request({
|
||||
url: apiPrefix + "/add",
|
||||
method: "post",
|
||||
data: obj
|
||||
data: obj,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@ export function createNotificationApi() {
|
||||
return await request({
|
||||
url: apiPrefix + "/update",
|
||||
method: "post",
|
||||
data: obj
|
||||
data: obj,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -31,7 +31,7 @@ export function createNotificationApi() {
|
||||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id }
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
|
||||
@@ -39,14 +39,14 @@ export function createNotificationApi() {
|
||||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id }
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
|
||||
async GetOptions(id: number) {
|
||||
return await request({
|
||||
url: apiPrefix + "/options",
|
||||
method: "post"
|
||||
method: "post",
|
||||
});
|
||||
},
|
||||
|
||||
@@ -54,14 +54,14 @@ export function createNotificationApi() {
|
||||
return await request({
|
||||
url: apiPrefix + "/setDefault",
|
||||
method: "post",
|
||||
params: { id }
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
|
||||
async GetDefaultId() {
|
||||
return await request({
|
||||
url: apiPrefix + "/getDefaultId",
|
||||
method: "post"
|
||||
method: "post",
|
||||
});
|
||||
},
|
||||
|
||||
@@ -69,14 +69,14 @@ export function createNotificationApi() {
|
||||
return await request({
|
||||
url: apiPrefix + "/simpleInfo",
|
||||
method: "post",
|
||||
params: { id }
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
|
||||
async GetDefineTypes() {
|
||||
return await request({
|
||||
url: apiPrefix + "/getTypeDict",
|
||||
method: "post"
|
||||
method: "post",
|
||||
});
|
||||
},
|
||||
|
||||
@@ -84,7 +84,7 @@ export function createNotificationApi() {
|
||||
return await request({
|
||||
url: apiPrefix + "/define",
|
||||
method: "post",
|
||||
params: { type }
|
||||
params: { type },
|
||||
});
|
||||
},
|
||||
|
||||
@@ -92,15 +92,15 @@ export function createNotificationApi() {
|
||||
return await request({
|
||||
url: apiPrefix + "/defineByType",
|
||||
method: "post",
|
||||
params: { type }
|
||||
params: { type },
|
||||
});
|
||||
},
|
||||
async GetOrCreateDefault(param: { email: any }) {
|
||||
return await request({
|
||||
url: apiPrefix + "/getOrCreateDefault",
|
||||
method: "post",
|
||||
data: param
|
||||
data: param,
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -122,3 +122,11 @@ export async function GetCert(pipelineId: number): Promise<CertInfo> {
|
||||
params: { id: pipelineId },
|
||||
});
|
||||
}
|
||||
|
||||
export async function ReadCertDetail(crt: string): Promise<any> {
|
||||
return await request({
|
||||
url: certApiPrefix + "/readCertDetail",
|
||||
method: "post",
|
||||
data: { crt },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import { computed, inject } from "vue";
|
||||
import { computed, inject, watch, ref } from "vue";
|
||||
import { useCertUpload } from "./use";
|
||||
import { getAllDomainsFromCrt } from "/@/views/certd/pipeline/utils";
|
||||
|
||||
@@ -27,19 +27,36 @@ const emit = defineEmits(["updated", "update:modelValue"]);
|
||||
|
||||
const { openUpdateCertDialog } = useCertUpload();
|
||||
|
||||
const domain = computed(() => {
|
||||
if (!props.modelValue?.crt) {
|
||||
return "";
|
||||
}
|
||||
const domains = getAllDomainsFromCrt(props.modelValue?.crt);
|
||||
const domainsRef = ref([]);
|
||||
|
||||
return domains[0];
|
||||
watch(
|
||||
() => {
|
||||
return props.modelValue?.crt;
|
||||
},
|
||||
async crt => {
|
||||
if (crt) {
|
||||
domainsRef.value = await getAllDomainsFromCrt(crt);
|
||||
} else {
|
||||
domainsRef.value = [];
|
||||
}
|
||||
|
||||
emit("updated", { domains: domainsRef.value });
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
);
|
||||
|
||||
const domain = computed(() => {
|
||||
if (domainsRef.value && domainsRef.value.length > 0) {
|
||||
return domainsRef.value[0];
|
||||
}
|
||||
|
||||
return "";
|
||||
});
|
||||
|
||||
function onUpdated(res: { uploadCert: any }) {
|
||||
async function onUpdated(res: { uploadCert: any }) {
|
||||
emit("update:modelValue", res.uploadCert);
|
||||
const domains = getAllDomainsFromCrt(res.uploadCert.crt);
|
||||
emit("updated", { domains });
|
||||
}
|
||||
|
||||
const pipeline: any = inject("pipeline");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { compute, useFormWrapper } from "@fast-crud/fast-crud";
|
||||
import { compute, dict, useFormWrapper } from "@fast-crud/fast-crud";
|
||||
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
|
||||
import { cloneDeep, omit } from "lodash-es";
|
||||
import { useReference } from "/@/use/use-refrence";
|
||||
@@ -8,6 +8,7 @@ import { checkPipelineLimit, getAllDomainsFromCrt } from "/@/views/certd/pipelin
|
||||
import { useRouter } from "vue-router";
|
||||
import { nanoid } from "nanoid";
|
||||
import { usePluginStore } from "/@/store/plugin";
|
||||
import GroupSelector from "/@/views/certd/pipeline/group/group-selector.vue";
|
||||
|
||||
export function useCertUpload() {
|
||||
const { openCrudFormDialog } = useFormWrapper();
|
||||
@@ -90,7 +91,7 @@ export function useCertUpload() {
|
||||
return inputs;
|
||||
}
|
||||
|
||||
async function openUploadCreateDialog() {
|
||||
async function openUploadCreateDialog(req: { defaultGroupId?: number }) {
|
||||
//检查是否流水线数量超出限制
|
||||
await checkPipelineLimit();
|
||||
|
||||
@@ -102,7 +103,11 @@ export function useCertUpload() {
|
||||
return wrapperRef.value.getFormData();
|
||||
}
|
||||
const inputs = await buildUploadCertPluginInputs(getFormData);
|
||||
|
||||
const groupDictRef = dict({
|
||||
url: "/pi/pipeline/group/all",
|
||||
value: "id",
|
||||
label: "name",
|
||||
});
|
||||
function createCrudOptions() {
|
||||
return {
|
||||
crudOptions: {
|
||||
@@ -127,6 +132,19 @@ export function useCertUpload() {
|
||||
helper: "任务执行失败实时提醒",
|
||||
},
|
||||
},
|
||||
groupId: {
|
||||
title: "流水线分组",
|
||||
type: "dict-select",
|
||||
dict: groupDictRef,
|
||||
form: {
|
||||
component: {
|
||||
name: GroupSelector,
|
||||
vModel: "modelValue",
|
||||
},
|
||||
value: req.defaultGroupId || undefined,
|
||||
order: 9999,
|
||||
},
|
||||
},
|
||||
},
|
||||
form: {
|
||||
wrapper: {
|
||||
@@ -135,7 +153,7 @@ export function useCertUpload() {
|
||||
},
|
||||
async doSubmit({ form }: any) {
|
||||
const cert = form.uploadCert;
|
||||
const domains = getAllDomainsFromCrt(cert.crt);
|
||||
const domains = await getAllDomainsFromCrt(cert.crt);
|
||||
|
||||
const notifications = [];
|
||||
if (form.notification != null) {
|
||||
@@ -191,6 +209,7 @@ export function useCertUpload() {
|
||||
content: JSON.stringify(pipeline),
|
||||
keepHistoryCount: 30,
|
||||
type: "cert_upload",
|
||||
groupId: form.groupId,
|
||||
});
|
||||
router.push({
|
||||
path: "/certd/pipeline/detail",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user