mirror of
https://github.com/certd/certd.git
synced 2026-04-14 20:40:53 +08:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4b6580247 | ||
|
|
84fb1c5127 | ||
|
|
ddfd0fb81d | ||
|
|
37edbf5824 | ||
|
|
e15212bf49 | ||
|
|
6a0cc1b1f3 | ||
|
|
0e8339c701 | ||
|
|
5d71a4dbde | ||
|
|
0b78030c59 | ||
|
|
24237c16bf | ||
|
|
c48da5dea7 | ||
|
|
a12b824339 | ||
|
|
c4a743189e | ||
|
|
85f9ef35f6 | ||
|
|
6de220e38a | ||
|
|
0d455d8c2f | ||
|
|
f7b0b44ef6 | ||
|
|
81282a9c88 | ||
|
|
a9b302e38d | ||
|
|
1fe4c367f7 | ||
|
|
2de7583900 | ||
|
|
356703c83e | ||
|
|
1cae709b2b | ||
|
|
46a492248f | ||
|
|
d876ea6711 | ||
|
|
b40b4c3cfd | ||
|
|
44980d6c46 | ||
|
|
442f9647a2 | ||
|
|
a06ef07178 | ||
|
|
0c2ea5da4c | ||
|
|
45814ceb49 | ||
|
|
41f4617e66 | ||
|
|
a463711b03 | ||
|
|
3a147141b1 | ||
|
|
aea1c13bd3 | ||
|
|
9cc4c017ae | ||
|
|
88022747be | ||
|
|
ebb292a2f7 | ||
|
|
818998259d | ||
|
|
36b02c2cec | ||
|
|
e6195ade3e | ||
|
|
231a875bb4 | ||
|
|
378c777a38 | ||
|
|
8ef63916ef | ||
|
|
f32ecdf5f1 | ||
|
|
94739b9b8e |
44
CHANGELOG.md
44
CHANGELOG.md
@@ -3,6 +3,50 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **flexcdn:** fix cert upload and skipSslVerify required ([c48da5d](https://github.com/certd/certd/commit/c48da5dea7f0f0cdeae643b106b4a678acc3b14b))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 阿里云CLB支持部署到扩展域名 ([0e8339c](https://github.com/certd/certd/commit/0e8339c70190890d449099e1d26e5ed06ff135fb))
|
||||||
|
* 优化流水线名称过长时的显示 ([6a0cc1b](https://github.com/certd/certd/commit/6a0cc1b1f3ad508f9e4093b3b682b163f12389eb))
|
||||||
|
* 支持部署到飞牛OS ([ddfd0fb](https://github.com/certd/certd/commit/ddfd0fb81d6638352920261065f1ab8e27bdd564))
|
||||||
|
* 支持日志写入文件 ([37edbf5](https://github.com/certd/certd/commit/37edbf5824d6aaae68ea1ef7259c6f739d418d2c))
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复Farcdn证书有效期错误的问题 ([1fe4c36](https://github.com/certd/certd/commit/1fe4c367f7128de9ba5e3395ae06bc81e63a7d5a))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 不止证书自动化,插件解锁无限可能 ([a9b302e](https://github.com/certd/certd/commit/a9b302e38d3328d75df8b2da3d8b914851e55e9c))
|
||||||
|
* 邮箱支持保存和选择 ([f7b0b44](https://github.com/certd/certd/commit/f7b0b44ef6044bec36510a6f0b06d8dca5bfce49))
|
||||||
|
* 支持github 新版本检查并发布通知 ([356703c](https://github.com/certd/certd/commit/356703c83ea18c6efb8931402e181280d7b7e696))
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 更新 1panel API 版本支持v1/v2设置 ([e6195ad](https://github.com/certd/certd/commit/e6195ade3ec54b138825b8d6738f86eb8afdd720))
|
||||||
|
* 同步更新namesilo接口,修复无法创建和删除dns记录的问题 ([36b02c2](https://github.com/certd/certd/commit/36b02c2cec145c13d4ef29d49aba5b6b4f697df2))
|
||||||
|
* 修复阿里云 esa 证书获取站点列表错误的问题 ([0c2ea5d](https://github.com/certd/certd/commit/0c2ea5da4c836f8a0df132a3f22d399bd9ee1de9))
|
||||||
|
* 修复部署到华为cdn,子账号ak查询不到域名的bug ([ebb292a](https://github.com/certd/certd/commit/ebb292a2f7a425c1bc810f59468beb3f1d5bc3f0))
|
||||||
|
* 修复证书申请任务无法修改dns提供商类型的bug ([8802274](https://github.com/certd/certd/commit/88022747bebe2054223e0241d68d410771405e68))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 关闭腾讯云证书通知提醒 ([231a875](https://github.com/certd/certd/commit/231a875bb481420c39bf76ec9ff4e50954ab9fe4))
|
||||||
|
* 优化站点选择组件,切换选择时不刷新列表 ([3a14714](https://github.com/certd/certd/commit/3a147141b1a5d67c92a5ce88a5313eaa62859e03))
|
||||||
|
* 优化站点ip检查 ([a463711](https://github.com/certd/certd/commit/a463711b03a20120f2a298be15d71ca152d27f21))
|
||||||
|
* 站点监控支持监控IP ([9cc4c01](https://github.com/certd/certd/commit/9cc4c017ae646a18284e732769b82636feda01d3))
|
||||||
|
* 支持批量重新运行 ([8189982](https://github.com/certd/certd/commit/818998259ddc75e722196ac5c365038818539b9b))
|
||||||
|
* farcdn优化 ([a06ef07](https://github.com/certd/certd/commit/a06ef07178ed73c537e21c7d57e5e5144d2c056d))
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
23:45
|
00:57
|
||||||
|
|||||||
@@ -3,6 +3,44 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复Farcdn证书有效期错误的问题 ([1fe4c36](https://github.com/certd/certd/commit/1fe4c367f7128de9ba5e3395ae06bc81e63a7d5a))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 不止证书自动化,插件解锁无限可能 ([a9b302e](https://github.com/certd/certd/commit/a9b302e38d3328d75df8b2da3d8b914851e55e9c))
|
||||||
|
* 邮箱支持保存和选择 ([f7b0b44](https://github.com/certd/certd/commit/f7b0b44ef6044bec36510a6f0b06d8dca5bfce49))
|
||||||
|
* 支持github 新版本检查并发布通知 ([356703c](https://github.com/certd/certd/commit/356703c83ea18c6efb8931402e181280d7b7e696))
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 更新 1panel API 版本支持v1/v2设置 ([e6195ad](https://github.com/certd/certd/commit/e6195ade3ec54b138825b8d6738f86eb8afdd720))
|
||||||
|
* 同步更新namesilo接口,修复无法创建和删除dns记录的问题 ([36b02c2](https://github.com/certd/certd/commit/36b02c2cec145c13d4ef29d49aba5b6b4f697df2))
|
||||||
|
* 修复阿里云 esa 证书获取站点列表错误的问题 ([0c2ea5d](https://github.com/certd/certd/commit/0c2ea5da4c836f8a0df132a3f22d399bd9ee1de9))
|
||||||
|
* 修复部署到华为cdn,子账号ak查询不到域名的bug ([ebb292a](https://github.com/certd/certd/commit/ebb292a2f7a425c1bc810f59468beb3f1d5bc3f0))
|
||||||
|
* 修复证书申请任务无法修改dns提供商类型的bug ([8802274](https://github.com/certd/certd/commit/88022747bebe2054223e0241d68d410771405e68))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 关闭腾讯云证书通知提醒 ([231a875](https://github.com/certd/certd/commit/231a875bb481420c39bf76ec9ff4e50954ab9fe4))
|
||||||
|
* 优化站点选择组件,切换选择时不刷新列表 ([3a14714](https://github.com/certd/certd/commit/3a147141b1a5d67c92a5ce88a5313eaa62859e03))
|
||||||
|
* 优化站点ip检查 ([a463711](https://github.com/certd/certd/commit/a463711b03a20120f2a298be15d71ca152d27f21))
|
||||||
|
* 站点监控支持监控IP ([9cc4c01](https://github.com/certd/certd/commit/9cc4c017ae646a18284e732769b82636feda01d3))
|
||||||
|
* 支持批量重新运行 ([8189982](https://github.com/certd/certd/commit/818998259ddc75e722196ac5c365038818539b9b))
|
||||||
|
* farcdn优化 ([a06ef07](https://github.com/certd/certd/commit/a06ef07178ed73c537e21c7d57e5e5144d2c056d))
|
||||||
|
|
||||||
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 优化阿里云DCDN插件,支持多选 ([b091657](https://github.com/certd/certd/commit/b091657b5c537acf2442a2bfc345d0a77f5e2c50))
|
||||||
|
* 支持部署到farcdn ([e08cf57](https://github.com/certd/certd/commit/e08cf57b72128998f487ab6469868052fbce0dba))
|
||||||
|
|
||||||
## [1.34.6](https://github.com/certd/certd/compare/v1.34.5...v1.34.6) (2025-05-25)
|
## [1.34.6](https://github.com/certd/certd/compare/v1.34.5...v1.34.6) (2025-05-25)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -9,5 +9,5 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"npmClient": "pnpm",
|
"npmClient": "pnpm",
|
||||||
"version": "1.34.7"
|
"version": "1.34.10"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,18 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/publishlab/node-acme-client/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/publishlab/node-acme-client/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/publishlab/node-acme-client/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
## [1.34.7](https://github.com/publishlab/node-acme-client/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/publishlab/node-acme-client/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/acme-client
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "Simple and unopinionated ACME client",
|
"description": "Simple and unopinionated ACME client",
|
||||||
"private": false,
|
"private": false,
|
||||||
"author": "nmorsman",
|
"author": "nmorsman",
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "scr/index.js",
|
"module": "scr/index.js",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"types"
|
"types"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.34.7",
|
"@certd/basic": "^1.34.10",
|
||||||
"@peculiar/x509": "^1.11.0",
|
"@peculiar/x509": "^1.11.0",
|
||||||
"asn1js": "^3.0.5",
|
"asn1js": "^3.0.5",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.7.2",
|
||||||
@@ -69,5 +69,5 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||||
},
|
},
|
||||||
"gitHead": "8478ce25f11f8e13b9be508cf44d7090f5c8a663"
|
"gitHead": "85f9ef35f650d6ed1125f9d5eecee08cabc7fe8a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,21 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 支持部署到飞牛OS ([ddfd0fb](https://github.com/certd/certd/commit/ddfd0fb81d6638352920261065f1ab8e27bdd564))
|
||||||
|
* 支持日志写入文件 ([37edbf5](https://github.com/certd/certd/commit/37edbf5824d6aaae68ea1ef7259c6f739d418d2c))
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/basic
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/basic
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/basic
|
**Note:** Version bump only for package @certd/basic
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
23:20
|
23:54
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/basic",
|
"name": "@certd/basic",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -45,5 +45,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "8478ce25f11f8e13b9be508cf44d7090f5c8a663"
|
"gitHead": "85f9ef35f650d6ed1125f9d5eecee08cabc7fe8a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,16 +18,31 @@ const OutputAppender = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let logFilePath = "./logs/app.log";
|
||||||
export function resetLogConfigure() {
|
export function resetLogConfigure() {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
log4js.configure({
|
log4js.configure({
|
||||||
appenders: { std: { type: "stdout" }, output: { type: OutputAppender } },
|
appenders: {
|
||||||
categories: { default: { appenders: ["std"], level: "info" }, pipeline: { appenders: ["std", "output"], level: "info" } },
|
std: { type: "stdout" },
|
||||||
|
output: { type: OutputAppender },
|
||||||
|
file: {
|
||||||
|
type: "dateFile",
|
||||||
|
filename: logFilePath,
|
||||||
|
keepFileExt: true,
|
||||||
|
compress: true,
|
||||||
|
numBackups: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
categories: { default: { appenders: ["std", "file"], level: "info" }, pipeline: { appenders: ["std", "file", "output"], level: "info" } },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
resetLogConfigure();
|
resetLogConfigure();
|
||||||
export const logger = log4js.getLogger("default");
|
export const logger = log4js.getLogger("default");
|
||||||
|
|
||||||
|
export function resetLogFilePath(filePath: string) {
|
||||||
|
logFilePath = filePath;
|
||||||
|
resetLogConfigure();
|
||||||
|
}
|
||||||
export function buildLogger(write: (text: string) => void) {
|
export function buildLogger(write: (text: string) => void) {
|
||||||
const logger = log4js.getLogger("pipeline");
|
const logger = log4js.getLogger("pipeline");
|
||||||
const _secrets: string[] = [];
|
const _secrets: string[] = [];
|
||||||
@@ -41,8 +56,15 @@ export function buildLogger(write: (text: string) => void) {
|
|||||||
if (item == null) {
|
if (item == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//换成同长度的*号, item可能有多行
|
if (item.includes(text)) {
|
||||||
text = text.replaceAll(item, "*".repeat(item.length));
|
//整个包含
|
||||||
|
text = "*".repeat(text.length);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (text.includes(item)) {
|
||||||
|
//换成同长度的*号, item可能有多行
|
||||||
|
text = text.replaceAll(item, "*".repeat(item.length));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
write(text);
|
write(text);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -97,11 +97,17 @@ export function createAxiosService({ logger }: { logger: Logger }) {
|
|||||||
if (config.logRes == null) {
|
if (config.logRes == null) {
|
||||||
config.logRes = false;
|
config.logRes = false;
|
||||||
}
|
}
|
||||||
|
if (config.logData == null) {
|
||||||
|
config.logData = false;
|
||||||
|
}
|
||||||
|
|
||||||
logger.info(`http request:${config.url},method:${config.method}`);
|
logger.info(`http request:${config.url},method:${config.method}`);
|
||||||
if (config.logParams !== false && config.params) {
|
if (config.logParams !== false && config.params) {
|
||||||
logger.info(`params:${JSON.stringify(config.params)}`);
|
logger.info(`params:${JSON.stringify(config.params)}`);
|
||||||
}
|
}
|
||||||
|
if (config.logData !== false && config.data) {
|
||||||
|
logger.info(`data:${JSON.stringify(config.data)}`);
|
||||||
|
}
|
||||||
if (config.timeout == null) {
|
if (config.timeout == null) {
|
||||||
config.timeout = 15000;
|
config.timeout = 15000;
|
||||||
}
|
}
|
||||||
@@ -226,6 +232,7 @@ export type HttpRequestConfig<D = any> = {
|
|||||||
skipCheckRes?: boolean;
|
skipCheckRes?: boolean;
|
||||||
logParams?: boolean;
|
logParams?: boolean;
|
||||||
logRes?: boolean;
|
logRes?: boolean;
|
||||||
|
logData?: boolean;
|
||||||
httpProxy?: string;
|
httpProxy?: string;
|
||||||
returnOriginRes?: boolean;
|
returnOriginRes?: boolean;
|
||||||
} & AxiosRequestConfig<D>;
|
} & AxiosRequestConfig<D>;
|
||||||
|
|||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 支持部署到飞牛OS ([ddfd0fb](https://github.com/certd/certd/commit/ddfd0fb81d6638352920261065f1ab8e27bdd564))
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 优化站点选择组件,切换选择时不刷新列表 ([3a14714](https://github.com/certd/certd/commit/3a147141b1a5d67c92a5ce88a5313eaa62859e03))
|
||||||
|
* 站点监控支持监控IP ([9cc4c01](https://github.com/certd/certd/commit/9cc4c017ae646a18284e732769b82636feda01d3))
|
||||||
|
* 支持批量重新运行 ([8189982](https://github.com/certd/certd/commit/818998259ddc75e722196ac5c365038818539b9b))
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/pipeline
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/pipeline",
|
"name": "@certd/pipeline",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -17,8 +17,8 @@
|
|||||||
"pub": "npm publish"
|
"pub": "npm publish"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.34.7",
|
"@certd/basic": "^1.34.10",
|
||||||
"@certd/plus-core": "^1.34.7",
|
"@certd/plus-core": "^1.34.10",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"reflect-metadata": "^0.1.13"
|
"reflect-metadata": "^0.1.13"
|
||||||
@@ -44,5 +44,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "8478ce25f11f8e13b9be508cf44d7090f5c8a663"
|
"gitHead": "85f9ef35f650d6ed1125f9d5eecee08cabc7fe8a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,3 +2,18 @@ import { IContext } from "../core/index.js";
|
|||||||
|
|
||||||
export type UserContext = IContext;
|
export type UserContext = IContext;
|
||||||
export type PipelineContext = IContext;
|
export type PipelineContext = IContext;
|
||||||
|
|
||||||
|
export type PageReq = {
|
||||||
|
offset?: number;
|
||||||
|
limit?: number;
|
||||||
|
searchKey?: string;
|
||||||
|
// sortBy?: string;
|
||||||
|
// sortOrder?: "asc" | "desc";
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PageRes = {
|
||||||
|
offset?: number;
|
||||||
|
limit?: number;
|
||||||
|
total?: string;
|
||||||
|
list: any[];
|
||||||
|
};
|
||||||
|
|||||||
@@ -489,7 +489,15 @@ export class Executor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param stepId 如果==ALL 清除所有
|
||||||
|
*/
|
||||||
clearLastStatus(stepId: string) {
|
clearLastStatus(stepId: string) {
|
||||||
|
if (stepId === "ALL") {
|
||||||
|
this.lastStatusMap.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.lastStatusMap.clearById(stepId);
|
this.lastStatusMap.clearById(stepId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ export class RunnableCollection {
|
|||||||
if (runnable?.status) {
|
if (runnable?.status) {
|
||||||
runnable.status.status = ResultType.none;
|
runnable.status.status = ResultType.none;
|
||||||
runnable.status.result = ResultType.none;
|
runnable.status.result = ResultType.none;
|
||||||
|
runnable.status.output = {};
|
||||||
runnable.status.inputHash = "";
|
runnable.status.inputHash = "";
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
runnable.input = {};
|
runnable.input = {};
|
||||||
|
|||||||
@@ -152,6 +152,16 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
|||||||
this.logger = ctx.logger;
|
this.logger = ctx.logger;
|
||||||
this.accessService = ctx.accessService;
|
this.accessService = ctx.accessService;
|
||||||
this.http = ctx.http;
|
this.http = ctx.http;
|
||||||
|
// 将证书加入secret
|
||||||
|
// @ts-ignore
|
||||||
|
if (this.cert && this.cert.crt && this.cert.key) {
|
||||||
|
//有证书
|
||||||
|
// @ts-ignore
|
||||||
|
const cert: any = this.cert;
|
||||||
|
this.registerSecret(cert.crt);
|
||||||
|
this.registerSecret(cert.key);
|
||||||
|
this.registerSecret(cert.one);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAccess<T = any>(accessId: string | number, isCommon = false) {
|
async getAccess<T = any>(accessId: string | number, isCommon = false) {
|
||||||
@@ -186,6 +196,14 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
|||||||
return res as T;
|
return res as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerSecret(value: string) {
|
||||||
|
// @ts-ignore
|
||||||
|
if (this.logger?.addSecret) {
|
||||||
|
// @ts-ignore
|
||||||
|
this.logger.addSecret(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
randomFileId() {
|
randomFileId() {
|
||||||
return Math.random().toString(36).substring(2, 9);
|
return Math.random().toString(36).substring(2, 9);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,18 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-huawei
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-huawei",
|
"name": "@certd/lib-huawei",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"main": "./dist/bundle.js",
|
"main": "./dist/bundle.js",
|
||||||
"module": "./dist/bundle.js",
|
"module": "./dist/bundle.js",
|
||||||
"types": "./dist/d/index.d.ts",
|
"types": "./dist/d/index.d.ts",
|
||||||
@@ -24,5 +24,5 @@
|
|||||||
"prettier": "^2.8.8",
|
"prettier": "^2.8.8",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
},
|
},
|
||||||
"gitHead": "8478ce25f11f8e13b9be508cf44d7090f5c8a663"
|
"gitHead": "85f9ef35f650d6ed1125f9d5eecee08cabc7fe8a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,18 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-iframe
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-iframe",
|
"name": "@certd/lib-iframe",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -31,5 +31,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "8478ce25f11f8e13b9be508cf44d7090f5c8a663"
|
"gitHead": "85f9ef35f650d6ed1125f9d5eecee08cabc7fe8a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,18 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/jdcloud
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/jdcloud",
|
"name": "@certd/jdcloud",
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"description": "jdcloud openApi sdk",
|
"description": "jdcloud openApi sdk",
|
||||||
"main": "./dist/bundle.js",
|
"main": "./dist/bundle.js",
|
||||||
"module": "./dist/bundle.js",
|
"module": "./dist/bundle.js",
|
||||||
@@ -61,5 +61,5 @@
|
|||||||
"fetch"
|
"fetch"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gitHead": "8478ce25f11f8e13b9be508cf44d7090f5c8a663"
|
"gitHead": "85f9ef35f650d6ed1125f9d5eecee08cabc7fe8a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,18 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-k8s
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-k8s",
|
"name": "@certd/lib-k8s",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
"pub": "npm publish"
|
"pub": "npm publish"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.34.7",
|
"@certd/basic": "^1.34.10",
|
||||||
"@kubernetes/client-node": "0.21.0"
|
"@kubernetes/client-node": "0.21.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -32,5 +32,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "8478ce25f11f8e13b9be508cf44d7090f5c8a663"
|
"gitHead": "85f9ef35f650d6ed1125f9d5eecee08cabc7fe8a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,20 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 邮箱支持保存和选择 ([f7b0b44](https://github.com/certd/certd/commit/f7b0b44ef6044bec36510a6f0b06d8dca5bfce49))
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-server
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-server",
|
"name": "@certd/lib-server",
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"description": "midway with flyway, sql upgrade way ",
|
"description": "midway with flyway, sql upgrade way ",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -27,10 +27,10 @@
|
|||||||
],
|
],
|
||||||
"license": "AGPL",
|
"license": "AGPL",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^1.34.7",
|
"@certd/acme-client": "^1.34.10",
|
||||||
"@certd/basic": "^1.34.7",
|
"@certd/basic": "^1.34.10",
|
||||||
"@certd/pipeline": "^1.34.7",
|
"@certd/pipeline": "^1.34.10",
|
||||||
"@certd/plus-core": "^1.34.7",
|
"@certd/plus-core": "^1.34.10",
|
||||||
"@midwayjs/cache": "~3.14.0",
|
"@midwayjs/cache": "~3.14.0",
|
||||||
"@midwayjs/core": "~3.20.3",
|
"@midwayjs/core": "~3.20.3",
|
||||||
"@midwayjs/i18n": "~3.20.3",
|
"@midwayjs/i18n": "~3.20.3",
|
||||||
@@ -61,5 +61,5 @@
|
|||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.11",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "8478ce25f11f8e13b9be508cf44d7090f5c8a663"
|
"gitHead": "85f9ef35f650d6ed1125f9d5eecee08cabc7fe8a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,12 @@ import { InjectEntityModel } from '@midwayjs/typeorm';
|
|||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import { SysSettingsEntity } from '../entity/sys-settings.js';
|
import { SysSettingsEntity } from '../entity/sys-settings.js';
|
||||||
import { BaseSettings, SysInstallInfo, SysPrivateSettings, SysPublicSettings, SysSecret, SysSecretBackup } from './models.js';
|
import { BaseSettings, SysInstallInfo, SysPrivateSettings, SysPublicSettings, SysSecret, SysSecretBackup } from './models.js';
|
||||||
import * as _ from 'lodash-es';
|
|
||||||
import { BaseService } from '../../../basic/index.js';
|
import { BaseService } from '../../../basic/index.js';
|
||||||
import { cache, logger, setGlobalProxy } from '@certd/basic';
|
import { cache, logger, setGlobalProxy } from '@certd/basic';
|
||||||
import * as dns from 'node:dns';
|
import * as dns from 'node:dns';
|
||||||
|
import {mergeUtils} from "@certd/basic";
|
||||||
|
const {merge} = mergeUtils;
|
||||||
/**
|
/**
|
||||||
* 设置
|
* 设置
|
||||||
*/
|
*/
|
||||||
@@ -75,7 +76,7 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
|
|||||||
}
|
}
|
||||||
let newSetting: T = new type();
|
let newSetting: T = new type();
|
||||||
const savedSettings = await this.getSettingByKey(key);
|
const savedSettings = await this.getSettingByKey(key);
|
||||||
newSetting = _.merge(newSetting, savedSettings);
|
newSetting = merge(newSetting, savedSettings);
|
||||||
await this.saveSetting(newSetting);
|
await this.saveSetting(newSetting);
|
||||||
cache.set(cacheKey, newSetting);
|
cache.set(cacheKey, newSetting);
|
||||||
return newSetting;
|
return newSetting;
|
||||||
|
|||||||
@@ -3,6 +3,18 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/midway-flyway-js",
|
"name": "@certd/midway-flyway-js",
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"description": "midway with flyway, sql upgrade way ",
|
"description": "midway with flyway, sql upgrade way ",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -46,5 +46,5 @@
|
|||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.11",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "8478ce25f11f8e13b9be508cf44d7090f5c8a663"
|
"gitHead": "85f9ef35f650d6ed1125f9d5eecee08cabc7fe8a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 邮箱支持保存和选择 ([f7b0b44](https://github.com/certd/certd/commit/f7b0b44ef6044bec36510a6f0b06d8dca5bfce49))
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复证书申请任务无法修改dns提供商类型的bug ([8802274](https://github.com/certd/certd/commit/88022747bebe2054223e0241d68d410771405e68))
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/plugin-cert
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugin-cert",
|
"name": "@certd/plugin-cert",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -16,10 +16,10 @@
|
|||||||
"pub": "npm publish"
|
"pub": "npm publish"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^1.34.7",
|
"@certd/acme-client": "^1.34.10",
|
||||||
"@certd/basic": "^1.34.7",
|
"@certd/basic": "^1.34.10",
|
||||||
"@certd/pipeline": "^1.34.7",
|
"@certd/pipeline": "^1.34.10",
|
||||||
"@certd/plugin-lib": "^1.34.7",
|
"@certd/plugin-lib": "^1.34.10",
|
||||||
"@google-cloud/publicca": "^1.3.0",
|
"@google-cloud/publicca": "^1.3.0",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
@@ -43,5 +43,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "8478ce25f11f8e13b9be508cf44d7090f5c8a663"
|
"gitHead": "85f9ef35f650d6ed1125f9d5eecee08cabc7fe8a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export abstract class CertApplyBasePlugin extends CertApplyBaseConvertPlugin {
|
|||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: "邮箱",
|
title: "邮箱",
|
||||||
component: {
|
component: {
|
||||||
name: "a-input",
|
name: "email-selector",
|
||||||
vModel: "value",
|
vModel: "value",
|
||||||
},
|
},
|
||||||
rules: [{ type: "email", message: "请输入正确的邮箱" }],
|
rules: [{ type: "email", message: "请输入正确的邮箱" }],
|
||||||
|
|||||||
@@ -102,11 +102,11 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
|||||||
return form.challengeType === 'dns'
|
return form.challengeType === 'dns'
|
||||||
}),
|
}),
|
||||||
component:{
|
component:{
|
||||||
on:{
|
onSelectedChange: ctx.compute(({form})=>{
|
||||||
selectedChange({form,$event}){
|
return ($event)=>{
|
||||||
form.dnsProviderAccessType = $event.accessType
|
form.dnsProviderAccessType = $event.accessType
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
|||||||
@@ -3,6 +3,27 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 支持部署到飞牛OS ([ddfd0fb](https://github.com/certd/certd/commit/ddfd0fb81d6638352920261065f1ab8e27bdd564))
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-lib
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复阿里云 esa 证书获取站点列表错误的问题 ([0c2ea5d](https://github.com/certd/certd/commit/0c2ea5da4c836f8a0df132a3f22d399bd9ee1de9))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 关闭腾讯云证书通知提醒 ([231a875](https://github.com/certd/certd/commit/231a875bb481420c39bf76ec9ff4e50954ab9fe4))
|
||||||
|
* 优化站点选择组件,切换选择时不刷新列表 ([3a14714](https://github.com/certd/certd/commit/3a147141b1a5d67c92a5ce88a5313eaa62859e03))
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/plugin-lib
|
**Note:** Version bump only for package @certd/plugin-lib
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugin-lib",
|
"name": "@certd/plugin-lib",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
"@alicloud/pop-core": "^1.7.10",
|
"@alicloud/pop-core": "^1.7.10",
|
||||||
"@alicloud/tea-util": "^1.4.10",
|
"@alicloud/tea-util": "^1.4.10",
|
||||||
"@aws-sdk/client-s3": "^3.787.0",
|
"@aws-sdk/client-s3": "^3.787.0",
|
||||||
"@certd/basic": "^1.34.7",
|
"@certd/basic": "^1.34.10",
|
||||||
"@certd/pipeline": "^1.34.7",
|
"@certd/pipeline": "^1.34.10",
|
||||||
"@kubernetes/client-node": "0.21.0",
|
"@kubernetes/client-node": "0.21.0",
|
||||||
"ali-oss": "^6.22.0",
|
"ali-oss": "^6.22.0",
|
||||||
"basic-ftp": "^5.0.5",
|
"basic-ftp": "^5.0.5",
|
||||||
@@ -52,5 +52,5 @@
|
|||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "8478ce25f11f8e13b9be508cf44d7090f5c8a663"
|
"gitHead": "85f9ef35f650d6ed1125f9d5eecee08cabc7fe8a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export type AliyunClientV2Req = {
|
|||||||
pathname?: `/`;
|
pathname?: `/`;
|
||||||
|
|
||||||
data?: any;
|
data?: any;
|
||||||
query?: any;
|
|
||||||
};
|
};
|
||||||
export class AliyunClientV2 {
|
export class AliyunClientV2 {
|
||||||
access: AliyunAccess;
|
access: AliyunAccess;
|
||||||
@@ -32,6 +31,12 @@ export class AliyunClientV2 {
|
|||||||
return this.client;
|
return this.client;
|
||||||
}
|
}
|
||||||
const $OpenApi = await import("@alicloud/openapi-client");
|
const $OpenApi = await import("@alicloud/openapi-client");
|
||||||
|
// const Credential = await import("@alicloud/credentials");
|
||||||
|
// //@ts-ignore
|
||||||
|
// const credential = new Credential.default.default({
|
||||||
|
//
|
||||||
|
// type: "access_key",
|
||||||
|
// });
|
||||||
const config = new $OpenApi.Config({
|
const config = new $OpenApi.Config({
|
||||||
accessKeyId: this.access.accessKeyId,
|
accessKeyId: this.access.accessKeyId,
|
||||||
accessKeySecret: this.access.accessKeySecret,
|
accessKeySecret: this.access.accessKeySecret,
|
||||||
@@ -70,10 +75,7 @@ export class AliyunClientV2 {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const runtime = new $Util.RuntimeOptions({});
|
const runtime = new $Util.RuntimeOptions({});
|
||||||
const request = new $OpenApi.OpenApiRequest({
|
const request = new $OpenApi.OpenApiRequest(req.data);
|
||||||
body: req.data,
|
|
||||||
query: req.query,
|
|
||||||
});
|
|
||||||
// 复制代码运行请自行打印 API 的返回值
|
// 复制代码运行请自行打印 API 的返回值
|
||||||
// 返回值实际为 Map 类型,可从 Map 中获得三类数据:响应体 body、响应头 headers、HTTP 返回的状态码 statusCode。
|
// 返回值实际为 Map 类型,可从 Map 中获得三类数据:响应体 body、响应头 headers、HTTP 返回的状态码 statusCode。
|
||||||
const res = await client.callApi(params, request, runtime);
|
const res = await client.callApi(params, request, runtime);
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ export function createRemoteSelectInputDefine(opts?: {
|
|||||||
rules?: any;
|
rules?: any;
|
||||||
mergeScript?: string;
|
mergeScript?: string;
|
||||||
search?: boolean;
|
search?: boolean;
|
||||||
|
pager?: boolean;
|
||||||
|
component?: any;
|
||||||
}) {
|
}) {
|
||||||
const title = opts?.title || "请选择";
|
const title = opts?.title || "请选择";
|
||||||
const certDomainsInputKey = opts?.certDomainsInputKey || "certDomains";
|
const certDomainsInputKey = opts?.certDomainsInputKey || "certDomains";
|
||||||
@@ -49,6 +51,7 @@ export function createRemoteSelectInputDefine(opts?: {
|
|||||||
const watches = opts?.watches || [];
|
const watches = opts?.watches || [];
|
||||||
const helper = opts?.helper || "请选择";
|
const helper = opts?.helper || "请选择";
|
||||||
const search = opts?.search ?? false;
|
const search = opts?.search ?? false;
|
||||||
|
const pager = opts?.pager ?? false;
|
||||||
let mode = "tags";
|
let mode = "tags";
|
||||||
if (opts.multi === false) {
|
if (opts.multi === false) {
|
||||||
mode = undefined;
|
mode = undefined;
|
||||||
@@ -66,7 +69,9 @@ export function createRemoteSelectInputDefine(opts?: {
|
|||||||
typeName,
|
typeName,
|
||||||
action,
|
action,
|
||||||
search,
|
search,
|
||||||
|
pager,
|
||||||
watches: [certDomainsInputKey, accessIdInputKey, ...watches],
|
watches: [certDomainsInputKey, accessIdInputKey, ...watches],
|
||||||
|
...opts.component,
|
||||||
},
|
},
|
||||||
rules: opts?.rules,
|
rules: opts?.rules,
|
||||||
required: opts.required ?? true,
|
required: opts.required ?? true,
|
||||||
|
|||||||
@@ -165,10 +165,16 @@ export class AsyncSsh2Client {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param script
|
||||||
|
* @param opts {withStdErr 返回{stdOut,stdErr}}
|
||||||
|
*/
|
||||||
async exec(
|
async exec(
|
||||||
script: string,
|
script: string,
|
||||||
opts: {
|
opts: {
|
||||||
throwOnStdErr?: boolean;
|
throwOnStdErr?: boolean;
|
||||||
|
withStdErr?: boolean;
|
||||||
env?: any;
|
env?: any;
|
||||||
} = {}
|
} = {}
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
@@ -193,6 +199,7 @@ export class AsyncSsh2Client {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let data = "";
|
let data = "";
|
||||||
|
let stdErr = "";
|
||||||
let hasErrorLog = false;
|
let hasErrorLog = false;
|
||||||
stream
|
stream
|
||||||
.on("close", (code: any, signal: any) => {
|
.on("close", (code: any, signal: any) => {
|
||||||
@@ -205,7 +212,15 @@ export class AsyncSsh2Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
resolve(data);
|
if (opts.withStdErr === true) {
|
||||||
|
//@ts-ignore
|
||||||
|
resolve({
|
||||||
|
stdErr,
|
||||||
|
stdOut: data,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
resolve(data);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
reject(new Error(data));
|
reject(new Error(data));
|
||||||
}
|
}
|
||||||
@@ -221,7 +236,7 @@ export class AsyncSsh2Client {
|
|||||||
})
|
})
|
||||||
.stderr.on("data", (ret: Buffer) => {
|
.stderr.on("data", (ret: Buffer) => {
|
||||||
const err = this.convert(iconv, ret);
|
const err = this.convert(iconv, ret);
|
||||||
data += err;
|
stdErr += err;
|
||||||
hasErrorLog = true;
|
hasErrorLog = true;
|
||||||
this.logger.error(`[${this.connConf.host}][error]: ` + err.trimEnd());
|
this.logger.error(`[${this.connConf.host}][error]: ` + err.trimEnd());
|
||||||
});
|
});
|
||||||
@@ -323,9 +338,6 @@ export class AsyncSsh2Client {
|
|||||||
|
|
||||||
export class SshClient {
|
export class SshClient {
|
||||||
logger: ILogger;
|
logger: ILogger;
|
||||||
constructor(logger: ILogger) {
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param connectConf
|
* @param connectConf
|
||||||
@@ -382,6 +394,9 @@ export class SshClient {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
constructor(logger: ILogger) {
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
async scpUpload(options: { conn: any; localPath: string; remotePath: string; opts?: { mode?: string } }) {
|
async scpUpload(options: { conn: any; localPath: string; remotePath: string; opts?: { mode?: string } }) {
|
||||||
const { conn, localPath, remotePath } = options;
|
const { conn, localPath, remotePath } = options;
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export class TencentSslClient {
|
|||||||
};
|
};
|
||||||
const ret = await client.UploadCertificate(params);
|
const ret = await client.UploadCertificate(params);
|
||||||
this.checkRet(ret);
|
this.checkRet(ret);
|
||||||
this.logger.info("证书上传成功:tencentCertId=", ret.CertificateId);
|
this.logger.info(`证书[${opts.certName}]上传成功:tencentCertId=`, ret.CertificateId);
|
||||||
await this.switchCertNotify([ret.CertificateId], true);
|
await this.switchCertNotify([ret.CertificateId], true);
|
||||||
return ret.CertificateId;
|
return ret.CertificateId;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,32 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 优化流水线名称过长时的显示 ([6a0cc1b](https://github.com/certd/certd/commit/6a0cc1b1f3ad508f9e4093b3b682b163f12389eb))
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 邮箱支持保存和选择 ([f7b0b44](https://github.com/certd/certd/commit/f7b0b44ef6044bec36510a6f0b06d8dca5bfce49))
|
||||||
|
* 支持github 新版本检查并发布通知 ([356703c](https://github.com/certd/certd/commit/356703c83ea18c6efb8931402e181280d7b7e696))
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复证书申请任务无法修改dns提供商类型的bug ([8802274](https://github.com/certd/certd/commit/88022747bebe2054223e0241d68d410771405e68))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 优化站点选择组件,切换选择时不刷新列表 ([3a14714](https://github.com/certd/certd/commit/3a147141b1a5d67c92a5ce88a5313eaa62859e03))
|
||||||
|
* 优化站点ip检查 ([a463711](https://github.com/certd/certd/commit/a463711b03a20120f2a298be15d71ca152d27f21))
|
||||||
|
* 站点监控支持监控IP ([9cc4c01](https://github.com/certd/certd/commit/9cc4c017ae646a18284e732769b82636feda01d3))
|
||||||
|
* 支持批量重新运行 ([8189982](https://github.com/certd/certd/commit/818998259ddc75e722196ac5c365038818539b9b))
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/ui-client
|
**Note:** Version bump only for package @certd/ui-client
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/ui-client",
|
"name": "@certd/ui-client",
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --open",
|
"dev": "vite --open",
|
||||||
@@ -102,8 +102,8 @@
|
|||||||
"zod-defaults": "^0.1.3"
|
"zod-defaults": "^0.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@certd/lib-iframe": "^1.34.7",
|
"@certd/lib-iframe": "^1.34.10",
|
||||||
"@certd/pipeline": "^1.34.7",
|
"@certd/pipeline": "^1.34.10",
|
||||||
"@rollup/plugin-commonjs": "^25.0.7",
|
"@rollup/plugin-commonjs": "^25.0.7",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@types/chai": "^4.3.12",
|
"@types/chai": "^4.3.12",
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { request } from "/src/api/service";
|
||||||
|
|
||||||
|
export async function EmailList() {
|
||||||
|
return await request({
|
||||||
|
url: "/mine/email/list",
|
||||||
|
method: "post",
|
||||||
|
data: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function EmailDelete(email: string) {
|
||||||
|
return await request({
|
||||||
|
url: "/mine/email/delete",
|
||||||
|
method: "post",
|
||||||
|
data: {
|
||||||
|
email: email,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function EmailAdd(email: string) {
|
||||||
|
return await request({
|
||||||
|
url: "/mine/email/add",
|
||||||
|
method: "post",
|
||||||
|
data: {
|
||||||
|
email: email,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<template>
|
||||||
|
<a-select :options="emails">
|
||||||
|
<template #option="{ value: val }">
|
||||||
|
<div class="flex flex-row w-full">
|
||||||
|
<span class="flex-1">{{ val }}</span>
|
||||||
|
<fs-icon class="ml-5" icon="ion:close" @click="deleteItem(val)"></fs-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #dropdownRender="{ menuNode: menu }">
|
||||||
|
<v-nodes :vnodes="menu" />
|
||||||
|
<a-divider style="margin: 4px 0" />
|
||||||
|
<div class="w-full flex flex-row p-5">
|
||||||
|
<a-input ref="inputRef" v-model:value="newEmail" class="flex-1" placeholder="添加新邮箱" @keydown.enter="addItem" />
|
||||||
|
<a-button class="ml-5" type="primary" @click="addItem">
|
||||||
|
<template #icon>
|
||||||
|
<plus-outlined />
|
||||||
|
</template>
|
||||||
|
添加邮箱
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { defineComponent, onMounted, ref } from "vue";
|
||||||
|
import * as api from "./api";
|
||||||
|
import { Modal, notification } from "ant-design-vue";
|
||||||
|
|
||||||
|
const props = defineProps<{}>();
|
||||||
|
const VNodes = defineComponent({
|
||||||
|
props: {
|
||||||
|
vnodes: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
render() {
|
||||||
|
return this.vnodes;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const newEmail = ref("");
|
||||||
|
const emails = ref([]);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
const list = await api.EmailList();
|
||||||
|
emails.value = list.map((item: string) => {
|
||||||
|
return {
|
||||||
|
value: item,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
async function addItem() {
|
||||||
|
const email = newEmail.value;
|
||||||
|
//验证邮箱格式
|
||||||
|
if (!/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/.test(newEmail.value)) {
|
||||||
|
notification.error({
|
||||||
|
message: "请填写正确的邮箱地址",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
debugger;
|
||||||
|
if (emails.value.find(item => item.value === email)) {
|
||||||
|
notification.warning({
|
||||||
|
message: "此邮箱已存在",
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await api.EmailAdd(email);
|
||||||
|
emails.value.unshift({
|
||||||
|
value: email,
|
||||||
|
label: email,
|
||||||
|
});
|
||||||
|
newEmail.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteItem(value: string) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "删除邮箱",
|
||||||
|
content: "确定要删除此邮箱吗?",
|
||||||
|
onOk: async () => {
|
||||||
|
await api.EmailDelete(value);
|
||||||
|
emails.value = emails.value.filter(item => item.value !== value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -13,13 +13,16 @@ import ExpiresTimeText from "./expires-time-text.vue";
|
|||||||
import FileInput from "./file-input.vue";
|
import FileInput from "./file-input.vue";
|
||||||
import PemInput from "./pem-input.vue";
|
import PemInput from "./pem-input.vue";
|
||||||
import { defineAsyncComponent } from "vue";
|
import { defineAsyncComponent } from "vue";
|
||||||
|
import NotificationSelector from "../views/certd/notification/notification-selector/index.vue";
|
||||||
|
import EmailSelector from "./email-selector/index.vue";
|
||||||
export default {
|
export default {
|
||||||
install(app: any) {
|
install(app: any) {
|
||||||
app.component(
|
app.component(
|
||||||
"CodeEditor",
|
"CodeEditor",
|
||||||
defineAsyncComponent(() => import("./code-editor/index.vue"))
|
defineAsyncComponent(() => import("./code-editor/index.vue"))
|
||||||
);
|
);
|
||||||
|
app.component("EmailSelector", EmailSelector);
|
||||||
|
app.component("NotificationSelector", NotificationSelector);
|
||||||
app.component("PiContainer", PiContainer);
|
app.component("PiContainer", PiContainer);
|
||||||
app.component("TextEditable", TextEditable);
|
app.component("TextEditable", TextEditable);
|
||||||
app.component("FileInput", FileInput);
|
app.component("FileInput", FileInput);
|
||||||
|
|||||||
@@ -19,6 +19,10 @@
|
|||||||
<a-divider style="margin: 4px 0" />
|
<a-divider style="margin: 4px 0" />
|
||||||
</template>
|
</template>
|
||||||
<v-nodes :vnodes="menu" />
|
<v-nodes :vnodes="menu" />
|
||||||
|
|
||||||
|
<div v-if="pager === true" class="pager text-center p-5">
|
||||||
|
<a-pagination v-model:current="pagerRef.current" simple :total="pagerRef.total" :page-size="pagerRef.limit" />
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</a-select>
|
</a-select>
|
||||||
<div class="ml-5">
|
<div class="ml-5">
|
||||||
@@ -32,7 +36,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
||||||
import { defineComponent, inject, ref, useAttrs, watch } from "vue";
|
import { defineComponent, inject, ref, useAttrs, watch, Ref } from "vue";
|
||||||
import { PluginDefine } from "@certd/pipeline";
|
import { PluginDefine } from "@certd/pipeline";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@@ -54,7 +58,8 @@ const VNodes = defineComponent({
|
|||||||
const props = defineProps<
|
const props = defineProps<
|
||||||
{
|
{
|
||||||
watches: string[];
|
watches: string[];
|
||||||
search: boolean;
|
search?: boolean;
|
||||||
|
pager?: boolean;
|
||||||
} & ComponentPropsType
|
} & ComponentPropsType
|
||||||
>();
|
>();
|
||||||
|
|
||||||
@@ -73,6 +78,9 @@ const optionsRef = ref([]);
|
|||||||
const message = ref("");
|
const message = ref("");
|
||||||
const hasError = ref(false);
|
const hasError = ref(false);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
const pagerRef: Ref = ref({
|
||||||
|
current: 1,
|
||||||
|
});
|
||||||
const getOptions = async () => {
|
const getOptions = async () => {
|
||||||
if (loading.value) {
|
if (loading.value) {
|
||||||
return;
|
return;
|
||||||
@@ -107,6 +115,7 @@ const getOptions = async () => {
|
|||||||
loading.value = true;
|
loading.value = true;
|
||||||
optionsRef.value = [];
|
optionsRef.value = [];
|
||||||
|
|
||||||
|
const offset = (pagerRef.value.current - 1) * (pagerRef.value.limit ?? 100);
|
||||||
try {
|
try {
|
||||||
const res = await doRequest(
|
const res = await doRequest(
|
||||||
{
|
{
|
||||||
@@ -116,6 +125,8 @@ const getOptions = async () => {
|
|||||||
input,
|
input,
|
||||||
data: {
|
data: {
|
||||||
searchKey: props.search ? searchKeyRef.value : "",
|
searchKey: props.search ? searchKeyRef.value : "",
|
||||||
|
offset: offset,
|
||||||
|
limit: pagerRef.value.limit,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -126,10 +137,26 @@ const getOptions = async () => {
|
|||||||
showErrorNotify: false,
|
showErrorNotify: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if (res && res.length > 0) {
|
const list = res?.list || res || [];
|
||||||
|
if (list.length > 0) {
|
||||||
message.value = "获取数据成功,请从下拉框中选择";
|
message.value = "获取数据成功,请从下拉框中选择";
|
||||||
}
|
}
|
||||||
optionsRef.value = res;
|
optionsRef.value = list;
|
||||||
|
pagerRef.value.total = list.length;
|
||||||
|
if (props.pager) {
|
||||||
|
if (res.offset != null) {
|
||||||
|
pagerRef.value.offset = res.offset ?? 0;
|
||||||
|
}
|
||||||
|
if (res.limit != null) {
|
||||||
|
pagerRef.value.limit = res.limit ?? 100;
|
||||||
|
}
|
||||||
|
if (res.total != null) {
|
||||||
|
pagerRef.value.total = res.total ?? list.length;
|
||||||
|
}
|
||||||
|
const { offset, limit } = pagerRef.value;
|
||||||
|
pagerRef.value.current = offset % limit === 0 ? offset / limit + 1 : offset / limit;
|
||||||
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
} finally {
|
} finally {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@@ -151,27 +178,37 @@ async function refreshOptions() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function doSearch() {
|
async function doSearch() {
|
||||||
|
pagerRef.value.current = 1;
|
||||||
await refreshOptions();
|
await refreshOptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => {
|
() => {
|
||||||
const values = [];
|
|
||||||
|
|
||||||
const pluginType = getPluginType();
|
const pluginType = getPluginType();
|
||||||
const { form } = getScope();
|
const { form, key } = getScope();
|
||||||
const input = pluginType === "plugin" ? form.input : form;
|
const input = pluginType === "plugin" ? form.input : form;
|
||||||
|
const watches = {};
|
||||||
for (const item of props.watches) {
|
for (const key of props.watches) {
|
||||||
values.push(input[item]);
|
watches[key] = input[key];
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
form: input,
|
form: watches,
|
||||||
watched: values,
|
key,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
async () => {
|
async (value, oldValue) => {
|
||||||
await getOptions();
|
const { form } = value;
|
||||||
|
const oldForm = oldValue.form;
|
||||||
|
let changed = oldForm == null || optionsRef.value.length == 0;
|
||||||
|
for (const key of props.watches) {
|
||||||
|
if (form[key] != oldForm[key]) {
|
||||||
|
changed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
await getOptions();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export async function doActive(form: any) {
|
|||||||
return await request({
|
return await request({
|
||||||
url: "/sys/plus/active",
|
url: "/sys/plus/active",
|
||||||
method: "post",
|
method: "post",
|
||||||
data: form
|
data: form,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,6 +12,6 @@ export async function getVipTrial() {
|
|||||||
return await request({
|
return await request({
|
||||||
url: "/sys/plus/getVipTrial",
|
url: "/sys/plus/getVipTrial",
|
||||||
method: "post",
|
method: "post",
|
||||||
data: {}
|
data: {},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,11 @@ function install(app: App, options: any = {}) {
|
|||||||
columnSizeSaver.clear();
|
columnSizeSaver.clear();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
buttons: {
|
||||||
|
export: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
|
|||||||
@@ -116,6 +116,6 @@ export async function getProductInfo(): Promise<any> {
|
|||||||
return await request({
|
return await request({
|
||||||
url: "/basic/settings/productInfo",
|
url: "/basic/settings/productInfo",
|
||||||
method: "get",
|
method: "get",
|
||||||
silent: true,
|
showErrorNotify: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,46 +44,46 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
props: {
|
props: {
|
||||||
multiple: true,
|
multiple: true,
|
||||||
crossPage: true,
|
crossPage: true,
|
||||||
selectedRowKeys
|
selectedRowKeys,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
pageRequest,
|
pageRequest,
|
||||||
addRequest,
|
addRequest,
|
||||||
editRequest,
|
editRequest,
|
||||||
delRequest
|
delRequest,
|
||||||
},
|
},
|
||||||
actionbar: {
|
actionbar: {
|
||||||
buttons: {
|
buttons: {
|
||||||
add: {
|
add: {
|
||||||
show: false
|
show: false,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
formItem: {
|
formItem: {
|
||||||
labelCol: {
|
labelCol: {
|
||||||
style: {
|
style: {
|
||||||
// width: "100px"
|
// width: "100px"
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
wrapperCol: {
|
wrapperCol: {
|
||||||
style: {
|
style: {
|
||||||
width: "50%"
|
width: "50%",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
minWidth: 200,
|
minWidth: 200,
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
buttons: {
|
buttons: {
|
||||||
edit: {
|
edit: {
|
||||||
show: false
|
show: false,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
id: {
|
id: {
|
||||||
@@ -91,11 +91,11 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
key: "id",
|
key: "id",
|
||||||
type: "number",
|
type: "number",
|
||||||
column: {
|
column: {
|
||||||
width: 100
|
width: 100,
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
show: false
|
show: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
userId: {
|
userId: {
|
||||||
title: "用户Id",
|
title: "用户Id",
|
||||||
@@ -103,69 +103,69 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
search: {
|
search: {
|
||||||
show: computed(() => {
|
show: computed(() => {
|
||||||
return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline;
|
return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline;
|
||||||
})
|
}),
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
show: false
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
show: computed(() => {
|
show: computed(() => {
|
||||||
return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline;
|
return userStore.isAdmin && settingStore.sysPublic.managerOtherUserPipeline;
|
||||||
}),
|
}),
|
||||||
width: 100
|
width: 100,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
pipelineId: {
|
pipelineId: {
|
||||||
title: "流水线Id",
|
title: "流水线Id",
|
||||||
type: "number",
|
type: "number",
|
||||||
search: {
|
search: {
|
||||||
show: true
|
show: true,
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
show: false
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 100
|
width: 100,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
pipelineTitle: {
|
pipelineTitle: {
|
||||||
title: "流水线名称",
|
title: "流水线名称",
|
||||||
type: "text",
|
type: "text",
|
||||||
search: {
|
search: {
|
||||||
show: true
|
show: true,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 300,
|
width: 300,
|
||||||
|
tooltip: true,
|
||||||
|
ellipsis: true,
|
||||||
cellRender: ({ row, value }) => {
|
cellRender: ({ row, value }) => {
|
||||||
return (
|
return <router-link to={{ path: "/certd/pipeline/detail", query: { id: row.pipelineId, editMode: false, historyId: row.id } }}>{value}</router-link>;
|
||||||
<router-link to={{ path: "/certd/pipeline/detail", query: { id: row.pipelineId, editMode: false, historyId: row.id } }}>{value}</router-link>
|
},
|
||||||
);
|
},
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
createTime: {
|
createTime: {
|
||||||
title: "创建时间",
|
title: "创建时间",
|
||||||
type: "datetime",
|
type: "datetime",
|
||||||
form: {
|
form: {
|
||||||
show: false
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
sorter: true,
|
sorter: true,
|
||||||
width: 160,
|
width: 160,
|
||||||
align: "center"
|
align: "center",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
updateTime: {
|
updateTime: {
|
||||||
title: "更新时间",
|
title: "更新时间",
|
||||||
type: "datetime",
|
type: "datetime",
|
||||||
form: {
|
form: {
|
||||||
show: false
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
show: true
|
show: true,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,4 +55,25 @@ export const siteInfoApi = {
|
|||||||
method: "post",
|
method: "post",
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async DisabledChange(id: number, disabled: boolean) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/disabledChange",
|
||||||
|
method: "post",
|
||||||
|
data: {
|
||||||
|
id,
|
||||||
|
disabled,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async IpCheckChange(id: number, ipCheck: boolean) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/ipCheckChange",
|
||||||
|
method: "post",
|
||||||
|
data: {
|
||||||
|
id,
|
||||||
|
ipCheck,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
import { AddReq, ColumnCompositionProps, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||||
import { siteInfoApi } from "./api";
|
import { siteInfoApi } from "./api";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { notification } from "ant-design-vue";
|
import { Modal, notification } from "ant-design-vue";
|
||||||
import { useSettingStore } from "/@/store/settings";
|
import { useSettingStore } from "/@/store/settings";
|
||||||
import { mySuiteApi } from "/@/views/certd/suite/mine/api";
|
import { mySuiteApi } from "/@/views/certd/suite/mine/api";
|
||||||
import { mitter } from "/@/utils/util.mitt";
|
import { mitter } from "/@/utils/util.mitt";
|
||||||
|
import { useSiteIpMonitor } from "./ip/use";
|
||||||
|
|
||||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
@@ -41,6 +42,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
{ label: "异常", value: "error", color: "red" },
|
{ label: "异常", value: "error", color: "red" },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const { openSiteIpMonitorDialog } = useSiteIpMonitor();
|
||||||
return {
|
return {
|
||||||
crudOptions: {
|
crudOptions: {
|
||||||
request: {
|
request: {
|
||||||
@@ -116,8 +119,27 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
ipCheck: {
|
||||||
|
order: 10,
|
||||||
|
type: "link",
|
||||||
|
text: null,
|
||||||
|
show: compute(({ row }) => {
|
||||||
|
return row.ipCheck === true;
|
||||||
|
}),
|
||||||
|
tooltip: {
|
||||||
|
title: "IP管理",
|
||||||
|
},
|
||||||
|
icon: "entypo:address",
|
||||||
|
click: async ({ row }) => {
|
||||||
|
openSiteIpMonitorDialog({ siteId: row.id });
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tabs: {
|
||||||
|
name: "disabled",
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
columns: {
|
columns: {
|
||||||
id: {
|
id: {
|
||||||
title: "ID",
|
title: "ID",
|
||||||
@@ -192,6 +214,34 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
certInfo: {
|
||||||
|
title: "证书信息",
|
||||||
|
type: "text",
|
||||||
|
form: { show: false },
|
||||||
|
column: {
|
||||||
|
width: 200,
|
||||||
|
sorter: false,
|
||||||
|
show: true,
|
||||||
|
conditionalRender: false,
|
||||||
|
cellRender({ value, row }) {
|
||||||
|
const slots = {
|
||||||
|
content() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>证书颁发机构:{row.certProvider}</div>
|
||||||
|
<div>证书域名:{row.certDomains}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<a-popover placement="left" v-slots={slots} overlayStyle={{ maxWidth: "30%" }}>
|
||||||
|
{row.certDomains}
|
||||||
|
</a-popover>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
certDomains: {
|
certDomains: {
|
||||||
title: "证书域名",
|
title: "证书域名",
|
||||||
search: {
|
search: {
|
||||||
@@ -204,7 +254,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
column: {
|
column: {
|
||||||
width: 200,
|
width: 200,
|
||||||
sorter: true,
|
sorter: true,
|
||||||
show: true,
|
show: false,
|
||||||
cellRender({ value }) {
|
cellRender({ value }) {
|
||||||
return (
|
return (
|
||||||
<a-tooltip title={value} placement="left">
|
<a-tooltip title={value} placement="left">
|
||||||
@@ -226,6 +276,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
column: {
|
column: {
|
||||||
width: 200,
|
width: 200,
|
||||||
sorter: true,
|
sorter: true,
|
||||||
|
show: false,
|
||||||
cellRender({ value }) {
|
cellRender({ value }) {
|
||||||
return <a-tooltip title={value}>{value}</a-tooltip>;
|
return <a-tooltip title={value}>{value}</a-tooltip>;
|
||||||
},
|
},
|
||||||
@@ -305,6 +356,72 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
form: {
|
form: {
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
column: {
|
||||||
|
width: 100,
|
||||||
|
sorter: true,
|
||||||
|
align: "center",
|
||||||
|
component: {
|
||||||
|
name: "fs-dict-switch",
|
||||||
|
vModel: "checked",
|
||||||
|
on: {
|
||||||
|
async change({ row, $event }) {
|
||||||
|
await api.DisabledChange(row.id, $event);
|
||||||
|
await crudExpose.doRefresh();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ipCheck: {
|
||||||
|
title: "开启IP检查",
|
||||||
|
type: "dict-switch",
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: "启用", value: true, color: "green" },
|
||||||
|
{ label: "禁用", value: false, color: "gray" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
value: false,
|
||||||
|
rules: [{ required: true, message: "请选择" }],
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
align: "center",
|
||||||
|
width: 100,
|
||||||
|
conditionalRender: false,
|
||||||
|
component: {
|
||||||
|
name: "fs-dict-switch",
|
||||||
|
vModel: "checked",
|
||||||
|
on: {
|
||||||
|
change({ row, $event }) {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "提示",
|
||||||
|
content: `确定${$event ? "开启" : "关闭"}IP检查?`,
|
||||||
|
onOk: async () => {
|
||||||
|
await api.IpCheckChange(row.id, $event);
|
||||||
|
await crudExpose.doRefresh();
|
||||||
|
if ($event) {
|
||||||
|
openSiteIpMonitorDialog({ siteId: row.id });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onCancel: async () => {
|
||||||
|
await crudExpose.doRefresh();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as ColumnCompositionProps,
|
||||||
|
ipCount: {
|
||||||
|
title: "IP数量",
|
||||||
|
search: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
type: "text",
|
||||||
|
form: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 100,
|
width: 100,
|
||||||
sorter: true,
|
sorter: true,
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
import { request } from "/src/api/service";
|
||||||
|
|
||||||
|
const apiPrefix = "/monitor/site/ip";
|
||||||
|
|
||||||
|
export const siteIpApi = {
|
||||||
|
async GetList(query: any) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/page",
|
||||||
|
method: "post",
|
||||||
|
data: query,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async AddObj(obj: any) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/add",
|
||||||
|
method: "post",
|
||||||
|
data: obj,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async UpdateObj(obj: any) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/update",
|
||||||
|
method: "post",
|
||||||
|
data: obj,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async DelObj(id: number) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/delete",
|
||||||
|
method: "post",
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async GetObj(id: number) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/info",
|
||||||
|
method: "post",
|
||||||
|
params: { id },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async DoCheck(id: number) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/check",
|
||||||
|
method: "post",
|
||||||
|
data: { id },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
async CheckAll(siteId: number) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/checkAll",
|
||||||
|
method: "post",
|
||||||
|
data: {
|
||||||
|
siteId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
async DoSync(siteId: number) {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/sync",
|
||||||
|
method: "post",
|
||||||
|
data: {
|
||||||
|
siteId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||||
|
import { siteIpApi } from "./api";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { Modal, notification } from "ant-design-vue";
|
||||||
|
import { useSettingStore } from "/@/store/settings";
|
||||||
|
|
||||||
|
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||||
|
const { t } = useI18n();
|
||||||
|
const api = siteIpApi;
|
||||||
|
|
||||||
|
const { crudBinding } = crudExpose;
|
||||||
|
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||||
|
if (!query.query) {
|
||||||
|
query.query = {};
|
||||||
|
}
|
||||||
|
query.query.siteId = context.props.siteId;
|
||||||
|
return await api.GetList(query);
|
||||||
|
};
|
||||||
|
const editRequest = async (req: EditReq) => {
|
||||||
|
const { form, row } = req;
|
||||||
|
form.id = row.id;
|
||||||
|
const res = await api.UpdateObj(form);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
const delRequest = async (req: DelReq) => {
|
||||||
|
const { row } = req;
|
||||||
|
return await api.DelObj(row.id);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addRequest = async (req: AddReq) => {
|
||||||
|
const { form } = req;
|
||||||
|
form.siteId = context.props.siteId;
|
||||||
|
const res = await api.AddObj(form);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
const settingsStore = useSettingStore();
|
||||||
|
|
||||||
|
const checkStatusDict = dict({
|
||||||
|
data: [
|
||||||
|
{ label: "成功", value: "ok", color: "green" },
|
||||||
|
{ label: "检查中", value: "checking", color: "blue" },
|
||||||
|
{ label: "异常", value: "error", color: "red" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
crudOptions: {
|
||||||
|
request: {
|
||||||
|
pageRequest,
|
||||||
|
addRequest,
|
||||||
|
editRequest,
|
||||||
|
delRequest,
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
labelCol: {
|
||||||
|
//固定label宽度
|
||||||
|
span: null,
|
||||||
|
style: {
|
||||||
|
width: "100px",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
col: {
|
||||||
|
span: 22,
|
||||||
|
},
|
||||||
|
wrapper: {
|
||||||
|
width: 600,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actionbar: {
|
||||||
|
buttons: {
|
||||||
|
add: {
|
||||||
|
async click() {
|
||||||
|
await crudExpose.openAdd({});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
load: {
|
||||||
|
text: "同步IP",
|
||||||
|
type: "primary",
|
||||||
|
async click() {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "同步IP",
|
||||||
|
content: "确定要同步IP吗?",
|
||||||
|
onOk: async () => {
|
||||||
|
await api.DoSync(context.props.siteId);
|
||||||
|
await crudExpose.doRefresh();
|
||||||
|
notification.success({
|
||||||
|
message: "同步完成",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
checkAll: {
|
||||||
|
text: "检查全部",
|
||||||
|
type: "primary",
|
||||||
|
click: () => {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "确认",
|
||||||
|
content: "确认触发检查全部IP站点的证书吗?",
|
||||||
|
onOk: async () => {
|
||||||
|
await siteIpApi.CheckAll(context.props.siteId);
|
||||||
|
notification.success({
|
||||||
|
message: "检查任务已提交",
|
||||||
|
description: "请稍后刷新页面查看结果",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rowHandle: {
|
||||||
|
fixed: "right",
|
||||||
|
width: 240,
|
||||||
|
buttons: {
|
||||||
|
check: {
|
||||||
|
order: 0,
|
||||||
|
type: "link",
|
||||||
|
text: null,
|
||||||
|
tooltip: {
|
||||||
|
title: "立即检查",
|
||||||
|
},
|
||||||
|
icon: "ion:play-sharp",
|
||||||
|
click: async ({ row }) => {
|
||||||
|
await api.DoCheck(row.id);
|
||||||
|
await crudExpose.doRefresh();
|
||||||
|
notification.success({
|
||||||
|
message: "检查任务已提交",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
id: {
|
||||||
|
title: "ID",
|
||||||
|
key: "id",
|
||||||
|
type: "number",
|
||||||
|
search: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 80,
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ipAddress: {
|
||||||
|
title: "IP",
|
||||||
|
search: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
type: "text",
|
||||||
|
helper: "也支持填写CNAME域名",
|
||||||
|
form: {
|
||||||
|
rules: [{ required: true, message: "请输入IP" }],
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 160,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
certDomains: {
|
||||||
|
title: "证书域名",
|
||||||
|
search: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
type: "text",
|
||||||
|
form: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 200,
|
||||||
|
sorter: true,
|
||||||
|
show: false,
|
||||||
|
cellRender({ value }) {
|
||||||
|
return (
|
||||||
|
<a-tooltip title={value} placement="left">
|
||||||
|
{value}
|
||||||
|
</a-tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
certProvider: {
|
||||||
|
title: "颁发机构",
|
||||||
|
search: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
type: "text",
|
||||||
|
form: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 200,
|
||||||
|
show: false,
|
||||||
|
sorter: true,
|
||||||
|
cellRender({ value }) {
|
||||||
|
return <a-tooltip title={value}>{value}</a-tooltip>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
certStatus: {
|
||||||
|
title: "证书状态",
|
||||||
|
search: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
type: "dict-select",
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: "正常", value: "ok", color: "green" },
|
||||||
|
{ label: "过期", value: "expired", color: "red" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 100,
|
||||||
|
sorter: true,
|
||||||
|
show: true,
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
certExpiresTime: {
|
||||||
|
title: "证书到期时间",
|
||||||
|
search: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
type: "date",
|
||||||
|
form: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
sorter: true,
|
||||||
|
cellRender({ value }) {
|
||||||
|
if (!value) {
|
||||||
|
return "-";
|
||||||
|
}
|
||||||
|
const expireDate = dayjs(value).format("YYYY-MM-DD");
|
||||||
|
const leftDays = dayjs(value).diff(dayjs(), "day");
|
||||||
|
const color = leftDays < 20 ? "red" : "#389e0d";
|
||||||
|
const percent = (leftDays / 90) * 100;
|
||||||
|
return <a-progress title={expireDate + "过期"} percent={percent} strokeColor={color} format={(percent: number) => `${leftDays}天`} />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
checkStatus: {
|
||||||
|
title: "检查状态",
|
||||||
|
search: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
type: "dict-select",
|
||||||
|
dict: checkStatusDict,
|
||||||
|
form: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 100,
|
||||||
|
align: "center",
|
||||||
|
sorter: true,
|
||||||
|
cellRender({ value, row, key }) {
|
||||||
|
return (
|
||||||
|
<a-tooltip title={row.error}>
|
||||||
|
<fs-values-format v-model={value} dict={checkStatusDict}></fs-values-format>
|
||||||
|
</a-tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
lastCheckTime: {
|
||||||
|
title: "上次检查时间",
|
||||||
|
search: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
type: "datetime",
|
||||||
|
form: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
sorter: true,
|
||||||
|
width: 155,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
title: "来源",
|
||||||
|
search: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
type: "dict-switch",
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: "同步", value: "sync", color: "green" },
|
||||||
|
{ label: "手动", value: "manual", color: "blue" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 100,
|
||||||
|
sorter: true,
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
title: "禁用启用",
|
||||||
|
search: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
type: "dict-switch",
|
||||||
|
dict: dict({
|
||||||
|
data: [
|
||||||
|
{ label: "启用", value: false, color: "green" },
|
||||||
|
{ label: "禁用", value: true, color: "red" },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
form: {
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 100,
|
||||||
|
sorter: true,
|
||||||
|
align: "center",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
remark: {
|
||||||
|
title: "备注",
|
||||||
|
search: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
type: "text",
|
||||||
|
form: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
column: {
|
||||||
|
width: 200,
|
||||||
|
sorter: true,
|
||||||
|
tooltip: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div class="site-ip-dialog" style="height: 60vh">
|
||||||
|
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onActivated, onMounted, ref, Ref } from "vue";
|
||||||
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
|
import createCrudOptions from "./crud";
|
||||||
|
import { siteIpApi } from "./api";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "SiteIpCertMonitor",
|
||||||
|
});
|
||||||
|
const props = defineProps<{
|
||||||
|
siteId: number;
|
||||||
|
}>();
|
||||||
|
const { crudBinding, crudRef, crudExpose } = useFs({
|
||||||
|
createCrudOptions,
|
||||||
|
context: {
|
||||||
|
props,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const siteInfoRef: Ref<any> = ref({});
|
||||||
|
onMounted(async () => {
|
||||||
|
siteInfoRef.value = await siteIpApi.GetObj(props.siteId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 页面打开后获取列表数据
|
||||||
|
onMounted(() => {
|
||||||
|
crudExpose.doRefresh();
|
||||||
|
});
|
||||||
|
onActivated(() => {
|
||||||
|
crudExpose.doRefresh();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { useFormWrapper } from "@fast-crud/fast-crud";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
|
||||||
|
import SiteIpCertMonitor from "./index.vue";
|
||||||
|
|
||||||
|
export function useSiteIpMonitor() {
|
||||||
|
const { openDialog } = useFormWrapper();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
async function openSiteIpMonitorDialog(opts: { siteId: number }) {
|
||||||
|
await openDialog({
|
||||||
|
wrapper: {
|
||||||
|
title: "站点IP监控",
|
||||||
|
width: "80%",
|
||||||
|
is: "a-modal",
|
||||||
|
footer: false,
|
||||||
|
buttons: {
|
||||||
|
cancel: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
reset: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
ok: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
slots: {
|
||||||
|
"form-body-top": () => {
|
||||||
|
return <SiteIpCertMonitor siteId={opts.siteId} />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
openSiteIpMonitorDialog,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -15,13 +15,13 @@ export function notificationProvide(api: any) {
|
|||||||
|
|
||||||
export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
|
export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
|
||||||
const notificationTypeDictRef = dict({
|
const notificationTypeDictRef = dict({
|
||||||
url: "/pi/notification/getTypeDict"
|
url: "/pi/notification/getTypeDict",
|
||||||
});
|
});
|
||||||
const defaultPluginConfig = {
|
const defaultPluginConfig = {
|
||||||
component: {
|
component: {
|
||||||
name: "a-input",
|
name: "a-input",
|
||||||
vModel: "value"
|
vModel: "value",
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function buildDefineFields(define: any, form: any, mode: string) {
|
function buildDefineFields(define: any, form: any, mode: string) {
|
||||||
@@ -38,7 +38,7 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
|
|||||||
const key = "body." + mapKey;
|
const key = "body." + mapKey;
|
||||||
const field = {
|
const field = {
|
||||||
...value,
|
...value,
|
||||||
key
|
key,
|
||||||
};
|
};
|
||||||
const column = merge({ title: key }, defaultPluginConfig, field);
|
const column = merge({ title: key }, defaultPluginConfig, field);
|
||||||
//eval
|
//eval
|
||||||
@@ -69,29 +69,29 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
|
|||||||
key: "id",
|
key: "id",
|
||||||
type: "number",
|
type: "number",
|
||||||
column: {
|
column: {
|
||||||
width: 100
|
width: 100,
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
show: false
|
show: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
title: "通知类型",
|
title: "通知类型",
|
||||||
type: "dict-select",
|
type: "dict-select",
|
||||||
dict: notificationTypeDictRef,
|
dict: notificationTypeDictRef,
|
||||||
search: {
|
search: {
|
||||||
show: false
|
show: false,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 200,
|
width: 200,
|
||||||
component: {
|
component: {
|
||||||
color: "auto"
|
color: "auto",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
editForm: {
|
editForm: {
|
||||||
component: {
|
component: {
|
||||||
disabled: false
|
disabled: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
component: {
|
component: {
|
||||||
@@ -108,7 +108,7 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
|
|||||||
{item.needPlus && <fs-icon icon={"mingcute:vip-1-line"} className={"color-plus"}></fs-icon>}
|
{item.needPlus && <fs-icon icon={"mingcute:vip-1-line"} className={"color-plus"}></fs-icon>}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
rules: [{ required: true, message: "请选择通知类型" }],
|
rules: [{ required: true, message: "请选择通知类型" }],
|
||||||
valueChange: {
|
valueChange: {
|
||||||
@@ -133,7 +133,7 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
|
|||||||
form.name = define.title;
|
form.name = define.title;
|
||||||
}
|
}
|
||||||
buildDefineFields(define, form, mode);
|
buildDefineFields(define, form, mode);
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
helper: computed(() => {
|
helper: computed(() => {
|
||||||
const define = currentDefine.value;
|
const define = currentDefine.value;
|
||||||
@@ -141,22 +141,22 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
return define.desc;
|
return define.desc;
|
||||||
})
|
}),
|
||||||
}
|
},
|
||||||
} as ColumnCompositionProps,
|
} as ColumnCompositionProps,
|
||||||
name: {
|
name: {
|
||||||
title: "通知名称",
|
title: "通知名称",
|
||||||
search: {
|
search: {
|
||||||
show: true
|
show: true,
|
||||||
},
|
},
|
||||||
type: ["text"],
|
type: ["text"],
|
||||||
form: {
|
form: {
|
||||||
rules: [{ required: true, message: "请填写名称" }],
|
rules: [{ required: true, message: "请填写名称" }],
|
||||||
helper: "随便填,当多个相同类型的通知时,便于区分"
|
helper: "随便填,当多个相同类型的通知时,便于区分",
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 200
|
width: 200,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
isDefault: {
|
isDefault: {
|
||||||
title: "是否默认",
|
title: "是否默认",
|
||||||
@@ -164,13 +164,13 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
|
|||||||
dict: dict({
|
dict: dict({
|
||||||
data: [
|
data: [
|
||||||
{ label: "是", value: true, color: "success" },
|
{ label: "是", value: true, color: "success" },
|
||||||
{ label: "否", value: false, color: "default" }
|
{ label: "否", value: false, color: "default" },
|
||||||
]
|
],
|
||||||
}),
|
}),
|
||||||
form: {
|
form: {
|
||||||
value: false,
|
value: false,
|
||||||
rules: [{ required: true, message: "请选择是否默认" }],
|
rules: [{ required: true, message: "请选择是否默认" }],
|
||||||
order: 999
|
order: 999,
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
align: "center",
|
align: "center",
|
||||||
@@ -192,12 +192,12 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
|
|||||||
},
|
},
|
||||||
onCancel: async () => {
|
onCancel: async () => {
|
||||||
await crudExpose.doRefresh();
|
await crudExpose.doRefresh();
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
} as ColumnCompositionProps,
|
} as ColumnCompositionProps,
|
||||||
test: {
|
test: {
|
||||||
title: "测试",
|
title: "测试",
|
||||||
@@ -207,16 +207,16 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
|
|||||||
}),
|
}),
|
||||||
component: {
|
component: {
|
||||||
name: "api-test",
|
name: "api-test",
|
||||||
action: "TestRequest"
|
action: "TestRequest",
|
||||||
},
|
},
|
||||||
order: 990,
|
order: 990,
|
||||||
col: {
|
col: {
|
||||||
span: 24
|
span: 24,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
show: false
|
show: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
setting: {
|
setting: {
|
||||||
column: { show: false },
|
column: { show: false },
|
||||||
@@ -235,8 +235,8 @@ export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
|
|||||||
valueResolve({ form }) {
|
valueResolve({ form }) {
|
||||||
const setting = form.body;
|
const setting = form.body;
|
||||||
form.setting = JSON.stringify(setting);
|
form.setting = JSON.stringify(setting);
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
} as ColumnCompositionProps
|
} as ColumnCompositionProps,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,23 +32,23 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
|||||||
pageRequest,
|
pageRequest,
|
||||||
addRequest,
|
addRequest,
|
||||||
editRequest,
|
editRequest,
|
||||||
delRequest
|
delRequest,
|
||||||
},
|
},
|
||||||
form: {
|
form: {
|
||||||
labelCol: {
|
labelCol: {
|
||||||
//固定label宽度
|
//固定label宽度
|
||||||
span: null,
|
span: null,
|
||||||
style: {
|
style: {
|
||||||
width: "145px"
|
width: "145px",
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
width: 200
|
width: 200,
|
||||||
},
|
},
|
||||||
columns: {
|
columns: {
|
||||||
...commonColumnsDefine
|
...commonColumnsDefine,
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="notification-selector">
|
<div class="notification-selector">
|
||||||
<div class="flex-o w-100">
|
<div class="flex-o w-100">
|
||||||
<fs-dict-select class="flex-1" :value="modelValue" :dict="optionsDictRef" :disabled="disabled" :render-label="renderLabel" :slots="selectSlots" :allow-clear="true" @update:value="onChange" />
|
<fs-dict-select class="flex-1" :value="modelValue" :dict="optionsDictRef" :disabled="disabled" :render-label="renderLabel" :slots="selectSlots" :allow-clear="true" v-bind="select" @update:value="onChange" />
|
||||||
<fs-table-select
|
<fs-table-select
|
||||||
ref="tableSelectRef"
|
ref="tableSelectRef"
|
||||||
class="flex-0"
|
class="flex-0"
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
:dialog="{ width: 960 }"
|
:dialog="{ width: 960 }"
|
||||||
:destroy-on-close="false"
|
:destroy-on-close="false"
|
||||||
height="400px"
|
height="400px"
|
||||||
|
v-bind="tableSelect"
|
||||||
@update:model-value="onChange"
|
@update:model-value="onChange"
|
||||||
@dialog-closed="doRefresh"
|
@dialog-closed="doRefresh"
|
||||||
>
|
>
|
||||||
@@ -39,17 +40,20 @@ import { message } from "ant-design-vue";
|
|||||||
import { dict } from "@fast-crud/fast-crud";
|
import { dict } from "@fast-crud/fast-crud";
|
||||||
import createCrudOptions from "../crud";
|
import createCrudOptions from "../crud";
|
||||||
import { notificationProvide } from "/@/views/certd/notification/common";
|
import { notificationProvide } from "/@/views/certd/notification/common";
|
||||||
|
import { useUserStore } from "/@/store/user";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "NotificationSelector",
|
name: "NotificationSelector",
|
||||||
});
|
});
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
modelValue?: number | string;
|
modelValue?: number | string | number[] | string[];
|
||||||
type?: string;
|
type?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
size?: string;
|
size?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
select?: any;
|
||||||
|
tableSelect?: any;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const onChange = async (value: number) => {
|
const onChange = async (value: number) => {
|
||||||
@@ -118,9 +122,12 @@ function clear() {
|
|||||||
emitValue(null);
|
emitValue(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const userStore = useUserStore();
|
||||||
|
|
||||||
async function emitValue(value: any) {
|
async function emitValue(value: any) {
|
||||||
target.value = optionsDictRef.dataMap[value];
|
// target.value = optionsDictRef.dataMap[value];
|
||||||
if (value !== 0 && pipeline?.value && target && pipeline.value.userId !== target.value.userId) {
|
const userId = userStore.userInfo.id;
|
||||||
|
if (pipeline?.value && pipeline.value.userId !== userId) {
|
||||||
message.error("对不起,您不能修改他人流水线的通知");
|
message.error("对不起,您不能修改他人流水线的通知");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -134,6 +141,7 @@ watch(
|
|||||||
},
|
},
|
||||||
async value => {
|
async value => {
|
||||||
await optionsDictRef.loadDict();
|
await optionsDictRef.loadDict();
|
||||||
|
//@ts-ignore
|
||||||
target.value = optionsDictRef.dataMap[value];
|
target.value = optionsDictRef.dataMap[value];
|
||||||
emit("selectedChange", target.value);
|
emit("selectedChange", target.value);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ export async function Cancel(historyId: any) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function BatchUpdateGroup(pipelineIds: number[], groupId: number): Promise<CertInfo> {
|
export async function BatchUpdateGroup(pipelineIds: number[], groupId: number): Promise<void> {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/batchUpdateGroup",
|
url: apiPrefix + "/batchUpdateGroup",
|
||||||
method: "post",
|
method: "post",
|
||||||
@@ -84,13 +84,20 @@ export async function BatchUpdateGroup(pipelineIds: number[], groupId: number):
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function BatchDelete(pipelineIds: number[]): Promise<CertInfo> {
|
export async function BatchDelete(pipelineIds: number[]): Promise<void> {
|
||||||
return await request({
|
return await request({
|
||||||
url: apiPrefix + "/batchDelete",
|
url: apiPrefix + "/batchDelete",
|
||||||
method: "post",
|
method: "post",
|
||||||
data: { ids: pipelineIds },
|
data: { ids: pipelineIds },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
export async function BatchRerun(pipelineIds: number[]): Promise<void> {
|
||||||
|
return await request({
|
||||||
|
url: apiPrefix + "/batchRerun",
|
||||||
|
method: "post",
|
||||||
|
data: { ids: pipelineIds },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export async function GetFiles(pipelineId: number) {
|
export async function GetFiles(pipelineId: number) {
|
||||||
return await request({
|
return await request({
|
||||||
|
|||||||
@@ -345,14 +345,12 @@ export default function ({ crudExpose, context: { groupDictRef, selectedRowKeys
|
|||||||
},
|
},
|
||||||
column: {
|
column: {
|
||||||
width: 350,
|
width: 350,
|
||||||
|
// tooltip: true,
|
||||||
|
ellipsis: true,
|
||||||
sorter: true,
|
sorter: true,
|
||||||
component: {
|
showTitle: true,
|
||||||
on: {
|
cellRender: ({ row, value }) => {
|
||||||
// 注意:必须要on前缀
|
return <router-link to={{ path: "/certd/pipeline/detail", query: { id: row.id, editMode: false } }}>{value}</router-link>;
|
||||||
onClick({ row }) {
|
|
||||||
router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "false" } });
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
<span> 已选择 {{ selectedRowKeys.length }} 项 </span>
|
<span> 已选择 {{ selectedRowKeys.length }} 项 </span>
|
||||||
<fs-button icon="ion:trash-outline" class="color-green" type="link" text="批量删除" @click="batchDelete"></fs-button>
|
<fs-button icon="ion:trash-outline" class="color-green" type="link" text="批量删除" @click="batchDelete"></fs-button>
|
||||||
<change-group class="color-green" :selected-row-keys="selectedRowKeys" @change="groupChanged"></change-group>
|
<change-group class="color-green" :selected-row-keys="selectedRowKeys" @change="groupChanged"></change-group>
|
||||||
|
<fs-button icon="icon-park-outline:replay-music" class="need-plus" type="link" text="强制重新运行" @click="batchRerun"></fs-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template #actionbar-right> </template>
|
<template #actionbar-right> </template>
|
||||||
@@ -70,6 +71,19 @@ function batchDelete() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function batchRerun() {
|
||||||
|
Modal.confirm({
|
||||||
|
title: "确认强制重新运行吗",
|
||||||
|
content: "确定要强制重新运行选中流水线吗?(20条一批执行)",
|
||||||
|
async onOk() {
|
||||||
|
await api.BatchRerun(selectedRowKeys.value);
|
||||||
|
notification.success({ message: "任务已提交" });
|
||||||
|
await crudExpose.doRefresh();
|
||||||
|
selectedRowKeys.value = [];
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
.batch-actions {
|
.batch-actions {
|
||||||
|
|||||||
@@ -208,7 +208,6 @@ function useStepForm() {
|
|||||||
const stepOpen = (step: any, emit: any) => {
|
const stepOpen = (step: any, emit: any) => {
|
||||||
callback.value = emit;
|
callback.value = emit;
|
||||||
currentStep.value = merge({ input: {}, strategy: {} }, step);
|
currentStep.value = merge({ input: {}, strategy: {} }, step);
|
||||||
|
|
||||||
if (step.type) {
|
if (step.type) {
|
||||||
changeCurrentPlugin(currentStep.value);
|
changeCurrentPlugin(currentStep.value);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,12 @@
|
|||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
</a-tabs>
|
</a-tabs>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<fs-button v-if="settingsStore.sysPublic.aiChatEnabled !== false" key="aiChat" type="primary" icon="ion:color-wand-outline" @click="taskModal.onAiChat">AI分析</fs-button>
|
<a-tooltip title="AI分析异常">
|
||||||
|
<fs-button v-if="settingsStore.sysPublic.aiChatEnabled !== false" key="aiChat" type="primary" icon="ion:color-wand-outline" @click="taskModal.onAiChat">AI分析</fs-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip title="强制重新执行此步骤">
|
||||||
|
<fs-button key="rerun" type="primary" text="重新运行" icon="icon-park-outline:replay-music" @click="triggerRun(activeKey)"></fs-button>
|
||||||
|
</a-tooltip>
|
||||||
<fs-button key="cancel" icon="ion:close-circle-outline" @click="taskModal.onOk">关闭</fs-button>
|
<fs-button key="cancel" icon="ion:close-circle-outline" @click="taskModal.onOk">关闭</fs-button>
|
||||||
<fs-button key="submit" icon="ion:checkmark-circle-outline" type="primary" @click="taskModal.onOk">确定</fs-button>
|
<fs-button key="submit" icon="ion:checkmark-circle-outline" type="primary" @click="taskModal.onOk">确定</fs-button>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,6 +3,48 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.34.10](https://github.com/certd/certd/compare/v1.34.9...v1.34.10) (2025-06-03)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **flexcdn:** fix cert upload and skipSslVerify required ([c48da5d](https://github.com/certd/certd/commit/c48da5dea7f0f0cdeae643b106b4a678acc3b14b))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 阿里云CLB支持部署到扩展域名 ([0e8339c](https://github.com/certd/certd/commit/0e8339c70190890d449099e1d26e5ed06ff135fb))
|
||||||
|
* 支持部署到飞牛OS ([ddfd0fb](https://github.com/certd/certd/commit/ddfd0fb81d6638352920261065f1ab8e27bdd564))
|
||||||
|
* 支持日志写入文件 ([37edbf5](https://github.com/certd/certd/commit/37edbf5824d6aaae68ea1ef7259c6f739d418d2c))
|
||||||
|
|
||||||
|
## [1.34.9](https://github.com/certd/certd/compare/v1.34.8...v1.34.9) (2025-05-30)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复Farcdn证书有效期错误的问题 ([1fe4c36](https://github.com/certd/certd/commit/1fe4c367f7128de9ba5e3395ae06bc81e63a7d5a))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 不止证书自动化,插件解锁无限可能 ([a9b302e](https://github.com/certd/certd/commit/a9b302e38d3328d75df8b2da3d8b914851e55e9c))
|
||||||
|
* 邮箱支持保存和选择 ([f7b0b44](https://github.com/certd/certd/commit/f7b0b44ef6044bec36510a6f0b06d8dca5bfce49))
|
||||||
|
* 支持github 新版本检查并发布通知 ([356703c](https://github.com/certd/certd/commit/356703c83ea18c6efb8931402e181280d7b7e696))
|
||||||
|
|
||||||
|
## [1.34.8](https://github.com/certd/certd/compare/v1.34.7...v1.34.8) (2025-05-28)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 更新 1panel API 版本支持v1/v2设置 ([e6195ad](https://github.com/certd/certd/commit/e6195ade3ec54b138825b8d6738f86eb8afdd720))
|
||||||
|
* 同步更新namesilo接口,修复无法创建和删除dns记录的问题 ([36b02c2](https://github.com/certd/certd/commit/36b02c2cec145c13d4ef29d49aba5b6b4f697df2))
|
||||||
|
* 修复阿里云 esa 证书获取站点列表错误的问题 ([0c2ea5d](https://github.com/certd/certd/commit/0c2ea5da4c836f8a0df132a3f22d399bd9ee1de9))
|
||||||
|
* 修复部署到华为cdn,子账号ak查询不到域名的bug ([ebb292a](https://github.com/certd/certd/commit/ebb292a2f7a425c1bc810f59468beb3f1d5bc3f0))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 关闭腾讯云证书通知提醒 ([231a875](https://github.com/certd/certd/commit/231a875bb481420c39bf76ec9ff4e50954ab9fe4))
|
||||||
|
* 优化站点选择组件,切换选择时不刷新列表 ([3a14714](https://github.com/certd/certd/commit/3a147141b1a5d67c92a5ce88a5313eaa62859e03))
|
||||||
|
* 优化站点ip检查 ([a463711](https://github.com/certd/certd/commit/a463711b03a20120f2a298be15d71ca152d27f21))
|
||||||
|
* 站点监控支持监控IP ([9cc4c01](https://github.com/certd/certd/commit/9cc4c017ae646a18284e732769b82636feda01d3))
|
||||||
|
* 支持批量重新运行 ([8189982](https://github.com/certd/certd/commit/818998259ddc75e722196ac5c365038818539b9b))
|
||||||
|
* farcdn优化 ([a06ef07](https://github.com/certd/certd/commit/a06ef07178ed73c537e21c7d57e5e5144d2c056d))
|
||||||
|
|
||||||
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
## [1.34.7](https://github.com/certd/certd/compare/v1.34.6...v1.34.7) (2025-05-26)
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npm run heap
|
npm run heap
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
|
||||||
|
ALTER TABLE cd_site_info ADD COLUMN `ip_check` boolean;
|
||||||
|
ALTER TABLE cd_site_info ADD COLUMN `ip_count` bigint;
|
||||||
|
ALTER TABLE cd_site_info ADD COLUMN `ip_error_count` bigint;
|
||||||
|
ALTER TABLE cd_site_info MODIFY COLUMN `error` longtext NULL;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE `cd_site_ip`
|
||||||
|
(
|
||||||
|
`id` bigint PRIMARY KEY AUTO_INCREMENT NOT NULL,
|
||||||
|
`user_id` bigint,
|
||||||
|
`site_id` bigint,
|
||||||
|
`ip_address` varchar(100),
|
||||||
|
`cert_domains` varchar(10240),
|
||||||
|
`cert_provider` varchar(100),
|
||||||
|
`cert_status` varchar(100),
|
||||||
|
`cert_expires_time` bigint,
|
||||||
|
`last_check_time` bigint,
|
||||||
|
`check_status` varchar(100),
|
||||||
|
`error` longtext,
|
||||||
|
`remark` varchar(4096),
|
||||||
|
`from` varchar(100),
|
||||||
|
`disabled` boolean NOT NULL DEFAULT false,
|
||||||
|
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX `index_site_ip_user_id` ON `cd_site_ip` (`user_id`);
|
||||||
|
CREATE INDEX `index_site_ip_site_id` ON `cd_site_ip` (`site_id`);
|
||||||
28
packages/ui/certd-server/db/migration-pg/v10023__site_ip.sql
Normal file
28
packages/ui/certd-server/db/migration-pg/v10023__site_ip.sql
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
ALTER TABLE cd_site_info ADD COLUMN "ip_check" boolean;
|
||||||
|
ALTER TABLE cd_site_info ADD COLUMN "ip_count" bigint;
|
||||||
|
ALTER TABLE cd_site_info ADD COLUMN "ip_error_count" bigint;
|
||||||
|
alter table cd_site_info alter column error type text using error::text;
|
||||||
|
|
||||||
|
CREATE TABLE "cd_site_ip"
|
||||||
|
(
|
||||||
|
"id" bigint PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
||||||
|
"user_id" bigint,
|
||||||
|
"site_id" bigint,
|
||||||
|
"ip_address" varchar(100),
|
||||||
|
"cert_domains" varchar(10240),
|
||||||
|
"cert_provider" varchar(100),
|
||||||
|
"cert_status" varchar(100),
|
||||||
|
"cert_expires_time" bigint,
|
||||||
|
"last_check_time" bigint,
|
||||||
|
"check_status" varchar(100),
|
||||||
|
"error" text,
|
||||||
|
"remark" varchar(4096),
|
||||||
|
"from" varchar(100),
|
||||||
|
"disabled" boolean NOT NULL DEFAULT (false),
|
||||||
|
"create_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||||
|
"update_time" timestamp NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX "index_site_ip_user_id" ON "cd_site_ip" ("user_id");
|
||||||
|
CREATE INDEX "index_site_ip_site_id" ON "cd_site_ip" ("site_id");
|
||||||
28
packages/ui/certd-server/db/migration/v10023__site_ip.sql
Normal file
28
packages/ui/certd-server/db/migration/v10023__site_ip.sql
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
|
||||||
|
ALTER TABLE cd_site_info ADD COLUMN "ip_check" boolean;
|
||||||
|
ALTER TABLE cd_site_info ADD COLUMN "ip_count" integer;
|
||||||
|
ALTER TABLE cd_site_info ADD COLUMN "ip_error_count" integer;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE "cd_site_ip"
|
||||||
|
(
|
||||||
|
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||||
|
"user_id" integer,
|
||||||
|
"site_id" integer,
|
||||||
|
"ip_address" varchar(100),
|
||||||
|
"cert_domains" varchar(10240),
|
||||||
|
"cert_provider" varchar(100),
|
||||||
|
"cert_status" varchar(100),
|
||||||
|
"cert_expires_time" integer,
|
||||||
|
"last_check_time" integer,
|
||||||
|
"check_status" varchar(100),
|
||||||
|
"error" varchar(4096),
|
||||||
|
"remark" varchar(4096),
|
||||||
|
"from" varchar(100),
|
||||||
|
"disabled" boolean NOT NULL DEFAULT (false),
|
||||||
|
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
|
||||||
|
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX "index_site_ip_user_id" ON "cd_site_ip" ("user_id");
|
||||||
|
CREATE INDEX "index_site_ip_site_id" ON "cd_site_ip" ("site_id");
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/ui-server",
|
"name": "@certd/ui-server",
|
||||||
"version": "1.34.7",
|
"version": "1.34.10",
|
||||||
"description": "fast-server base midway",
|
"description": "fast-server base midway",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -41,19 +41,19 @@
|
|||||||
"@aws-sdk/client-acm": "^3.699.0",
|
"@aws-sdk/client-acm": "^3.699.0",
|
||||||
"@aws-sdk/client-cloudfront": "^3.699.0",
|
"@aws-sdk/client-cloudfront": "^3.699.0",
|
||||||
"@aws-sdk/client-s3": "^3.705.0",
|
"@aws-sdk/client-s3": "^3.705.0",
|
||||||
"@certd/acme-client": "^1.34.7",
|
"@certd/acme-client": "^1.34.10",
|
||||||
"@certd/basic": "^1.34.7",
|
"@certd/basic": "^1.34.10",
|
||||||
"@certd/commercial-core": "^1.34.7",
|
"@certd/commercial-core": "^1.34.10",
|
||||||
"@certd/jdcloud": "^1.34.7",
|
"@certd/jdcloud": "^1.34.10",
|
||||||
"@certd/lib-huawei": "^1.34.7",
|
"@certd/lib-huawei": "^1.34.10",
|
||||||
"@certd/lib-k8s": "^1.34.7",
|
"@certd/lib-k8s": "^1.34.10",
|
||||||
"@certd/lib-server": "^1.34.7",
|
"@certd/lib-server": "^1.34.10",
|
||||||
"@certd/midway-flyway-js": "^1.34.7",
|
"@certd/midway-flyway-js": "^1.34.10",
|
||||||
"@certd/pipeline": "^1.34.7",
|
"@certd/pipeline": "^1.34.10",
|
||||||
"@certd/plugin-cert": "^1.34.7",
|
"@certd/plugin-cert": "^1.34.10",
|
||||||
"@certd/plugin-lib": "^1.34.7",
|
"@certd/plugin-lib": "^1.34.10",
|
||||||
"@certd/plugin-plus": "^1.34.7",
|
"@certd/plugin-plus": "^1.34.10",
|
||||||
"@certd/plus-core": "^1.34.7",
|
"@certd/plus-core": "^1.34.10",
|
||||||
"@corsinvest/cv4pve-api-javascript": "^8.3.0",
|
"@corsinvest/cv4pve-api-javascript": "^8.3.0",
|
||||||
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
|
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.120",
|
||||||
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",
|
"@huaweicloud/huaweicloud-sdk-core": "^3.1.120",
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ import path from 'path';
|
|||||||
const env = process.env.NODE_ENV || 'development';
|
const env = process.env.NODE_ENV || 'development';
|
||||||
|
|
||||||
const development = {
|
const development = {
|
||||||
|
midwayLogger: {
|
||||||
|
default: {
|
||||||
|
dir: './logs',
|
||||||
|
},
|
||||||
|
// ...
|
||||||
|
},
|
||||||
keys: 'certd',
|
keys: 'certd',
|
||||||
koa: {
|
koa: {
|
||||||
port: 7001,
|
port: 7001,
|
||||||
|
|||||||
@@ -17,4 +17,25 @@ export class EmailController extends BaseController {
|
|||||||
await this.emailService.test(userId, receiver);
|
await this.emailService.test(userId, receiver);
|
||||||
return this.ok({});
|
return this.ok({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('/list', { summary: Constants.per.authOnly })
|
||||||
|
public async list() {
|
||||||
|
const userId = super.getUserId();
|
||||||
|
const res = await this.emailService.list(userId);
|
||||||
|
return this.ok(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/add', { summary: Constants.per.authOnly })
|
||||||
|
public async add(@Body('email') email) {
|
||||||
|
const userId = super.getUserId();
|
||||||
|
await this.emailService.add(userId, email);
|
||||||
|
return this.ok({});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/delete', { summary: Constants.per.authOnly })
|
||||||
|
public async delete(@Body('email') email) {
|
||||||
|
const userId = super.getUserId();
|
||||||
|
await this.emailService.delete(userId, email);
|
||||||
|
return this.ok({});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { AuthService } from "../../../modules/sys/authority/service/auth-service
|
|||||||
import { SiteInfoService } from "../../../modules/monitor/service/site-info-service.js";
|
import { SiteInfoService } from "../../../modules/monitor/service/site-info-service.js";
|
||||||
import { UserSiteMonitorSetting } from "../../../modules/mine/service/models.js";
|
import { UserSiteMonitorSetting } from "../../../modules/mine/service/models.js";
|
||||||
import { merge } from "lodash-es";
|
import { merge } from "lodash-es";
|
||||||
|
import {SiteIpService} from "../../../modules/monitor/service/site-ip-service.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*/
|
*/
|
||||||
@@ -14,6 +15,8 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
|
|||||||
service: SiteInfoService;
|
service: SiteInfoService;
|
||||||
@Inject()
|
@Inject()
|
||||||
authService: AuthService;
|
authService: AuthService;
|
||||||
|
@Inject()
|
||||||
|
siteIpService: SiteIpService;
|
||||||
|
|
||||||
getService(): SiteInfoService {
|
getService(): SiteInfoService {
|
||||||
return this.service;
|
return this.service;
|
||||||
@@ -59,7 +62,10 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
|
|||||||
async add(@Body(ALL) bean: any) {
|
async add(@Body(ALL) bean: any) {
|
||||||
bean.userId = this.getUserId();
|
bean.userId = this.getUserId();
|
||||||
const res = await this.service.add(bean);
|
const res = await this.service.add(bean);
|
||||||
this.service.check(res.id, true, 0);
|
const entity = await this.service.info(res.id);
|
||||||
|
if (entity.disabled) {
|
||||||
|
this.service.check(entity.id, true, 0);
|
||||||
|
}
|
||||||
return this.ok(res);
|
return this.ok(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +74,10 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
|
|||||||
await this.service.checkUserId(bean.id, this.getUserId());
|
await this.service.checkUserId(bean.id, this.getUserId());
|
||||||
delete bean.userId;
|
delete bean.userId;
|
||||||
await this.service.update(bean);
|
await this.service.update(bean);
|
||||||
this.service.check(bean.id, true, 0);
|
const entity = await this.service.info(bean.id);
|
||||||
|
if (entity.disabled) {
|
||||||
|
this.service.check(entity.id, true, 0);
|
||||||
|
}
|
||||||
return this.ok();
|
return this.ok();
|
||||||
}
|
}
|
||||||
@Post('/info', { summary: Constants.per.authOnly })
|
@Post('/info', { summary: Constants.per.authOnly })
|
||||||
@@ -96,8 +105,27 @@ export class SiteInfoController extends CrudController<SiteInfoService> {
|
|||||||
await this.service.checkAllByUsers(userId);
|
await this.service.checkAllByUsers(userId);
|
||||||
return this.ok();
|
return this.ok();
|
||||||
}
|
}
|
||||||
|
@Post('/ipCheckChange', { summary: Constants.per.authOnly })
|
||||||
|
async ipCheckChange(@Body(ALL) bean: any) {
|
||||||
|
const userId = this.getUserId();
|
||||||
|
await this.service.checkUserId(bean.id, userId)
|
||||||
|
await this.service.ipCheckChange({
|
||||||
|
id: bean.id,
|
||||||
|
ipCheck: bean.ipCheck
|
||||||
|
});
|
||||||
|
return this.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/disabledChange', { summary: Constants.per.authOnly })
|
||||||
|
async disabledChange(@Body(ALL) bean: any) {
|
||||||
|
const userId = this.getUserId();
|
||||||
|
await this.service.checkUserId(bean.id, userId)
|
||||||
|
await this.service.disabledChange({
|
||||||
|
id: bean.id,
|
||||||
|
disabled: bean.disabled
|
||||||
|
});
|
||||||
|
return this.ok();
|
||||||
|
}
|
||||||
|
|
||||||
@Post("/setting/get", { summary: Constants.per.authOnly })
|
@Post("/setting/get", { summary: Constants.per.authOnly })
|
||||||
async get() {
|
async get() {
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core";
|
||||||
|
import { Constants, CrudController } from "@certd/lib-server";
|
||||||
|
import { AuthService } from "../../../modules/sys/authority/service/auth-service.js";
|
||||||
|
import { SiteIpService } from "../../../modules/monitor/service/site-ip-service.js";
|
||||||
|
import { SiteInfoService } from "../../../modules/monitor/index.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
@Provide()
|
||||||
|
@Controller('/api/monitor/site/ip')
|
||||||
|
export class SiteInfoController extends CrudController<SiteIpService> {
|
||||||
|
@Inject()
|
||||||
|
service: SiteIpService;
|
||||||
|
@Inject()
|
||||||
|
authService: AuthService;
|
||||||
|
@Inject()
|
||||||
|
siteInfoService: SiteInfoService;
|
||||||
|
|
||||||
|
getService(): SiteIpService {
|
||||||
|
return this.service;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/page', { summary: Constants.per.authOnly })
|
||||||
|
async page(@Body(ALL) body: any) {
|
||||||
|
body.query = body.query ?? {};
|
||||||
|
body.query.userId = this.getUserId();
|
||||||
|
const res = await this.service.page({
|
||||||
|
query: body.query,
|
||||||
|
page: body.page,
|
||||||
|
sort: body.sort,
|
||||||
|
});
|
||||||
|
return this.ok(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/list', { summary: Constants.per.authOnly })
|
||||||
|
async list(@Body(ALL) body: any) {
|
||||||
|
body.query = body.query ?? {};
|
||||||
|
body.query.userId = this.getUserId();
|
||||||
|
return await super.list(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/add', { summary: Constants.per.authOnly })
|
||||||
|
async add(@Body(ALL) bean: any) {
|
||||||
|
bean.userId = this.getUserId();
|
||||||
|
bean.from = "manual"
|
||||||
|
const res = await this.service.add(bean);
|
||||||
|
const siteEntity = await this.siteInfoService.info(bean.siteId);
|
||||||
|
if(!siteEntity.disabled){
|
||||||
|
const {domain, httpsPort} = siteEntity;
|
||||||
|
this.service.check(res.id,domain, httpsPort);
|
||||||
|
}
|
||||||
|
return this.ok(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/update', { summary: Constants.per.authOnly })
|
||||||
|
async update(@Body(ALL) bean) {
|
||||||
|
await this.service.checkUserId(bean.id, this.getUserId());
|
||||||
|
delete bean.userId;
|
||||||
|
await this.service.update(bean);
|
||||||
|
const siteEntity = await this.siteInfoService.info(bean.siteId);
|
||||||
|
if(!siteEntity.disabled){
|
||||||
|
const {domain, httpsPort} = siteEntity;
|
||||||
|
this.service.check(siteEntity.id,domain, httpsPort);
|
||||||
|
}
|
||||||
|
return this.ok();
|
||||||
|
}
|
||||||
|
@Post('/info', { summary: Constants.per.authOnly })
|
||||||
|
async info(@Query('id') id: number) {
|
||||||
|
await this.service.checkUserId(id, this.getUserId());
|
||||||
|
return await super.info(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/delete', { summary: Constants.per.authOnly })
|
||||||
|
async delete(@Query('id') id: number) {
|
||||||
|
const entity = await this.service.info(id);
|
||||||
|
await this.service.checkUserId(id, this.getUserId());
|
||||||
|
|
||||||
|
const res = await super.delete(id);
|
||||||
|
await this.service.updateIpCount(entity.siteId)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/check', { summary: Constants.per.authOnly })
|
||||||
|
async check(@Body('id') id: number) {
|
||||||
|
await this.service.checkUserId(id, this.getUserId());
|
||||||
|
const entity = await this.service.info(id);
|
||||||
|
const siteEntity = await this.siteInfoService.info(entity.siteId);
|
||||||
|
const domain = siteEntity.domain;
|
||||||
|
const port = siteEntity.httpsPort;
|
||||||
|
this.service.check(id,domain,port);
|
||||||
|
return this.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/checkAll', { summary: Constants.per.authOnly })
|
||||||
|
async checkAll(@Body('siteId') siteId: number) {
|
||||||
|
const userId = this.getUserId();
|
||||||
|
await this.siteInfoService.checkUserId(siteId, userId);
|
||||||
|
const siteEntity = await this.siteInfoService.info(siteId);
|
||||||
|
await this.service.checkAll(siteEntity);
|
||||||
|
return this.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('/sync', { summary: Constants.per.authOnly })
|
||||||
|
async sync(@Body('siteId') siteId: number) {
|
||||||
|
const userId = this.getUserId();
|
||||||
|
const entity = await this.siteInfoService.info(siteId)
|
||||||
|
if(entity.userId != userId){
|
||||||
|
throw new Error('无权限')
|
||||||
|
}
|
||||||
|
await this.service.sync(entity);
|
||||||
|
return this.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -122,4 +122,10 @@ export class PipelineController extends CrudController<PipelineService> {
|
|||||||
await this.service.batchUpdateGroup(ids, groupId, this.getUserId());
|
await this.service.batchUpdateGroup(ids, groupId, this.getUserId());
|
||||||
return this.ok({});
|
return this.ok({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('/batchRerun', { summary: Constants.per.authOnly })
|
||||||
|
async batchRerun(@Body('ids') ids: number[]) {
|
||||||
|
await this.service.batchRerun(ids, this.getUserId());
|
||||||
|
return this.ok({});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import { SendMailOptions } from 'nodemailer';
|
|||||||
import { UserSettingsService } from '../../mine/service/user-settings-service.js';
|
import { UserSettingsService } from '../../mine/service/user-settings-service.js';
|
||||||
import { PlusService, SysSettingsService, SysSiteInfo } from '@certd/lib-server';
|
import { PlusService, SysSettingsService, SysSiteInfo } from '@certd/lib-server';
|
||||||
import { getEmailSettings } from '../../sys/settings/fix.js';
|
import { getEmailSettings } from '../../sys/settings/fix.js';
|
||||||
|
import { UserEmailSetting } from "../../mine/service/models.js";
|
||||||
|
|
||||||
export type EmailConfig = {
|
export type EmailConfig = {
|
||||||
host: string;
|
host: string;
|
||||||
@@ -108,4 +109,24 @@ export class EmailService implements IEmailService {
|
|||||||
content: '测试邮件,from certd',
|
content: '测试邮件,from certd',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async list(userId: any) {
|
||||||
|
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId,UserEmailSetting)
|
||||||
|
return userEmailSetting.list;
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(userId: any, email: string) {
|
||||||
|
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId,UserEmailSetting)
|
||||||
|
userEmailSetting.list = userEmailSetting.list.filter(item=>item !== email);
|
||||||
|
await this.settingsService.saveSetting(userId,userEmailSetting)
|
||||||
|
}
|
||||||
|
async add(userId: any, email: string) {
|
||||||
|
const userEmailSetting = await this.settingsService.getSetting<UserEmailSetting>(userId,UserEmailSetting)
|
||||||
|
//如果已存在
|
||||||
|
if(userEmailSetting.list.includes(email)){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
userEmailSetting.list.unshift(email)
|
||||||
|
await this.settingsService.saveSetting(userId,userEmailSetting)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,3 +26,10 @@ export class UserSiteMonitorSetting extends BaseSettings {
|
|||||||
|
|
||||||
notificationId?:number= 0;
|
notificationId?:number= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class UserEmailSetting extends BaseSettings {
|
||||||
|
static __title__ = "用户邮箱设置";
|
||||||
|
static __key__ = "user.email";
|
||||||
|
|
||||||
|
list:string[] = [];
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { InjectEntityModel } from "@midwayjs/typeorm";
|
|||||||
import { Repository } from "typeorm";
|
import { Repository } from "typeorm";
|
||||||
import { BaseService, BaseSettings } from "@certd/lib-server";
|
import { BaseService, BaseSettings } from "@certd/lib-server";
|
||||||
import { UserSettingsEntity } from "../entity/user-settings.js";
|
import { UserSettingsEntity } from "../entity/user-settings.js";
|
||||||
import { merge } from "lodash-es";
|
import { mergeUtils } from "@certd/basic";
|
||||||
|
const {merge} = mergeUtils
|
||||||
/**
|
/**
|
||||||
* 授权
|
* 授权
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -40,6 +40,17 @@ export class SiteInfoEntity {
|
|||||||
@Column({ name: 'cert_info_id', comment: '证书id' })
|
@Column({ name: 'cert_info_id', comment: '证书id' })
|
||||||
certInfoId: number;
|
certInfoId: number;
|
||||||
|
|
||||||
|
|
||||||
|
@Column({ name: 'ip_check', comment: '是否检查IP' })
|
||||||
|
ipCheck: boolean;
|
||||||
|
|
||||||
|
@Column({ name: 'ip_count', comment: 'ip数量' })
|
||||||
|
ipCount: number
|
||||||
|
|
||||||
|
@Column({ name: 'ip_error_count', comment: 'ip异常数量' })
|
||||||
|
ipErrorCount: number
|
||||||
|
|
||||||
|
|
||||||
@Column({ name: 'disabled', comment: '禁用启用' })
|
@Column({ name: 'disabled', comment: '禁用启用' })
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
@Entity('cd_site_ip')
|
||||||
|
export class SiteIpEntity {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
@Column({ name: 'user_id', comment: '用户id' })
|
||||||
|
userId: number;
|
||||||
|
@Column({ name: 'site_id', comment: '站点id' })
|
||||||
|
siteId: number;
|
||||||
|
@Column({ name: 'ip_address', comment: 'IP', length: 100 })
|
||||||
|
ipAddress: string;
|
||||||
|
|
||||||
|
@Column({ name: 'cert_domains', comment: '证书域名', length: 4096 })
|
||||||
|
certDomains: string;
|
||||||
|
@Column({ name: 'cert_status', comment: '证书状态', length: 100 })
|
||||||
|
certStatus: string;
|
||||||
|
@Column({ name: 'cert_provider', comment: '证书颁发机构', length: 100 })
|
||||||
|
certProvider: string;
|
||||||
|
@Column({ name: 'cert_expires_time', comment: '证书到期时间' })
|
||||||
|
certExpiresTime: number;
|
||||||
|
@Column({ name: 'last_check_time', comment: '上次检查时间' })
|
||||||
|
lastCheckTime: number;
|
||||||
|
@Column({ name: 'check_status', comment: '检查状态' })
|
||||||
|
checkStatus: string;
|
||||||
|
@Column({ name: 'error', comment: '错误信息' })
|
||||||
|
error: string;
|
||||||
|
@Column({ name: 'from', comment: '来源' })
|
||||||
|
from: string
|
||||||
|
@Column({ name: 'remark', comment: '备注' })
|
||||||
|
remark: string;
|
||||||
|
@Column({ name: "disabled", comment: "禁用启用" })
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
@Column({ name: 'create_time', comment: '创建时间', default: () => 'CURRENT_TIMESTAMP' })
|
||||||
|
createTime: Date;
|
||||||
|
@Column({ name: 'update_time', comment: '修改时间', default: () => 'CURRENT_TIMESTAMP' })
|
||||||
|
updateTime: Date;
|
||||||
|
}
|
||||||
@@ -12,6 +12,8 @@ import { isComm, isPlus } from '@certd/plus-core';
|
|||||||
import { UserSuiteService } from '@certd/commercial-core';
|
import { UserSuiteService } from '@certd/commercial-core';
|
||||||
import { UserSettingsService } from "../../mine/service/user-settings-service.js";
|
import { UserSettingsService } from "../../mine/service/user-settings-service.js";
|
||||||
import { UserSiteMonitorSetting } from "../../mine/service/models.js";
|
import { UserSiteMonitorSetting } from "../../mine/service/models.js";
|
||||||
|
import {SiteIpService} from "./site-ip-service.js";
|
||||||
|
import {SiteIpEntity} from "../entity/site-ip.js";
|
||||||
|
|
||||||
@Provide()
|
@Provide()
|
||||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||||
@@ -31,6 +33,8 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
@Inject()
|
@Inject()
|
||||||
userSettingsService: UserSettingsService;
|
userSettingsService: UserSettingsService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
siteIpService: SiteIpService;
|
||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
getRepository() {
|
getRepository() {
|
||||||
@@ -94,7 +98,7 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
await this.update({
|
await this.update({
|
||||||
id: site.id,
|
id: site.id,
|
||||||
checkStatus: 'checking',
|
checkStatus: 'checking',
|
||||||
lastCheckTime: dayjs,
|
lastCheckTime: dayjs().valueOf(),
|
||||||
});
|
});
|
||||||
const res = await siteTester.test({
|
const res = await siteTester.test({
|
||||||
host: site.domain,
|
host: site.domain,
|
||||||
@@ -128,6 +132,11 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await this.update(updateData);
|
await this.update(updateData);
|
||||||
|
|
||||||
|
|
||||||
|
//检查ip
|
||||||
|
await this.checkAllIp(site)
|
||||||
|
|
||||||
if (!notify) {
|
if (!notify) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -155,8 +164,48 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async checkAllIp(site:SiteInfoEntity){
|
||||||
|
if( !site.ipCheck){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const certExpiresTime = site.certExpiresTime;
|
||||||
|
const onFinished = async (list:SiteIpEntity[])=>{
|
||||||
|
let errorCount = 0
|
||||||
|
let errorMessage = ""
|
||||||
|
for (const item of list) {
|
||||||
|
if (!item) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
errorCount++
|
||||||
|
if(item.error){
|
||||||
|
errorMessage += `${item.ipAddress}:${item.error}; \n`
|
||||||
|
}else if(item.certExpiresTime!==certExpiresTime){
|
||||||
|
errorMessage += `${item.ipAddress}:与主站证书过期时间不一致; \n`
|
||||||
|
}else{
|
||||||
|
errorCount--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errorCount<=0){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await this.update({
|
||||||
|
id: site.id,
|
||||||
|
checkStatus: 'error',
|
||||||
|
error: errorMessage,
|
||||||
|
ipErrorCount: errorCount,
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
site = await this.info(site.id)
|
||||||
|
await this.sendCheckErrorNotify(site,true);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error('send notify error', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.siteIpService.checkAll(site,onFinished)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查,但不发邮件
|
* 检查
|
||||||
* @param id
|
* @param id
|
||||||
* @param notify
|
* @param notify
|
||||||
* @param retryTimes
|
* @param retryTimes
|
||||||
@@ -169,7 +218,7 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
return await this.doCheck(site, notify, retryTimes);
|
return await this.doCheck(site, notify, retryTimes);
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendCheckErrorNotify(site: SiteInfoEntity) {
|
async sendCheckErrorNotify(site: SiteInfoEntity,fromIpCheck=false) {
|
||||||
const url = await this.notificationService.getBindUrl('#/certd/monitor/site');
|
const url = await this.notificationService.getBindUrl('#/certd/monitor/site');
|
||||||
// 发邮件
|
// 发邮件
|
||||||
await this.notificationService.send(
|
await this.notificationService.send(
|
||||||
@@ -178,8 +227,9 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
body: {
|
body: {
|
||||||
url,
|
url,
|
||||||
title: `站点证书检查出错<${site.name}>`,
|
title: `站点证书${fromIpCheck?"(IP)":""}检查出错<${site.name}>`,
|
||||||
content: `站点名称: ${site.name} \n站点域名: ${site.domain} \n错误信息:${site.error}`,
|
content: `站点名称: ${site.name} \n站点域名: ${site.domain} \n错误信息:${site.error}`,
|
||||||
|
errorMessage: site.error,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
site.userId
|
site.userId
|
||||||
@@ -217,6 +267,7 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
title: `站点证书已过期${-validDays}天<${site.name}>`,
|
title: `站点证书已过期${-validDays}天<${site.name}>`,
|
||||||
content,
|
content,
|
||||||
url,
|
url,
|
||||||
|
errorMessage: "站点证书已过期"
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
site.userId
|
site.userId
|
||||||
@@ -250,4 +301,27 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
|
|||||||
async saveSetting(userId: number, bean: UserSiteMonitorSetting) {
|
async saveSetting(userId: number, bean: UserSiteMonitorSetting) {
|
||||||
await this.userSettingsService.saveSetting(userId, bean);
|
await this.userSettingsService.saveSetting(userId, bean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async ipCheckChange(req: {id: any; ipCheck: any}) {
|
||||||
|
|
||||||
|
await this.update({
|
||||||
|
id: req.id,
|
||||||
|
ipCheck: req.ipCheck,
|
||||||
|
});
|
||||||
|
if(req.ipCheck){
|
||||||
|
const site = await this.info(req.id);
|
||||||
|
await this.siteIpService.sync(site)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async disabledChange(req: { disabled: any; id: any }) {
|
||||||
|
await this.update({
|
||||||
|
id: req.id,
|
||||||
|
disabled: req.disabled,
|
||||||
|
});
|
||||||
|
if(!req.disabled){
|
||||||
|
const site = await this.info(req.id);
|
||||||
|
await this.doCheck(site)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
import {Inject, Provide, Scope, ScopeEnum} from "@midwayjs/core";
|
||||||
|
import {BaseService, SysSettingsService} from "@certd/lib-server";
|
||||||
|
import {InjectEntityModel} from "@midwayjs/typeorm";
|
||||||
|
import {Repository} from "typeorm";
|
||||||
|
import {SiteInfoEntity} from "../entity/site-info.js";
|
||||||
|
import {NotificationService} from "../../pipeline/service/notification-service.js";
|
||||||
|
import {UserSuiteService} from "@certd/commercial-core";
|
||||||
|
import {UserSettingsService} from "../../mine/service/user-settings-service.js";
|
||||||
|
import {SiteIpEntity} from "../entity/site-ip.js";
|
||||||
|
import dns from "dns";
|
||||||
|
import {logger, safePromise} from "@certd/basic";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import {siteTester} from "./site-tester.js";
|
||||||
|
import {PeerCertificate} from "tls";
|
||||||
|
|
||||||
|
@Provide()
|
||||||
|
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||||
|
export class SiteIpService extends BaseService<SiteIpEntity> {
|
||||||
|
@InjectEntityModel(SiteIpEntity)
|
||||||
|
repository: Repository<SiteIpEntity>;
|
||||||
|
@InjectEntityModel(SiteInfoEntity)
|
||||||
|
siteInfoRepository: Repository<SiteInfoEntity>;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
notificationService: NotificationService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
sysSettingsService: SysSettingsService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
userSuiteService: UserSuiteService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
userSettingsService: UserSettingsService;
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
getRepository() {
|
||||||
|
return this.repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
async add(data: SiteIpEntity) {
|
||||||
|
if (!data.userId) {
|
||||||
|
throw new Error("userId is required");
|
||||||
|
}
|
||||||
|
data.disabled = false;
|
||||||
|
const res= await super.add(data);
|
||||||
|
await this.updateIpCount(data.siteId)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async update(data: any) {
|
||||||
|
if (!data.id) {
|
||||||
|
throw new Error("id is required");
|
||||||
|
}
|
||||||
|
delete data.userId;
|
||||||
|
await super.update(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
async sync(entity: SiteInfoEntity) {
|
||||||
|
|
||||||
|
const domain = entity.domain;
|
||||||
|
//从域名解析中获取所有ip
|
||||||
|
const ips = await this.getAllIpsFromDomain(domain);
|
||||||
|
if (ips.length === 0 ) {
|
||||||
|
throw new Error(`没有发现${domain}的IP`)
|
||||||
|
}
|
||||||
|
//删除所有的ip
|
||||||
|
await this.repository.delete({
|
||||||
|
siteId: entity.id,
|
||||||
|
from: "sync"
|
||||||
|
});
|
||||||
|
|
||||||
|
//添加新的ip
|
||||||
|
for (const ip of ips) {
|
||||||
|
await this.repository.save({
|
||||||
|
ipAddress: ip,
|
||||||
|
userId: entity.userId,
|
||||||
|
siteId: entity.id,
|
||||||
|
from: "sync",
|
||||||
|
disabled:false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.checkAll(entity);
|
||||||
|
await this.updateIpCount(entity.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async check(ipId: number, domain: string, port: number) {
|
||||||
|
if(!ipId){
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const entity = await this.info(ipId);
|
||||||
|
if (!entity) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await this.update({
|
||||||
|
id: entity.id,
|
||||||
|
checkStatus: "checking",
|
||||||
|
lastCheckTime: dayjs().valueOf()
|
||||||
|
});
|
||||||
|
const res = await siteTester.test({
|
||||||
|
host: domain,
|
||||||
|
port: port,
|
||||||
|
retryTimes: 3,
|
||||||
|
ipAddress: entity.ipAddress
|
||||||
|
});
|
||||||
|
|
||||||
|
const certi: PeerCertificate = res.certificate;
|
||||||
|
if (!certi) {
|
||||||
|
throw new Error("没有发现证书");
|
||||||
|
}
|
||||||
|
const expires = certi.valid_to;
|
||||||
|
const allDomains = certi.subjectaltname?.replaceAll("DNS:", "").split(",") || [];
|
||||||
|
const mainDomain = certi.subject?.CN;
|
||||||
|
let domains = allDomains;
|
||||||
|
if (!allDomains.includes(mainDomain)) {
|
||||||
|
domains = [mainDomain, ...allDomains];
|
||||||
|
}
|
||||||
|
const issuer = `${certi.issuer.O}<${certi.issuer.CN}>`;
|
||||||
|
const isExpired = dayjs().valueOf() > dayjs(expires).valueOf();
|
||||||
|
const status = isExpired ? "expired" : "ok";
|
||||||
|
const updateData = {
|
||||||
|
id: entity.id,
|
||||||
|
certDomains: domains.join(","),
|
||||||
|
certStatus: status,
|
||||||
|
certProvider: issuer,
|
||||||
|
certExpiresTime: dayjs(expires).valueOf(),
|
||||||
|
lastCheckTime: dayjs().valueOf(),
|
||||||
|
error: null,
|
||||||
|
checkStatus: "ok"
|
||||||
|
};
|
||||||
|
|
||||||
|
await this.update(updateData);
|
||||||
|
|
||||||
|
return updateData
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("check site ip error", e);
|
||||||
|
await this.update({
|
||||||
|
id: entity.id,
|
||||||
|
checkStatus: "error",
|
||||||
|
lastCheckTime: dayjs().valueOf(),
|
||||||
|
error: e.message
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
id: entity.id,
|
||||||
|
ipAddress: entity.ipAddress,
|
||||||
|
error: e.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkAll(siteInfo: SiteInfoEntity,onFinish?: (e: any) => void) {
|
||||||
|
const siteId = siteInfo.id;
|
||||||
|
const ips = await this.repository.find({
|
||||||
|
where: {
|
||||||
|
siteId: siteId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const domain = siteInfo.domain;
|
||||||
|
const port = siteInfo.httpsPort;
|
||||||
|
const promiseList = [];
|
||||||
|
for (const item of ips) {
|
||||||
|
const func = async () => {
|
||||||
|
try {
|
||||||
|
return await this.check(item.id, domain, port);
|
||||||
|
} catch (e) {
|
||||||
|
logger.error("check site item error", e);
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
error:e.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
promiseList.push(func());
|
||||||
|
}
|
||||||
|
Promise.all(promiseList).then((res)=>{
|
||||||
|
const finished = res.filter(item=>{
|
||||||
|
return item!=null
|
||||||
|
})
|
||||||
|
if (finished.length > 0) {
|
||||||
|
onFinish && onFinish(finished)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllIpsFromDomain(domain: string) {
|
||||||
|
const getFromV4 = safePromise<string[]>((resolve, reject) => {
|
||||||
|
dns.resolve4(domain, (err, addresses) => {
|
||||||
|
if (err) {
|
||||||
|
logger.error(`[${domain}] resolve4 error`, err)
|
||||||
|
resolve([])
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(addresses);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const getFromV6 = safePromise<string[]>((resolve, reject) => {
|
||||||
|
dns.resolve6(domain, (err, addresses) => {
|
||||||
|
if (err) {
|
||||||
|
logger.error("[${domain}] resolve6 error", err)
|
||||||
|
resolve([])
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
resolve(addresses);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all([getFromV4, getFromV6]).then(res => {
|
||||||
|
return [...res[0], ...res[1]];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async updateIpCount(siteId:number){
|
||||||
|
const count = await this.repository.count({
|
||||||
|
where:{
|
||||||
|
siteId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await this.siteInfoRepository.update({
|
||||||
|
//where
|
||||||
|
id:siteId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
//update
|
||||||
|
ipCount:count
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +1,23 @@
|
|||||||
import {logger, safePromise, utils} from '@certd/basic';
|
import { logger, safePromise, utils } from "@certd/basic";
|
||||||
import { merge } from 'lodash-es';
|
import { merge } from "lodash-es";
|
||||||
import https from 'https';
|
import https from "https";
|
||||||
import { PeerCertificate } from 'tls';
|
import { PeerCertificate } from "tls";
|
||||||
|
|
||||||
export type SiteTestReq = {
|
export type SiteTestReq = {
|
||||||
host: string; // 只用域名部分
|
host: string; // 只用域名部分
|
||||||
port?: number;
|
port?: number;
|
||||||
method?: string;
|
method?: string;
|
||||||
retryTimes?: number;
|
retryTimes?: number;
|
||||||
|
ipAddress?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SiteTestRes = {
|
export type SiteTestRes = {
|
||||||
certificate?: PeerCertificate;
|
certificate?: PeerCertificate;
|
||||||
};
|
};
|
||||||
|
|
||||||
export class SiteTester {
|
export class SiteTester {
|
||||||
async test(req: SiteTestReq): Promise<SiteTestRes> {
|
async test(req: SiteTestReq): Promise<SiteTestRes> {
|
||||||
logger.info('测试站点:', JSON.stringify(req));
|
logger.info("测试站点:", JSON.stringify(req));
|
||||||
const maxRetryTimes = req.retryTimes ?? 3;
|
const maxRetryTimes = req.retryTimes ?? 3;
|
||||||
let tryCount = 0;
|
let tryCount = 0;
|
||||||
let result: SiteTestRes = {};
|
let result: SiteTestRes = {};
|
||||||
@@ -37,17 +40,28 @@ export class SiteTester {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async doTestOnce(req: SiteTestReq): Promise<SiteTestRes> {
|
async doTestOnce(req: SiteTestReq): Promise<SiteTestRes> {
|
||||||
const agent = new https.Agent({ keepAlive: false });
|
|
||||||
|
|
||||||
const options: any = merge(
|
const options: any = merge(
|
||||||
{
|
{
|
||||||
port: 443,
|
port: 443,
|
||||||
method: 'GET',
|
method: "GET",
|
||||||
rejectUnauthorized: false,
|
rejectUnauthorized: false
|
||||||
},
|
},
|
||||||
req
|
req
|
||||||
);
|
);
|
||||||
options.agent = agent;
|
|
||||||
|
if (req.ipAddress) {
|
||||||
|
//使用固定的ip
|
||||||
|
const ipAddress = req.ipAddress;
|
||||||
|
options.headers={
|
||||||
|
host: options.host,
|
||||||
|
//sni
|
||||||
|
servername: options.host
|
||||||
|
}
|
||||||
|
options.host = ipAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
options.agent = new https.Agent({ keepAlive: false });
|
||||||
|
|
||||||
// 创建 HTTPS 请求
|
// 创建 HTTPS 请求
|
||||||
const requestPromise = safePromise((resolve, reject) => {
|
const requestPromise = safePromise((resolve, reject) => {
|
||||||
const req = https.request(options, res => {
|
const req = https.request(options, res => {
|
||||||
@@ -56,20 +70,20 @@ export class SiteTester {
|
|||||||
const certificate = res.socket.getPeerCertificate();
|
const certificate = res.socket.getPeerCertificate();
|
||||||
// logger.info('证书信息', certificate);
|
// logger.info('证书信息', certificate);
|
||||||
if (certificate.subject == null) {
|
if (certificate.subject == null) {
|
||||||
logger.warn('证书信息为空');
|
logger.warn("证书信息为空");
|
||||||
resolve({
|
resolve({
|
||||||
certificate: null,
|
certificate: null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
resolve({
|
resolve({
|
||||||
certificate,
|
certificate
|
||||||
});
|
});
|
||||||
res.socket.end();
|
res.socket.end();
|
||||||
// 关闭响应
|
// 关闭响应
|
||||||
res.destroy();
|
res.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
req.on('error', e => {
|
req.on("error", e => {
|
||||||
reject(e);
|
reject(e);
|
||||||
});
|
});
|
||||||
req.end();
|
req.end();
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import {CnameRecordService} from "../../cname/service/cname-record-service.js";
|
|||||||
import {PluginConfigGetter} from "../../plugin/service/plugin-config-getter.js";
|
import {PluginConfigGetter} from "../../plugin/service/plugin-config-getter.js";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import {DbAdapter} from "../../db/index.js";
|
import {DbAdapter} from "../../db/index.js";
|
||||||
import {isComm} from "@certd/plus-core";
|
import {isComm, isPlus} from "@certd/plus-core";
|
||||||
import {logger} from "@certd/basic";
|
import {logger} from "@certd/basic";
|
||||||
import {UrlService} from "./url-service.js";
|
import {UrlService} from "./url-service.js";
|
||||||
import {NotificationService} from "./notification-service.js";
|
import {NotificationService} from "./notification-service.js";
|
||||||
@@ -429,6 +429,12 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
|||||||
logger.info('当前定时器数量:', this.cron.getTaskSize());
|
logger.info('当前定时器数量:', this.cron.getTaskSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param id
|
||||||
|
* @param triggerId =null手动启动
|
||||||
|
* @param stepId 如果传入ALL,清空所有状态
|
||||||
|
*/
|
||||||
async run(id: number, triggerId: string, stepId?: string) {
|
async run(id: number, triggerId: string, stepId?: string) {
|
||||||
const entity: PipelineEntity = await this.info(id);
|
const entity: PipelineEntity = await this.info(id);
|
||||||
await this.doRun(entity, triggerId, stepId);
|
await this.doRun(entity, triggerId, stepId);
|
||||||
@@ -684,6 +690,42 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
|||||||
{ groupId }
|
{ groupId }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
async batchRerun(ids: number[], userId: any) {
|
||||||
|
if (!isPlus()){
|
||||||
|
throw new NeedVIPException("此功能需要升级专业版")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userId || ids.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const list = await this.repository.find({
|
||||||
|
select:{
|
||||||
|
id:true
|
||||||
|
},
|
||||||
|
where:{
|
||||||
|
id: In(ids),
|
||||||
|
userId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
ids = list.map(item=>item.id)
|
||||||
|
|
||||||
|
//异步执行
|
||||||
|
this.startBatchRerun(ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
async startBatchRerun(ids: number[]){
|
||||||
|
//20条一批
|
||||||
|
const batchSize = 20;
|
||||||
|
for (let i = 0; i < ids.length; i += batchSize) {
|
||||||
|
const batchIds = ids.slice(i, i + batchSize);
|
||||||
|
const batchPromises = batchIds.map(async (id)=>{
|
||||||
|
await this.run(id,null,"ALL")
|
||||||
|
});
|
||||||
|
await Promise.all(batchPromises)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async getUserPipelineCount(userId) {
|
async getUserPipelineCount(userId) {
|
||||||
return await this.repository.count({ where: { userId } });
|
return await this.repository.count({ where: { userId } });
|
||||||
|
|||||||
@@ -22,3 +22,4 @@ export * from './plugin-51dns/index.js'
|
|||||||
export * from './plugin-notification/index.js'
|
export * from './plugin-notification/index.js'
|
||||||
export * from './plugin-flex/index.js'
|
export * from './plugin-flex/index.js'
|
||||||
export * from './plugin-farcdn/index.js'
|
export * from './plugin-farcdn/index.js'
|
||||||
|
export * from './plugin-fnos/index.js'
|
||||||
|
|||||||
@@ -132,10 +132,12 @@ export class AliyunDeployCertToESA extends AbstractTaskPlugin {
|
|||||||
// 接口版本
|
// 接口版本
|
||||||
version: "2024-09-10",
|
version: "2024-09-10",
|
||||||
data: {
|
data: {
|
||||||
SiteId: siteId,
|
body:{
|
||||||
CasId: certId,
|
SiteId: siteId,
|
||||||
Type: "cas",
|
CasId: certId,
|
||||||
Name: certName
|
Type: "cas",
|
||||||
|
Name: certName
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.logger.info(`部署站点[${siteId}]证书成功:${JSON.stringify(res)}`);
|
this.logger.info(`部署站点[${siteId}]证书成功:${JSON.stringify(res)}`);
|
||||||
@@ -197,8 +199,10 @@ export class AliyunDeployCertToESA extends AbstractTaskPlugin {
|
|||||||
action: "ListCertificates",
|
action: "ListCertificates",
|
||||||
version: "2024-09-10",
|
version: "2024-09-10",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
query: {
|
data:{
|
||||||
SiteId: siteId
|
query: {
|
||||||
|
SiteId: siteId
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -212,10 +216,12 @@ export class AliyunDeployCertToESA extends AbstractTaskPlugin {
|
|||||||
version: "2024-09-10",
|
version: "2024-09-10",
|
||||||
// 接口 HTTP 方法
|
// 接口 HTTP 方法
|
||||||
method: "GET",
|
method: "GET",
|
||||||
query: {
|
data:{
|
||||||
SiteId: siteId,
|
query: {
|
||||||
Id: item.id
|
SiteId: siteId,
|
||||||
}
|
Id: item.id
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.logger.info(`证书${item.Name}已删除`);
|
this.logger.info(`证书${item.Name}已删除`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
|
import {AbstractTaskPlugin, IsTaskPlugin, PageReq, pluginGroups, RunStrategy, TaskInput} from '@certd/pipeline';
|
||||||
import { CertInfo } from '@certd/plugin-cert';
|
import {CertInfo} from '@certd/plugin-cert';
|
||||||
import { AliyunAccess, AliyunClient, AliyunSslClient, CasCertInfo, createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from '@certd/plugin-lib';
|
import {
|
||||||
import { CertApplyPluginNames} from '@certd/plugin-cert';
|
AliyunAccess,
|
||||||
|
AliyunClient,
|
||||||
|
AliyunSslClient,
|
||||||
|
CasCertInfo,
|
||||||
|
createCertDomainGetterInputDefine,
|
||||||
|
createRemoteSelectInputDefine
|
||||||
|
} from '@certd/plugin-lib';
|
||||||
|
import {CertApplyPluginNames} from '@certd/plugin-cert';
|
||||||
|
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
name: 'AliyunDeployCertToSLB',
|
name: 'AliyunDeployCertToSLB',
|
||||||
title: '阿里云-部署至SLB(传统负载均衡)',
|
title: '阿里云-部署至CLB(传统负载均衡)',
|
||||||
icon: 'svg:icon-aliyun',
|
icon: 'svg:icon-aliyun',
|
||||||
group: pluginGroups.aliyun.key,
|
group: pluginGroups.aliyun.key,
|
||||||
desc: '部署证书到阿里云SLB(传统负载均衡)',
|
desc: '部署证书到阿里云CLB(传统负载均衡)',
|
||||||
needPlus: false,
|
needPlus: false,
|
||||||
default: {
|
default: {
|
||||||
strategy: {
|
strategy: {
|
||||||
@@ -27,9 +35,27 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin {
|
|||||||
})
|
})
|
||||||
cert!: CertInfo | number;
|
cert!: CertInfo | number;
|
||||||
|
|
||||||
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
|
@TaskInput(createCertDomainGetterInputDefine({props: {required: false}}))
|
||||||
certDomains!: string[];
|
certDomains!: string[];
|
||||||
|
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: '证书接入点',
|
||||||
|
helper: '不会选就保持默认即可',
|
||||||
|
value: 'cas.aliyuncs.com',
|
||||||
|
component: {
|
||||||
|
name: 'a-select',
|
||||||
|
options: [
|
||||||
|
{value: 'cas.aliyuncs.com', label: '中国大陆'},
|
||||||
|
{value: 'cas.ap-southeast-1.aliyuncs.com', label: '新加坡'},
|
||||||
|
{value: 'cas.eu-central-1.aliyuncs.com', label: '德国(法兰克福)'},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
casEndpoint!: string;
|
||||||
|
|
||||||
|
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: 'Access授权',
|
title: 'Access授权',
|
||||||
helper: '阿里云授权AccessKeyId、AccessKeySecret',
|
helper: '阿里云授权AccessKeyId、AccessKeySecret',
|
||||||
@@ -41,10 +67,11 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin {
|
|||||||
})
|
})
|
||||||
accessId!: string;
|
accessId!: string;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@TaskInput(
|
@TaskInput(
|
||||||
createRemoteSelectInputDefine({
|
createRemoteSelectInputDefine({
|
||||||
title: 'LB所在地区',
|
title: 'LB所在地区',
|
||||||
typeName: 'AliyunDeployCertToSLB',
|
|
||||||
multi: false,
|
multi: false,
|
||||||
action: AliyunDeployCertToSLB.prototype.onGetRegionList.name,
|
action: AliyunDeployCertToSLB.prototype.onGetRegionList.name,
|
||||||
watches: ['accessId'],
|
watches: ['accessId'],
|
||||||
@@ -56,7 +83,6 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin {
|
|||||||
createRemoteSelectInputDefine({
|
createRemoteSelectInputDefine({
|
||||||
title: '负载均衡列表',
|
title: '负载均衡列表',
|
||||||
helper: '要部署证书的负载均衡ID',
|
helper: '要部署证书的负载均衡ID',
|
||||||
typeName: 'AliyunDeployCertToSLB',
|
|
||||||
action: AliyunDeployCertToSLB.prototype.onGetLoadBalanceList.name,
|
action: AliyunDeployCertToSLB.prototype.onGetLoadBalanceList.name,
|
||||||
watches: ['regionId'],
|
watches: ['regionId'],
|
||||||
})
|
})
|
||||||
@@ -67,33 +93,59 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin {
|
|||||||
createRemoteSelectInputDefine({
|
createRemoteSelectInputDefine({
|
||||||
title: '监听器列表',
|
title: '监听器列表',
|
||||||
helper: '要部署证书的监听器列表',
|
helper: '要部署证书的监听器列表',
|
||||||
typeName: 'AliyunDeployCertToSLB',
|
|
||||||
action: AliyunDeployCertToSLB.prototype.onGetListenerList.name,
|
action: AliyunDeployCertToSLB.prototype.onGetListenerList.name,
|
||||||
watches: ['loadBalancers'],
|
watches: ['loadBalancers'],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
listeners!: string[];
|
listeners!: string[];
|
||||||
|
|
||||||
@TaskInput({
|
|
||||||
title: '证书接入点',
|
|
||||||
helper: '不会选就保持默认即可',
|
|
||||||
value: 'cas.aliyuncs.com',
|
|
||||||
component: {
|
|
||||||
name: 'a-select',
|
|
||||||
options: [
|
|
||||||
{ value: 'cas.aliyuncs.com', label: '中国大陆' },
|
|
||||||
{ value: 'cas.ap-southeast-1.aliyuncs.com', label: '新加坡' },
|
|
||||||
{ value: 'cas.eu-central-1.aliyuncs.com', label: '德国(法兰克福)' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
casEndpoint!: string;
|
|
||||||
|
|
||||||
async onInstance() {}
|
@TaskInput({
|
||||||
|
title: "部署默认证书",
|
||||||
|
value: true,
|
||||||
|
component: {
|
||||||
|
name: "a-switch",
|
||||||
|
vModel: "checked"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
deployDefault!: boolean;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "部署扩展证书",
|
||||||
|
value: false,
|
||||||
|
component: {
|
||||||
|
name: "a-switch",
|
||||||
|
vModel: "checked"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
deployExtension!: boolean;
|
||||||
|
|
||||||
|
|
||||||
|
@TaskInput(
|
||||||
|
createRemoteSelectInputDefine({
|
||||||
|
title: '扩展域名列表',
|
||||||
|
helper: '要部署扩展域名列表',
|
||||||
|
action: AliyunDeployCertToSLB.prototype.onGetExtensionDomainList.name,
|
||||||
|
watches: ['listeners','deployExtension'],
|
||||||
|
mergeScript:`
|
||||||
|
return {
|
||||||
|
show: ctx.compute(({form})=>{
|
||||||
|
return form.deployExtension;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
`
|
||||||
|
})
|
||||||
|
)
|
||||||
|
extensionDomains!: string[];
|
||||||
|
|
||||||
|
|
||||||
|
async onInstance() {
|
||||||
|
}
|
||||||
|
|
||||||
async getLBClient(access: AliyunAccess, region: string) {
|
async getLBClient(access: AliyunAccess, region: string) {
|
||||||
const client = new AliyunClient({ logger: this.logger });
|
const client = new AliyunClient({logger: this.logger});
|
||||||
const version = '2014-05-15';
|
const version = '2014-05-15';
|
||||||
await client.init({
|
await client.init({
|
||||||
accessKeyId: access.accessKeyId,
|
accessKeyId: access.accessKeyId,
|
||||||
@@ -106,30 +158,76 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async execute(): Promise<void> {
|
async execute(): Promise<void> {
|
||||||
this.logger.info(`开始部署证书到阿里云(slb)`);
|
this.logger.info(`开始部署证书到阿里云(clb)`);
|
||||||
const access = await this.getAccess<AliyunAccess>(this.accessId);
|
const access = await this.getAccess<AliyunAccess>(this.accessId);
|
||||||
|
|
||||||
const client = await this.getLBClient(access, this.regionId);
|
const client = await this.getLBClient(access, this.regionId);
|
||||||
const aliyunCert = await this.getAliyunCertId(access);
|
const aliyunCert = await this.getAliyunCertId(access);
|
||||||
const slbServerCertId = await this.uploadServerCert(client, aliyunCert);
|
const slbServerCertId = await this.uploadServerCert(client, aliyunCert);
|
||||||
for (const listener of this.listeners) {
|
|
||||||
const arr = listener.split('_');
|
|
||||||
const loadBalanceId = arr[0];
|
|
||||||
const port = arr[2];
|
|
||||||
const params = {
|
|
||||||
RegionId: this.regionId,
|
|
||||||
LoadBalancerId: loadBalanceId,
|
|
||||||
ListenerPort: parseInt(port),
|
|
||||||
ServerCertificateId: slbServerCertId,
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = await client.request('SetLoadBalancerHTTPSListenerAttribute', params);
|
if (this.deployDefault!==false) {
|
||||||
this.checkRet(res);
|
this.logger.info("部署监听器默认证书")
|
||||||
this.logger.info(`部署${listener}监听器证书成功`, JSON.stringify(res));
|
for (const listener of this.listeners) {
|
||||||
|
const {port, loadBalanceId} = this.resolveListenerKey(listener)
|
||||||
|
const params = {
|
||||||
|
RegionId: this.regionId,
|
||||||
|
LoadBalancerId: loadBalanceId,
|
||||||
|
ListenerPort: port,
|
||||||
|
ServerCertificateId: slbServerCertId,
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await client.request('SetLoadBalancerHTTPSListenerAttribute', params);
|
||||||
|
this.checkRet(res);
|
||||||
|
this.logger.info(`部署${listener}监听器证书成功`, JSON.stringify(res));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.deployExtension) {
|
||||||
|
this.logger.info("部署监听器扩展域名证书")
|
||||||
|
|
||||||
|
const clientV2 = this.getCLBClientV2(access);
|
||||||
|
for (const domainStr of this.extensionDomains) {
|
||||||
|
const {extensionDomainId} = this.resolveListenerKey(domainStr)
|
||||||
|
const res = await clientV2.doRequest({
|
||||||
|
action: "SetDomainExtensionAttribute",
|
||||||
|
// 接口版本
|
||||||
|
version: "2014-05-15",
|
||||||
|
data: {
|
||||||
|
query: {
|
||||||
|
RegionId: this.regionId,
|
||||||
|
DomainExtensionId: extensionDomainId,
|
||||||
|
ServerCertificateId: slbServerCertId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.logger.info(`部署扩展域名${extensionDomainId}证书成功`, JSON.stringify(res))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.logger.info('执行完成');
|
this.logger.info('执行完成');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCLBClientV2(access: AliyunAccess) {
|
||||||
|
return access.getClient("slb.aliyuncs.com")
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveListenerKey(listener: string) {
|
||||||
|
const arr = listener.split('_');
|
||||||
|
const loadBalanceId = arr[0];
|
||||||
|
const protocol = arr[1];
|
||||||
|
const port = arr[2];
|
||||||
|
let extensionDomainId = undefined;
|
||||||
|
if (arr.length > 3) {
|
||||||
|
extensionDomainId = arr[3];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
loadBalanceId,
|
||||||
|
port: parseInt(port),
|
||||||
|
extensionDomainId: extensionDomainId,
|
||||||
|
protocol: protocol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async uploadServerCert(client: any, aliyunCert: CasCertInfo) {
|
async uploadServerCert(client: any, aliyunCert: CasCertInfo) {
|
||||||
const params = {
|
const params = {
|
||||||
RegionId: this.regionId,
|
RegionId: this.regionId,
|
||||||
@@ -248,6 +346,78 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async onGetExtensionDomainList(data: PageReq) {
|
||||||
|
if (!this.accessId) {
|
||||||
|
throw new Error('请先选择Access授权');
|
||||||
|
}
|
||||||
|
if (!this.regionId) {
|
||||||
|
throw new Error('请先选择地区');
|
||||||
|
}
|
||||||
|
if (!this.listeners && this.listeners.length == 0) {
|
||||||
|
throw new Error('请先选择监听器');
|
||||||
|
}
|
||||||
|
const access = await this.getAccess<AliyunAccess>(this.accessId);
|
||||||
|
|
||||||
|
const allDomains: any[] = []
|
||||||
|
for (const ls of this.listeners) {
|
||||||
|
const {port, loadBalanceId, protocol} = this.resolveListenerKey(ls)
|
||||||
|
const domains = await this.doGetExtensionDomainList({
|
||||||
|
access,
|
||||||
|
loadBalancerId: loadBalanceId,
|
||||||
|
listenerPort: port,
|
||||||
|
listenerProtocol: protocol,
|
||||||
|
});
|
||||||
|
allDomains.push(...domains)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.ctx.utils.options.buildGroupOptions(allDomains, this.certDomains)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async doGetExtensionDomainList(data: {
|
||||||
|
loadBalancerId: string,
|
||||||
|
listenerPort: number,
|
||||||
|
listenerProtocol: string,
|
||||||
|
access: AliyunAccess
|
||||||
|
}) {
|
||||||
|
const {loadBalancerId, listenerPort, listenerProtocol, access} = data;
|
||||||
|
const client = access.getClient("slb.aliyuncs.com")
|
||||||
|
|
||||||
|
let queries = {
|
||||||
|
RegionId: this.regionId,
|
||||||
|
LoadBalancerId: loadBalancerId,
|
||||||
|
ListenerPort: listenerPort
|
||||||
|
};
|
||||||
|
|
||||||
|
const res = await client.doRequest({
|
||||||
|
// 接口名称
|
||||||
|
action: "DescribeDomainExtensions",
|
||||||
|
// 接口版本
|
||||||
|
version: "2014-05-15",
|
||||||
|
data: {
|
||||||
|
query: queries,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.checkRet(res);
|
||||||
|
const list = res?.DomainExtensions.DomainExtension;
|
||||||
|
if (!list || list.length === 0) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.map((i: any) => {
|
||||||
|
const value = `${loadBalancerId}_${listenerProtocol ?? "HTTPS"}_${listenerPort}_${i.DomainExtensionId}`;
|
||||||
|
const label = `${i.DomainExtensionId}<${i.Domain}>`;
|
||||||
|
return {
|
||||||
|
value: value,
|
||||||
|
label: label,
|
||||||
|
domain:i.Domain
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
checkRet(ret: any) {
|
checkRet(ret: any) {
|
||||||
if (ret.Code != null) {
|
if (ret.Code != null) {
|
||||||
throw new Error(ret.Message);
|
throw new Error(ret.Message);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { CertInfo, CertReader } from "@certd/plugin-cert";
|
|||||||
export class FarcdnAccess extends BaseAccess {
|
export class FarcdnAccess extends BaseAccess {
|
||||||
@AccessInput({
|
@AccessInput({
|
||||||
title: "接口地址",
|
title: "接口地址",
|
||||||
|
value:"https://open.farcdn.net/api/source",
|
||||||
component: {
|
component: {
|
||||||
placeholder: "https://open.farcdn.net/api/source",
|
placeholder: "https://open.farcdn.net/api/source",
|
||||||
name: "a-input",
|
name: "a-input",
|
||||||
@@ -63,7 +64,7 @@ export class FarcdnAccess extends BaseAccess {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
encrypt: false,
|
encrypt: false,
|
||||||
required: true
|
required: false
|
||||||
})
|
})
|
||||||
httpProxy!: string;
|
httpProxy!: string;
|
||||||
|
|
||||||
@@ -79,21 +80,16 @@ export class FarcdnAccess extends BaseAccess {
|
|||||||
testRequest = true;
|
testRequest = true;
|
||||||
|
|
||||||
async onTestRequest() {
|
async onTestRequest() {
|
||||||
try{
|
await this.getSSLCertList({size:1});
|
||||||
const data = await this.findSSLCertConfig(1);
|
return "ok"
|
||||||
if (data) {
|
|
||||||
return "ok";
|
|
||||||
}
|
|
||||||
}catch (e) {
|
|
||||||
if(e.message.indexOf("11111111")>-1){
|
|
||||||
return "ok";
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw "测试失败,未知错误";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getSSLCertList(req:{offset?:number,size?:number}){
|
||||||
|
return await this.doRequest({
|
||||||
|
url: "/getSSLCertList",
|
||||||
|
data: req
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
async findSSLCertConfig(sslCertId: number) {
|
async findSSLCertConfig(sslCertId: number) {
|
||||||
/**
|
/**
|
||||||
@@ -154,11 +150,11 @@ export class FarcdnAccess extends BaseAccess {
|
|||||||
keyData: req.cert.key,
|
keyData: req.cert.key,
|
||||||
isOn: true,
|
isOn: true,
|
||||||
isCA: false,
|
isCA: false,
|
||||||
serverName: oldCert.serverName,
|
serverName: oldCert.serverName || certReader.getMainDomain(),
|
||||||
commonNames: [certReader.getMainDomain()],
|
commonNames: [certReader.getMainDomain()],
|
||||||
dnsNames: certReader.getAltNames(),
|
dnsNames: certReader.getAltNames(),
|
||||||
timeBeginAt: detail.notBefore,
|
timeBeginAt: detail.notBefore.getTime(),
|
||||||
timeEndAt: detail.notAfter,
|
timeEndAt: detail.notAfter.getTime(),
|
||||||
name: oldCert.name|| certReader.buildCertName(),
|
name: oldCert.name|| certReader.buildCertName(),
|
||||||
description:oldCert.description||""
|
description:oldCert.description||""
|
||||||
}
|
}
|
||||||
@@ -183,7 +179,7 @@ export class FarcdnAccess extends BaseAccess {
|
|||||||
httpProxy: this.httpProxy||undefined,
|
httpProxy: this.httpProxy||undefined,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.code === "200") {
|
if (res.code === 200) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
throw new Error(res.message || res);
|
throw new Error(res.message || res);
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
import { AbstractTaskPlugin, IsTaskPlugin, PageReq, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||||
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
|
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
|
||||||
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
|
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
|
||||||
import { FarcdnAccess } from "../access.js";
|
import { FarcdnAccess } from "../access.js";
|
||||||
import { AbstractPlusTaskPlugin } from "@certd/plugin-plus";
|
|
||||||
|
|
||||||
@IsTaskPlugin({
|
@IsTaskPlugin({
|
||||||
//命名规范,插件类型+功能(就是目录plugin-demo中的demo),大写字母开头,驼峰命名
|
//命名规范,插件类型+功能(就是目录plugin-demo中的demo),大写字母开头,驼峰命名
|
||||||
name: "FarcdnRefreshCert",
|
name: "FarcdnRefreshCert",
|
||||||
title: "farcdn-更新证书",
|
title: "farcdn-更新证书",
|
||||||
|
desc:"www.farcdn.net",
|
||||||
icon: "svg:icon-lucky",
|
icon: "svg:icon-lucky",
|
||||||
//插件分组
|
//插件分组
|
||||||
group: pluginGroups.cdn.key,
|
group: pluginGroups.cdn.key,
|
||||||
needPlus: true,
|
needPlus: false,
|
||||||
default: {
|
default: {
|
||||||
//默认值配置照抄即可
|
//默认值配置照抄即可
|
||||||
strategy: {
|
strategy: {
|
||||||
@@ -20,7 +20,7 @@ import { AbstractPlusTaskPlugin } from "@certd/plugin-plus";
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
//类名规范,跟上面插件名称(name)一致
|
//类名规范,跟上面插件名称(name)一致
|
||||||
export class FarcdnRefreshCert extends AbstractPlusTaskPlugin {
|
export class FarcdnRefreshCert extends AbstractTaskPlugin {
|
||||||
//证书选择,此项必须要有
|
//证书选择,此项必须要有
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: "域名证书",
|
title: "域名证书",
|
||||||
@@ -52,6 +52,7 @@ export class FarcdnRefreshCert extends AbstractPlusTaskPlugin {
|
|||||||
createRemoteSelectInputDefine({
|
createRemoteSelectInputDefine({
|
||||||
title: "证书Id",
|
title: "证书Id",
|
||||||
helper: "要更新的Farcdn证书id",
|
helper: "要更新的Farcdn证书id",
|
||||||
|
|
||||||
action: FarcdnRefreshCert.prototype.onGetCertList.name
|
action: FarcdnRefreshCert.prototype.onGetCertList.name
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -77,28 +78,31 @@ export class FarcdnRefreshCert extends AbstractPlusTaskPlugin {
|
|||||||
this.logger.info("部署完成");
|
this.logger.info("部署完成");
|
||||||
}
|
}
|
||||||
|
|
||||||
async onGetCertList() {
|
async onGetCertList(data:PageReq = {}) {
|
||||||
throw new Error("暂无查询证书列表接口,您需要手动输入证书id");
|
const access = await this.getAccess<FarcdnAccess>(this.accessId);
|
||||||
// const access = await this.getAccess<FarcdnAccess>(this.accessId);
|
|
||||||
|
|
||||||
// const res = await access.doRequest({
|
const res = await access.getSSLCertList({
|
||||||
// url: "/SSLCertService/listSSLCerts",
|
offset: data.offset?? 0,
|
||||||
// data: { size: 1000 },
|
size: data.limit?? 100,
|
||||||
// method: "POST"
|
});
|
||||||
// });
|
const list = res.list
|
||||||
// const list = JSON.parse(this.ctx.utils.hash.base64Decode(res.sslCertsJSON));
|
if (!list || list.length === 0) {
|
||||||
// if (!list || list.length === 0) {
|
throw new Error("没有找到证书,请先在控制台上传一次证书且关联网站");
|
||||||
// throw new Error("没有找到证书,请先在控制台上传一次证书且关联网站");
|
}
|
||||||
// }
|
|
||||||
//
|
const options = list.map((item: any) => {
|
||||||
// const options = list.map((item: any) => {
|
return {
|
||||||
// return {
|
label: `${item.name}<${item.id}>`,
|
||||||
// label: `${item.name}<${item.id}-${item.dnsNames[0]}>`,
|
value: item.id,
|
||||||
// value: item.id,
|
domain: item.dnsNames
|
||||||
// domain: item.dnsNames
|
};
|
||||||
// };
|
});
|
||||||
// });
|
return {
|
||||||
// return this.ctx.utils.options.buildGroupOptions(options, this.certDomains);
|
list:this.ctx.utils.options.buildGroupOptions(options, this.certDomains),
|
||||||
|
total:res.total,
|
||||||
|
offset: res.offset,
|
||||||
|
limit:res.size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ export class FlexCDNAccess extends BaseAccess {
|
|||||||
vModel: "checked"
|
vModel: "checked"
|
||||||
},
|
},
|
||||||
encrypt: false,
|
encrypt: false,
|
||||||
required: true
|
required: true,
|
||||||
|
value: false,
|
||||||
})
|
})
|
||||||
skipSslVerify!: boolean;
|
skipSslVerify!: boolean;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||||
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
|
import { CertApplyPluginNames, CertInfo,CertReader } from "@certd/plugin-cert";
|
||||||
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
|
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
|
||||||
import { FlexCDNAccess } from "../access.js";
|
import { FlexCDNAccess } from "../access.js";
|
||||||
import { FlexCDNClient } from "../client.js";
|
import { FlexCDNClient } from "../client.js";
|
||||||
@@ -83,7 +83,24 @@ export class FlexCDNRefreshCert extends AbstractTaskPlugin {
|
|||||||
|
|
||||||
const sslCert = JSON.parse(this.ctx.utils.hash.base64Decode(res.sslCertJSON))
|
const sslCert = JSON.parse(this.ctx.utils.hash.base64Decode(res.sslCertJSON))
|
||||||
this.logger.info(`证书信息:${sslCert.name},${sslCert.dnsNames}`);
|
this.logger.info(`证书信息:${sslCert.name},${sslCert.dnsNames}`);
|
||||||
|
const certReader = new CertReader(this.cert)
|
||||||
|
/**
|
||||||
|
* commonNames: commonNames,
|
||||||
|
* dnsNames: dnsNames,
|
||||||
|
* timeBeginAt: Math.floor((new Date(currentInfo.validFrom)).getTime() / 1000),
|
||||||
|
* timeEndAt: Math.floor((new Date(currentInfo.validTo)).getTime() / 1000),
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const commonNames =[ certReader.getMainDomain()]
|
||||||
|
const dnsNames = certReader.getAltNames()
|
||||||
|
const timeBeginAt = Math.floor(certReader.detail.notBefore.getTime() / 1000);
|
||||||
|
const timeEndAt = Math.floor(certReader.detail.notAfter.getTime() / 1000);
|
||||||
const body = {
|
const body = {
|
||||||
|
...sslCert, // inherit old cert info like name and description
|
||||||
|
commonNames,
|
||||||
|
dnsNames,
|
||||||
|
timeBeginAt,
|
||||||
|
timeEndAt,
|
||||||
name: sslCert.name,
|
name: sslCert.name,
|
||||||
sslCertId: item,
|
sslCertId: item,
|
||||||
certData: this.ctx.utils.hash.base64(this.cert.crt),
|
certData: this.ctx.utils.hash.base64(this.cert.crt),
|
||||||
@@ -122,7 +139,7 @@ export class FlexCDNRefreshCert extends AbstractTaskPlugin {
|
|||||||
|
|
||||||
const options = list.map((item: any) => {
|
const options = list.map((item: any) => {
|
||||||
return {
|
return {
|
||||||
label: `${item.name}<${item.id}-${item.dnsNames[0]}>`,
|
label: `${item.name}<${item.id}-${item.dnsNames?.[0]}>`,
|
||||||
value: item.id,
|
value: item.id,
|
||||||
domain: item.dnsNames
|
domain: item.dnsNames
|
||||||
};
|
};
|
||||||
|
|||||||
170
packages/ui/certd-server/src/plugins/plugin-fnos/index.ts
Normal file
170
packages/ui/certd-server/src/plugins/plugin-fnos/index.ts
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
|
||||||
|
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
|
||||||
|
import {
|
||||||
|
createCertDomainGetterInputDefine,
|
||||||
|
createRemoteSelectInputDefine,
|
||||||
|
SshAccess,
|
||||||
|
SshClient
|
||||||
|
} from "@certd/plugin-lib";
|
||||||
|
|
||||||
|
@IsTaskPlugin({
|
||||||
|
//命名规范,插件类型+功能(就是目录plugin-demo中的demo),大写字母开头,驼峰命名
|
||||||
|
name: "FnOSDeployToNAS",
|
||||||
|
title: "飞牛NAS-部署证书",
|
||||||
|
icon: "svg:icon-lucky",
|
||||||
|
//插件分组
|
||||||
|
group: pluginGroups.panel.key,
|
||||||
|
needPlus: false,
|
||||||
|
default: {
|
||||||
|
//默认值配置照抄即可
|
||||||
|
strategy: {
|
||||||
|
runStrategy: RunStrategy.SkipWhenSucceed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//类名规范,跟上面插件名称(name)一致
|
||||||
|
export class FnOSDeployToNAS extends AbstractTaskPlugin {
|
||||||
|
//证书选择,此项必须要有
|
||||||
|
@TaskInput({
|
||||||
|
title: "域名证书",
|
||||||
|
helper: "请选择前置任务输出的域名证书",
|
||||||
|
component: {
|
||||||
|
name: "output-selector",
|
||||||
|
from: [...CertApplyPluginNames]
|
||||||
|
}
|
||||||
|
// required: true, // 必填
|
||||||
|
})
|
||||||
|
cert!: CertInfo;
|
||||||
|
|
||||||
|
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
|
||||||
|
certDomains!: string[];
|
||||||
|
|
||||||
|
//授权选择框
|
||||||
|
@TaskInput({
|
||||||
|
title: "飞牛SSH授权",
|
||||||
|
component: {
|
||||||
|
name: "access-selector",
|
||||||
|
type: "ssh" //固定授权类型
|
||||||
|
},
|
||||||
|
helper:"请先配置sudo免密:\nsudo visudo\n#在文件最后一行增加以下内容,需要将username替换成自己的用户名\nusername ALL=(ALL) NOPASSWD: NOPASSWD: ALL\nctrl+x 保存退出",
|
||||||
|
required: true //必填
|
||||||
|
})
|
||||||
|
accessId!: string;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@TaskInput(
|
||||||
|
createRemoteSelectInputDefine({
|
||||||
|
title: "证书Id",
|
||||||
|
helper: "要更新的证书id",
|
||||||
|
action: FnOSDeployToNAS.prototype.onGetCertList.name
|
||||||
|
})
|
||||||
|
)
|
||||||
|
certList!: number[];
|
||||||
|
|
||||||
|
//插件实例化时执行的方法
|
||||||
|
async onInstance() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//插件执行方法
|
||||||
|
async execute(): Promise<void> {
|
||||||
|
const access: SshAccess = await this.getAccess<SshAccess>(this.accessId);
|
||||||
|
|
||||||
|
const client = new SshClient(this.logger);
|
||||||
|
|
||||||
|
//复制证书
|
||||||
|
const list = await this.doGetCertList()
|
||||||
|
|
||||||
|
for (const target of this.certList) {
|
||||||
|
this.logger.info(`----------- 准备部署:${target}`);
|
||||||
|
let found = false
|
||||||
|
for (const item of list) {
|
||||||
|
if (item.sum === target) {
|
||||||
|
this.logger.info(`----------- 找到证书,开始部署:${item.sum},${item.domain}`)
|
||||||
|
const certPath = item.certificate;
|
||||||
|
const keyPath = item.privateKey;
|
||||||
|
const cmd = `
|
||||||
|
sudo tee ${certPath} > /dev/null <<'EOF'
|
||||||
|
${this.cert.crt}
|
||||||
|
EOF
|
||||||
|
sudo tee ${keyPath} > /dev/null <<'EOF'
|
||||||
|
${this.cert.key}
|
||||||
|
EOF
|
||||||
|
`
|
||||||
|
const res = await client.exec({
|
||||||
|
connectConf: access,
|
||||||
|
script: cmd
|
||||||
|
});
|
||||||
|
if (res.indexOf("Permission denied") > -1){
|
||||||
|
this.logger.error("权限不足,请先配置 sudo 免密")
|
||||||
|
}
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
throw new Error(`没有找到证书:${target},请修改任务重新选择证书id`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.logger.info("证书已上传,准备重启...");
|
||||||
|
|
||||||
|
|
||||||
|
const restartCmd= `
|
||||||
|
echo "正在重启相关服务..."
|
||||||
|
systemctl restart webdav.service
|
||||||
|
systemctl restart smbftpd.service
|
||||||
|
systemctl restart trim_nginx.service
|
||||||
|
echo "服务重启完成!"
|
||||||
|
`
|
||||||
|
await client.exec({
|
||||||
|
connectConf: access,
|
||||||
|
script: restartCmd
|
||||||
|
});
|
||||||
|
|
||||||
|
this.logger.info("部署完成");
|
||||||
|
}
|
||||||
|
|
||||||
|
async doGetCertList(){
|
||||||
|
const access: SshAccess = await this.getAccess<SshAccess>(this.accessId);
|
||||||
|
const client = new SshClient(this.logger);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* :/usr/trim/etc$ cat network_cert_all.conf | jq .
|
||||||
|
*/
|
||||||
|
const sslListCmd = "cat /usr/trim/etc/network_cert_all.conf | jq ."
|
||||||
|
|
||||||
|
const res:string = await client.exec({
|
||||||
|
connectConf: access,
|
||||||
|
script: sslListCmd
|
||||||
|
});
|
||||||
|
let list = []
|
||||||
|
try{
|
||||||
|
list = JSON.parse(res.trim())
|
||||||
|
}catch (e){
|
||||||
|
throw new Error(`证书列表解析失败:${res}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!list || list.length === 0) {
|
||||||
|
throw new Error("没有找到证书,请先在证书管理也没上传一次证书");
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
async onGetCertList() {
|
||||||
|
|
||||||
|
const list = await this.doGetCertList()
|
||||||
|
|
||||||
|
const options = list.map((item: any) => {
|
||||||
|
return {
|
||||||
|
label: `${item.domain}<${item.used?'已使用':"未使用"}-${item.sum}>`,
|
||||||
|
value: item.sum,
|
||||||
|
domain: item.san
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return this.ctx.utils.options.buildGroupOptions(options, this.certDomains);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new FnOSDeployToNAS();
|
||||||
139
packages/ui/certd-server/src/plugins/plugin-github/access.ts
Normal file
139
packages/ui/certd-server/src/plugins/plugin-github/access.ts
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import { AccessInput, BaseAccess, IsAccess } from "@certd/pipeline";
|
||||||
|
import { HttpRequestConfig } from "@certd/basic";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
@IsAccess({
|
||||||
|
name: "github",
|
||||||
|
title: "Github授权",
|
||||||
|
desc: "",
|
||||||
|
icon: "ion:logo-github"
|
||||||
|
})
|
||||||
|
export class GithubAccess extends BaseAccess {
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "接口地址",
|
||||||
|
component: {
|
||||||
|
placeholder: "可以使用反向代理地址",
|
||||||
|
component: {
|
||||||
|
name: "a-input",
|
||||||
|
vModel: "value"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
helper:"默认值:https://api.github.com",
|
||||||
|
encrypt: false,
|
||||||
|
required: false
|
||||||
|
})
|
||||||
|
endpoint!: string;
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "GithubToken",
|
||||||
|
component: {
|
||||||
|
placeholder: "GithubToken",
|
||||||
|
component: {
|
||||||
|
name: "a-input",
|
||||||
|
vModel: "value"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
helper:"支持匿名访问的接口可以不填",
|
||||||
|
encrypt: true,
|
||||||
|
required: false
|
||||||
|
})
|
||||||
|
githubToken!: string;
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "HttpProxy",
|
||||||
|
component: {
|
||||||
|
placeholder: "http://192.168.x.x:10811",
|
||||||
|
component: {
|
||||||
|
name: "a-input",
|
||||||
|
vModel: "value"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
encrypt: false,
|
||||||
|
required: false
|
||||||
|
})
|
||||||
|
httpProxy!: string;
|
||||||
|
|
||||||
|
|
||||||
|
@AccessInput({
|
||||||
|
title: "测试",
|
||||||
|
component: {
|
||||||
|
name: "api-test",
|
||||||
|
action: "TestRequest"
|
||||||
|
},
|
||||||
|
helper: "点击测试接口是否正常"
|
||||||
|
})
|
||||||
|
testRequest = true;
|
||||||
|
|
||||||
|
async onTestRequest() {
|
||||||
|
await this.getRelease({repoName:"certd/certd"})
|
||||||
|
return "ok"
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRelease(req:{repoName:string}){
|
||||||
|
const url = `/repos/${req.repoName}/releases/latest`;
|
||||||
|
return await this.doRequest({
|
||||||
|
url,
|
||||||
|
method: "GET",
|
||||||
|
data:{}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async doRequest(req:HttpRequestConfig){
|
||||||
|
/**
|
||||||
|
* async function getLatestRelease() {
|
||||||
|
* const { REPO_OWNER, REPO_NAME, API_URL, TOKEN } = CONFIG.GITHUB;
|
||||||
|
* const url = `${API_URL}/${REPO_OWNER}/${REPO_NAME}/releases/latest`;
|
||||||
|
*
|
||||||
|
* try {
|
||||||
|
* const response = await axios.get(url, {
|
||||||
|
* headers: TOKEN ? { Authorization: `token ${TOKEN}` } : {}
|
||||||
|
* });
|
||||||
|
*
|
||||||
|
* return {
|
||||||
|
* tag_name: response.data.tag_name,
|
||||||
|
* name: response.data.name || '无标题',
|
||||||
|
* body: response.data.body || '无描述内容',
|
||||||
|
* html_url: response.data.html_url,
|
||||||
|
* published_at: new Date(response.data.published_at).toLocaleString(),
|
||||||
|
* assets: response.data.assets.map(a => ({
|
||||||
|
* name: a.name,
|
||||||
|
* download_url: a.browser_download_url
|
||||||
|
* }))
|
||||||
|
* };
|
||||||
|
* } catch (error) {
|
||||||
|
* if (error.response?.status === 404) {
|
||||||
|
* return { success: false, error: '仓库未找到或没有Release' };
|
||||||
|
* }
|
||||||
|
* return { success: false, error: `请求失败: ${error.message}` };
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
const headers:any = {}
|
||||||
|
if(this.githubToken){
|
||||||
|
headers.Authorization = `token ${this.githubToken}`
|
||||||
|
}
|
||||||
|
const baseURL= this.endpoint || "https://api.github.com";
|
||||||
|
const res = await this.ctx.http.request({
|
||||||
|
url: req.url,
|
||||||
|
baseURL,
|
||||||
|
method: req.method || "POST",
|
||||||
|
data: req.data,
|
||||||
|
headers,
|
||||||
|
httpProxy: this.httpProxy||undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
throw new Error(res.message || res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
new GithubAccess();
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
export * from "./plugins/index.js";
|
||||||
|
export * from "./access.js";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./plugin-check-release.js";
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
|
||||||
|
import { GithubAccess } from "../access.js";
|
||||||
|
|
||||||
|
@IsTaskPlugin({
|
||||||
|
//命名规范,插件类型+功能(就是目录plugin-demo中的demo),大写字母开头,驼峰命名
|
||||||
|
name: "GithubCheckRelease",
|
||||||
|
title: "Github-检查Release版本",
|
||||||
|
desc:"检查最新Release版本并推送消息",
|
||||||
|
icon: "ion:logo-github",
|
||||||
|
//插件分组
|
||||||
|
group: pluginGroups.other.key,
|
||||||
|
needPlus: false,
|
||||||
|
default: {
|
||||||
|
//默认值配置照抄即可
|
||||||
|
strategy: {
|
||||||
|
runStrategy: RunStrategy.AlwaysRun
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
//类名规范,跟上面插件名称(name)一致
|
||||||
|
export class GithubCheckRelease extends AbstractTaskPlugin {
|
||||||
|
//授权选择框
|
||||||
|
@TaskInput({
|
||||||
|
title: "Github授权",
|
||||||
|
component: {
|
||||||
|
name: "access-selector",
|
||||||
|
type: "github" //固定授权类型
|
||||||
|
},
|
||||||
|
required: true //必填
|
||||||
|
})
|
||||||
|
accessId!: string;
|
||||||
|
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "仓库名称",
|
||||||
|
helper:"owner/name,比如 certd/certd",
|
||||||
|
required:true,
|
||||||
|
})
|
||||||
|
repoName!: string;
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: "通知渠道",
|
||||||
|
component:{
|
||||||
|
name:"notification-selector",
|
||||||
|
select:{
|
||||||
|
mode:"tags"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
required:true,
|
||||||
|
})
|
||||||
|
notificationIds!: number[];
|
||||||
|
|
||||||
|
@TaskOutput({
|
||||||
|
title: "最后版本",
|
||||||
|
})
|
||||||
|
lastVersion?: string;
|
||||||
|
|
||||||
|
|
||||||
|
//插件实例化时执行的方法
|
||||||
|
async onInstance() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//插件执行方法
|
||||||
|
async execute(): Promise<string> {
|
||||||
|
const access = await this.getAccess<GithubAccess>(this.accessId);
|
||||||
|
const res = await access.getRelease({repoName:this.repoName})
|
||||||
|
if(res == null){
|
||||||
|
throw new Error(`获取${this.repoName}最新版本失败`)
|
||||||
|
}
|
||||||
|
const lastVersion = this.ctx.lastStatus?.status?.output?.lastVersion;
|
||||||
|
|
||||||
|
if(res.tag_name == null || res.tag_name ==lastVersion){
|
||||||
|
this.logger.info(`暂无更新,${res.tag_name}`);
|
||||||
|
return "skip"
|
||||||
|
}
|
||||||
|
//有更新
|
||||||
|
this.logger.info(`有更新,${lastVersion??"0"}->${res.tag_name}`)
|
||||||
|
this.lastVersion = res.tag_name;
|
||||||
|
|
||||||
|
const body = res.body.replaceAll("* ","- ")
|
||||||
|
//发送通知
|
||||||
|
for (const notificationId of this.notificationIds) {
|
||||||
|
await this.ctx.notificationService.send({
|
||||||
|
id: notificationId,
|
||||||
|
useDefault: false,
|
||||||
|
useEmail:false,
|
||||||
|
logger: this.logger,
|
||||||
|
body: {
|
||||||
|
title: `${this.repoName} 新版本 ${this.lastVersion} 发布`,
|
||||||
|
content: `${body}\n\n > [Certd](https://certd.docmirror.cn),不止证书自动化,插件解锁无限可能!\n\n`,
|
||||||
|
url: `https://github.com/${this.repoName}/releases/tag/${this.lastVersion}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
new GithubCheckRelease();
|
||||||
@@ -31,6 +31,14 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin {
|
|||||||
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
|
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
|
||||||
certDomains!: string[];
|
certDomains!: string[];
|
||||||
|
|
||||||
|
@TaskInput({
|
||||||
|
title: '企业项目ID',
|
||||||
|
helper: '华为云子账号必填,"all"表示查询所有项目',
|
||||||
|
required: false,
|
||||||
|
value:"all"
|
||||||
|
})
|
||||||
|
enterpriseProjectId!: string;
|
||||||
|
|
||||||
@TaskInput({
|
@TaskInput({
|
||||||
title: 'Access授权',
|
title: 'Access授权',
|
||||||
helper: '华为云授权AccessKeyId、AccessKeySecret',
|
helper: '华为云授权AccessKeyId、AccessKeySecret',
|
||||||
@@ -52,6 +60,8 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin {
|
|||||||
)
|
)
|
||||||
domains!: string[];
|
domains!: string[];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async execute(): Promise<void> {
|
async execute(): Promise<void> {
|
||||||
if (!this.cert) {
|
if (!this.cert) {
|
||||||
throw new Error('域名证书不能为空');
|
throw new Error('域名证书不能为空');
|
||||||
@@ -84,6 +94,9 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin {
|
|||||||
this.logger.info('部署到域名:', domain);
|
this.logger.info('部署到域名:', domain);
|
||||||
|
|
||||||
const queryReq = new cdn.ShowDomainDetailByNameRequest(domain);
|
const queryReq = new cdn.ShowDomainDetailByNameRequest(domain);
|
||||||
|
if(this.enterpriseProjectId){
|
||||||
|
queryReq.withEnterpriseProjectId(this.enterpriseProjectId)
|
||||||
|
}
|
||||||
const domainDetail = await client.showDomainDetailByName(queryReq);
|
const domainDetail = await client.showDomainDetailByName(queryReq);
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const status = domainDetail.domain.domainStatus || domainDetail.domain.domain_status
|
const status = domainDetail.domain.domainStatus || domainDetail.domain.domain_status
|
||||||
@@ -94,6 +107,9 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin {
|
|||||||
}
|
}
|
||||||
try{
|
try{
|
||||||
const req = new cdn.UpdateDomainFullConfigRequest().withDomainName(domain).withBody(body);
|
const req = new cdn.UpdateDomainFullConfigRequest().withDomainName(domain).withBody(body);
|
||||||
|
if(this.enterpriseProjectId){
|
||||||
|
req.withEnterpriseProjectId(this.enterpriseProjectId)
|
||||||
|
}
|
||||||
await client.updateDomainFullConfig(req);
|
await client.updateDomainFullConfig(req);
|
||||||
this.logger.info(`部署到域名${domain}完成:`);
|
this.logger.info(`部署到域名${domain}完成:`);
|
||||||
}catch (e) {
|
}catch (e) {
|
||||||
@@ -129,6 +145,7 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin {
|
|||||||
const request = new cdn.ListDomainsRequest();
|
const request = new cdn.ListDomainsRequest();
|
||||||
request.pageNumber = 1;
|
request.pageNumber = 1;
|
||||||
request.pageSize = 1000;
|
request.pageSize = 1000;
|
||||||
|
request.enterpriseProjectId = this.enterpriseProjectId || undefined;
|
||||||
const result: any = await client.listDomains(request);
|
const result: any = await client.listDomains(request);
|
||||||
if (!result || !result.domains || result.domains.length === 0) {
|
if (!result || !result.domains || result.domains.length === 0) {
|
||||||
throw new Error('未找到CDN域名,您可以手动输入');
|
throw new Error('未找到CDN域名,您可以手动输入');
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export class NamesiloDnsProvider extends AbstractDnsProvider<NamesiloRecord> {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.reply?.code !== '300') {
|
if (res.reply?.code !== '300' && res.reply?.code !== 300 && res.reply?.detail!=="success") {
|
||||||
throw new Error(`${JSON.stringify(res.reply.detail)}`);
|
throw new Error(`${JSON.stringify(res.reply.detail)}`);
|
||||||
}
|
}
|
||||||
return res.reply;
|
return res.reply;
|
||||||
@@ -93,7 +93,7 @@ export class NamesiloDnsProvider extends AbstractDnsProvider<NamesiloRecord> {
|
|||||||
const recordId = record.record_id;
|
const recordId = record.record_id;
|
||||||
await this.doRequest('/api/dnsDeleteRecord', {
|
await this.doRequest('/api/dnsDeleteRecord', {
|
||||||
domain: options.recordReq.domain,
|
domain: options.recordReq.domain,
|
||||||
record_id: recordId,
|
rrid: recordId,
|
||||||
});
|
});
|
||||||
this.logger.info(`删除域名解析成功:fullRecord=${fullRecord},value=${value}`);
|
this.logger.info(`删除域名解析成功:fullRecord=${fullRecord},value=${value}`);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ export class EmailNotification extends BaseNotification {
|
|||||||
@NotificationInput({
|
@NotificationInput({
|
||||||
title: '收件人邮箱',
|
title: '收件人邮箱',
|
||||||
component: {
|
component: {
|
||||||
name: 'a-select',
|
name: 'email-selector',
|
||||||
vModel: 'value',
|
vModel: 'value',
|
||||||
mode: 'tags',
|
mode: 'tags',
|
||||||
open: false,
|
// open: false,
|
||||||
},
|
},
|
||||||
required: true,
|
required: true,
|
||||||
helper: '可以填写多个,填写一个按回车键再填写下一个\n需要先[配置邮件服务器](#/sys/settings/email)',
|
helper: '可以填写多个,填写一个按回车键再填写下一个\n需要先[配置邮件服务器](#/sys/settings/email)',
|
||||||
|
|||||||
@@ -55,14 +55,14 @@ export class QywxNotification extends BaseNotification {
|
|||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
|
const color = body.errorMessage?'red':'green';
|
||||||
await this.http.request({
|
await this.http.request({
|
||||||
url: this.webhook,
|
url: this.webhook,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {
|
data: {
|
||||||
msgtype: 'text',
|
msgtype: 'markdown',
|
||||||
text: {
|
markdown: {
|
||||||
content: `${body.title}\n${body.content}\n查看详情: ${body.url}`,
|
content: `<font color='${color}'>${body.title}</font>\n\n\n${body.content}\n\n[查看详情](${body.url})`,
|
||||||
mentioned_list: this.mentionedList,
|
mentioned_list: this.mentionedList,
|
||||||
mentioned_mobile_list: this.mentionedMobileList,
|
mentioned_mobile_list: this.mentionedMobileList,
|
||||||
},
|
},
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user