mirror of
https://github.com/certd/certd.git
synced 2026-04-05 07:20:56 +08:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc42ade63f | ||
|
|
310ef2be7e | ||
|
|
04f0245a95 | ||
|
|
a5ea74d035 | ||
|
|
99678c1635 | ||
|
|
77f163144f | ||
|
|
5dde5bd3f7 | ||
|
|
64c4933645 | ||
|
|
7eebfb35ce | ||
|
|
1c6028abcf | ||
|
|
28bb4856be | ||
|
|
41ad0aea2b | ||
|
|
02fe704769 | ||
|
|
e85b441f60 | ||
|
|
3f83e7463c | ||
|
|
4220ec11e7 | ||
|
|
7879e2d12d | ||
|
|
d16730f0cf | ||
|
|
35f01fc0c8 | ||
|
|
36b26ae9f5 | ||
|
|
3ec6adfc0d | ||
|
|
96717d3dbc |
2
.github/workflows/build-image.yml
vendored
2
.github/workflows/build-image.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: build-image
|
||||
on:
|
||||
push:
|
||||
branches: ['v2']
|
||||
branches: ['v2-dev']
|
||||
paths:
|
||||
- "build.trigger"
|
||||
|
||||
|
||||
19
CHANGELOG.md
19
CHANGELOG.md
@@ -3,6 +3,25 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复无法设置角色的bug ([02fe704](https://github.com/certd/certd/commit/02fe704769edb25fea5ffd85a51a5530864b37b3))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 角色删除安全 ([28bb485](https://github.com/certd/certd/commit/28bb4856bee03569153f6471527c9b9f28cb3d14))
|
||||
* 密钥备份 ([1c6028a](https://github.com/certd/certd/commit/1c6028abcf8849163462bb2f8441b6838357e09b))
|
||||
* 证书直接查看 ([5dde5bd](https://github.com/certd/certd/commit/5dde5bd3f76db3959d411619d29bfb8064e3b307))
|
||||
* sqlite数据库备份插件 ([77f1631](https://github.com/certd/certd/commit/77f163144f7dcfb0431475c55508fecfd6d969f8))
|
||||
|
||||
## [1.26.7](https://github.com/certd/certd/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复siteInfo每次都要重新设置的bug ([36b26ae](https://github.com/certd/certd/commit/36b26ae9f5c7a53c1c2546fb79b2ea451b854abf))
|
||||
|
||||
## [1.26.6](https://github.com/certd/certd/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -14,6 +14,7 @@ Certd 是一个免费全自动申请和自动部署更新SSL证书的工具。
|
||||
* 支持通配符域名/泛域名,支持多个域名打到一个证书上
|
||||
* 邮件通知
|
||||
* 私有化部署,保障数据安全
|
||||
* 支持sqlite,postgresql数据库
|
||||
* 免费、免费、免费([阿里云单个通配符域名证书最便宜也要1800/年](https://yundun.console.aliyun.com/?p=cas#/certExtend/buy/cn-hangzhou))
|
||||
|
||||
|
||||
@@ -121,6 +122,7 @@ http://your_server_ip:7001
|
||||
```shell
|
||||
# 克隆代码
|
||||
git clone https://github.com/certd/certd
|
||||
git checkout v1.26.7 # 这里换成最新版本号
|
||||
cd certd
|
||||
# 启动服务
|
||||
./start.sh
|
||||
|
||||
@@ -11,6 +11,10 @@ git clone https://github.com/certd/certd
|
||||
|
||||
#进入项目目录
|
||||
cd certd
|
||||
|
||||
# 切换到最新版本代码
|
||||
git checkout v1.26.7 # 这里换成最新版本号
|
||||
|
||||
```
|
||||
|
||||
### 修改pnpm-workspace.yaml文件
|
||||
|
||||
@@ -9,5 +9,5 @@
|
||||
}
|
||||
},
|
||||
"npmClient": "pnpm",
|
||||
"version": "1.26.6"
|
||||
"version": "1.26.8"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/publishlab/node-acme-client/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.26.7](https://github.com/publishlab/node-acme-client/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.26.6](https://github.com/publishlab/node-acme-client/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "Simple and unopinionated ACME client",
|
||||
"private": false,
|
||||
"author": "nmorsman",
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.8",
|
||||
"main": "src/index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"license": "MIT",
|
||||
@@ -59,5 +59,5 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||
},
|
||||
"gitHead": "6ea5f04bae6143cab3db41304049d0327355b42f"
|
||||
"gitHead": "4220ec11e750704d61bae33b1614bae87375cf34"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
## [1.26.7](https://github.com/certd/certd/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
## [1.26.6](https://github.com/certd/certd/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
@@ -1 +1 @@
|
||||
14:00
|
||||
01:30
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/basic",
|
||||
"private": false,
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.8",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -64,5 +64,5 @@
|
||||
"vite": "^4.3.8",
|
||||
"vue-tsc": "^1.6.5"
|
||||
},
|
||||
"gitHead": "6ea5f04bae6143cab3db41304049d0327355b42f"
|
||||
"gitHead": "4220ec11e750704d61bae33b1614bae87375cf34"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import fs from "fs";
|
||||
import fs from 'fs';
|
||||
function getFileRootDir(rootDir?: string) {
|
||||
if (rootDir == null) {
|
||||
const userHome = process.env.HOME || process.env.USERPROFILE;
|
||||
rootDir = userHome + "/.certd/storage/";
|
||||
rootDir = userHome + '/.certd/storage/';
|
||||
}
|
||||
|
||||
if (!fs.existsSync(rootDir)) {
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 密钥备份 ([1c6028a](https://github.com/certd/certd/commit/1c6028abcf8849163462bb2f8441b6838357e09b))
|
||||
* 证书直接查看 ([5dde5bd](https://github.com/certd/certd/commit/5dde5bd3f76db3959d411619d29bfb8064e3b307))
|
||||
|
||||
## [1.26.7](https://github.com/certd/certd/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.26.6](https://github.com/certd/certd/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/pipeline",
|
||||
"private": false,
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.8",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -15,8 +15,8 @@
|
||||
"test": "mocha --loader=ts-node/esm"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.26.6",
|
||||
"@certd/plus-core": "^1.26.6",
|
||||
"@certd/basic": "^1.26.8",
|
||||
"@certd/plus-core": "^1.26.8",
|
||||
"axios": "^1.7.2",
|
||||
"dayjs": "^1.11.7",
|
||||
"fix-path": "^4.0.0",
|
||||
@@ -66,5 +66,5 @@
|
||||
"vite": "^4.3.8",
|
||||
"vue-tsc": "^1.6.5"
|
||||
},
|
||||
"gitHead": "6ea5f04bae6143cab3db41304049d0327355b42f"
|
||||
"gitHead": "4220ec11e750704d61bae33b1614bae87375cf34"
|
||||
}
|
||||
|
||||
@@ -307,10 +307,18 @@ export class Executor {
|
||||
//更新pipeline vars
|
||||
if (Object.keys(instance._result.pipelineVars).length > 0) {
|
||||
// 判断 pipelineVars 有值时更新
|
||||
const vars = this.pipelineContext.getObj("vars");
|
||||
let vars = await this.pipelineContext.getObj("vars");
|
||||
vars = vars || {};
|
||||
merge(vars, instance._result.pipelineVars);
|
||||
await this.pipelineContext.setObj("vars", vars);
|
||||
}
|
||||
if (Object.keys(instance._result.pipelinePrivateVars).length > 0) {
|
||||
// 判断 pipelineVars 有值时更新
|
||||
let vars = await this.pipelineContext.getObj("privateVars");
|
||||
vars = vars || {};
|
||||
merge(vars, instance._result.pipelinePrivateVars);
|
||||
await this.pipelineContext.setObj("privateVars", vars);
|
||||
}
|
||||
}
|
||||
|
||||
async notification(when: NotificationWhen, error?: any) {
|
||||
|
||||
@@ -38,7 +38,7 @@ export class RunHistory {
|
||||
start(runnable: Runnable): HistoryResult {
|
||||
const now = new Date().getTime();
|
||||
this.logs[runnable.id] = [];
|
||||
this._loggers[runnable.id] = buildLogger((text) => {
|
||||
this._loggers[runnable.id] = buildLogger((text: string) => {
|
||||
this.logs[runnable.id].push(text);
|
||||
});
|
||||
const status: HistoryResult = {
|
||||
|
||||
@@ -60,6 +60,7 @@ export type TaskResult = {
|
||||
clearLastStatus?: boolean;
|
||||
files?: FileItem[];
|
||||
pipelineVars: Record<string, any>;
|
||||
pipelinePrivateVars?: Record<string, any>;
|
||||
};
|
||||
export type TaskInstanceContext = {
|
||||
//流水线定义
|
||||
@@ -97,7 +98,7 @@ export type TaskInstanceContext = {
|
||||
};
|
||||
|
||||
export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
||||
_result: TaskResult = { clearLastStatus: false, files: [], pipelineVars: {} };
|
||||
_result: TaskResult = { clearLastStatus: false, files: [], pipelineVars: {}, pipelinePrivateVars: {} };
|
||||
ctx!: TaskInstanceContext;
|
||||
logger!: ILogger;
|
||||
accessService!: IAccessService;
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.26.7](https://github.com/certd/certd/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.26.6](https://github.com/certd/certd/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-huawei",
|
||||
"private": false,
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.8",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
"types": "./dist/d/index.d.ts",
|
||||
@@ -17,5 +17,5 @@
|
||||
"rimraf": "^5.0.5",
|
||||
"rollup": "^3.7.4"
|
||||
},
|
||||
"gitHead": "6ea5f04bae6143cab3db41304049d0327355b42f"
|
||||
"gitHead": "4220ec11e750704d61bae33b1614bae87375cf34"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.26.7](https://github.com/certd/certd/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.26.6](https://github.com/certd/certd/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-iframe",
|
||||
"private": false,
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.8",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -39,5 +39,5 @@
|
||||
"tslib": "^2.5.2",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "6ea5f04bae6143cab3db41304049d0327355b42f"
|
||||
"gitHead": "4220ec11e750704d61bae33b1614bae87375cf34"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-jdcloud
|
||||
|
||||
## [1.26.7](https://github.com/certd/certd/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-jdcloud
|
||||
|
||||
## [1.26.6](https://github.com/certd/certd/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-jdcloud
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-jdcloud",
|
||||
"private": false,
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.8",
|
||||
"main": "./dist/bundle.mjs",
|
||||
"module": "./dist/bundle.mjs",
|
||||
"types": "./dist/d/index.d.ts",
|
||||
@@ -27,5 +27,5 @@
|
||||
"rimraf": "^5.0.5",
|
||||
"rollup": "^3.7.4"
|
||||
},
|
||||
"gitHead": "6ea5f04bae6143cab3db41304049d0327355b42f"
|
||||
"gitHead": "4220ec11e750704d61bae33b1614bae87375cf34"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.26.7](https://github.com/certd/certd/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.26.6](https://github.com/certd/certd/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-k8s",
|
||||
"private": false,
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.8",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -18,7 +18,7 @@
|
||||
"@kubernetes/client-node": "0.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/pipeline": "^1.26.6",
|
||||
"@certd/pipeline": "^1.26.8",
|
||||
"@rollup/plugin-commonjs": "^23.0.4",
|
||||
"@rollup/plugin-json": "^6.0.0",
|
||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||
@@ -40,5 +40,5 @@
|
||||
"tslib": "^2.5.2",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "6ea5f04bae6143cab3db41304049d0327355b42f"
|
||||
"gitHead": "4220ec11e750704d61bae33b1614bae87375cf34"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复无法设置角色的bug ([02fe704](https://github.com/certd/certd/commit/02fe704769edb25fea5ffd85a51a5530864b37b3))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 密钥备份 ([1c6028a](https://github.com/certd/certd/commit/1c6028abcf8849163462bb2f8441b6838357e09b))
|
||||
|
||||
## [1.26.7](https://github.com/certd/certd/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复siteInfo每次都要重新设置的bug ([36b26ae](https://github.com/certd/certd/commit/36b26ae9f5c7a53c1c2546fb79b2ea451b854abf))
|
||||
|
||||
## [1.26.6](https://github.com/certd/certd/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/lib-server",
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.8",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -26,8 +26,8 @@
|
||||
],
|
||||
"license": "AGPL",
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.26.6",
|
||||
"@certd/pipeline": "^1.26.6",
|
||||
"@certd/basic": "^1.26.8",
|
||||
"@certd/pipeline": "^1.26.8",
|
||||
"@midwayjs/cache": "~3.14.0",
|
||||
"@midwayjs/core": "~3.17.1",
|
||||
"@midwayjs/i18n": "~3.17.3",
|
||||
@@ -68,5 +68,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "6ea5f04bae6143cab3db41304049d0327355b42f"
|
||||
"gitHead": "4220ec11e750704d61bae33b1614bae87375cf34"
|
||||
}
|
||||
|
||||
@@ -59,26 +59,42 @@ export abstract class BaseService<T> {
|
||||
return await this.getRepository().find(options);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param where
|
||||
*/
|
||||
async deleteWhere(where: any) {
|
||||
await this.getRepository().delete({
|
||||
...where,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3
|
||||
* @param where
|
||||
*/
|
||||
async delete(ids: any, where?: any) {
|
||||
async delete(ids: string | any[], where?: any) {
|
||||
const idArr = this.resolveIdArr(ids);
|
||||
if (idArr.length === 0) {
|
||||
return;
|
||||
}
|
||||
await this.getRepository().delete({
|
||||
id: In(idArr),
|
||||
...where,
|
||||
});
|
||||
await this.modifyAfter(idArr);
|
||||
}
|
||||
|
||||
resolveIdArr(ids: string | any[]) {
|
||||
if (!ids) {
|
||||
throw new ValidateException('ids不能为空');
|
||||
}
|
||||
if (typeof ids === 'string') {
|
||||
ids = ids.split(',');
|
||||
return ids.split(',');
|
||||
} else {
|
||||
return ids;
|
||||
}
|
||||
if (ids.length === 0) {
|
||||
return;
|
||||
}
|
||||
await this.getRepository().delete({
|
||||
id: In(ids),
|
||||
...where,
|
||||
});
|
||||
await this.modifyAfter(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
export class BaseSettings {
|
||||
static __key__: string;
|
||||
static __title__: string;
|
||||
@@ -29,8 +31,10 @@ export class SysPrivateSettings extends BaseSettings {
|
||||
httpProxy? = '';
|
||||
|
||||
removeSecret() {
|
||||
delete this.jwtKey;
|
||||
delete this.encryptSecret;
|
||||
const clone = cloneDeep(this);
|
||||
delete clone.jwtKey;
|
||||
delete clone.encryptSecret;
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +87,14 @@ export class SysSiteInfo extends BaseSettings {
|
||||
loginLogo?: string;
|
||||
}
|
||||
|
||||
export class SysSecretBackup extends BaseSettings {
|
||||
static __title__ = '密钥信息备份';
|
||||
static __key__ = 'sys.secret.backup';
|
||||
static __access__ = 'private';
|
||||
siteId?: string;
|
||||
encryptSecret?: string;
|
||||
}
|
||||
|
||||
export class SysSiteEnv {
|
||||
agent?: {
|
||||
enabled?: boolean;
|
||||
|
||||
@@ -3,11 +3,10 @@ import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { SysSettingsEntity } from '../entity/sys-settings.js';
|
||||
import { CacheManager } from '@midwayjs/cache';
|
||||
import { BaseSettings, SysPrivateSettings, SysPublicSettings } from './models.js';
|
||||
import { BaseSettings, SysInstallInfo, SysPrivateSettings, SysPublicSettings, SysSecretBackup } from './models.js';
|
||||
import * as _ from 'lodash-es';
|
||||
import { BaseService } from '../../../basic/index.js';
|
||||
import { isComm } from '@certd/pipeline';
|
||||
import { setGlobalProxy } from '@certd/basic';
|
||||
import { logger, setGlobalProxy } from '@certd/basic';
|
||||
|
||||
/**
|
||||
* 设置
|
||||
@@ -41,9 +40,6 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
|
||||
if (!key) {
|
||||
return null;
|
||||
}
|
||||
if (key === 'sys.site' && isComm()) {
|
||||
return null;
|
||||
}
|
||||
return await this.repository.findOne({
|
||||
where: {
|
||||
key,
|
||||
@@ -150,4 +146,21 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
|
||||
}
|
||||
await this.cache.del(`settings.${key}`);
|
||||
}
|
||||
|
||||
async backupSecret() {
|
||||
const settings = await this.getSettingByKey(SysSecretBackup.__key__);
|
||||
if (settings == null) {
|
||||
const backup = new SysSecretBackup();
|
||||
const privateSettings = await this.getPrivateSettings();
|
||||
const installInfo = await this.getSetting<SysInstallInfo>(SysInstallInfo);
|
||||
if (installInfo.siteId == null || privateSettings.encryptSecret == null) {
|
||||
logger.error('备份密钥失败,siteId或encryptSecret为空');
|
||||
return;
|
||||
}
|
||||
backup.siteId = installInfo.siteId;
|
||||
backup.encryptSecret = privateSettings.encryptSecret;
|
||||
await this.saveSetting(backup);
|
||||
logger.info('备份密钥成功');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.26.7](https://github.com/certd/certd/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.26.6](https://github.com/certd/certd/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/midway-flyway-js",
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.8",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -56,5 +56,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "6ea5f04bae6143cab3db41304049d0327355b42f"
|
||||
"gitHead": "4220ec11e750704d61bae33b1614bae87375cf34"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 密钥备份 ([1c6028a](https://github.com/certd/certd/commit/1c6028abcf8849163462bb2f8441b6838357e09b))
|
||||
* 证书直接查看 ([5dde5bd](https://github.com/certd/certd/commit/5dde5bd3f76db3959d411619d29bfb8064e3b307))
|
||||
* sqlite数据库备份插件 ([77f1631](https://github.com/certd/certd/commit/77f163144f7dcfb0431475c55508fecfd6d969f8))
|
||||
|
||||
## [1.26.7](https://github.com/certd/certd/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.26.6](https://github.com/certd/certd/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-cert",
|
||||
"private": false,
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.8",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -15,9 +15,9 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.26.6",
|
||||
"@certd/basic": "^1.26.6",
|
||||
"@certd/pipeline": "^1.26.6",
|
||||
"@certd/acme-client": "^1.26.8",
|
||||
"@certd/basic": "^1.26.8",
|
||||
"@certd/pipeline": "^1.26.8",
|
||||
"@google-cloud/publicca": "^1.3.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"jszip": "^3.10.1",
|
||||
@@ -57,5 +57,5 @@
|
||||
"vite": "^3.1.0",
|
||||
"vue-tsc": "^0.38.9"
|
||||
},
|
||||
"gitHead": "6ea5f04bae6143cab3db41304049d0327355b42f"
|
||||
"gitHead": "4220ec11e750704d61bae33b1614bae87375cf34"
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ export class EabAccess extends BaseAccess {
|
||||
@AccessInput({
|
||||
title: "KID",
|
||||
component: {
|
||||
placeholder: "kid",
|
||||
placeholder: "kid / keyId",
|
||||
},
|
||||
helper: "EAB KID",
|
||||
helper: "EAB KID, google的叫 keyId",
|
||||
required: true,
|
||||
encrypt: true,
|
||||
})
|
||||
@@ -19,9 +19,9 @@ export class EabAccess extends BaseAccess {
|
||||
@AccessInput({
|
||||
title: "HMACKey",
|
||||
component: {
|
||||
placeholder: "HMAC Key",
|
||||
placeholder: "HMAC Key / b64MacKey",
|
||||
},
|
||||
helper: "EAB HMAC Key",
|
||||
helper: "EAB HMAC Key ,google的叫b64MacKey",
|
||||
required: true,
|
||||
encrypt: true,
|
||||
})
|
||||
@@ -32,7 +32,8 @@ export class EabAccess extends BaseAccess {
|
||||
component: {
|
||||
placeholder: "绑定一个邮箱",
|
||||
},
|
||||
helper: "Google EAB 申请证书绑定邮箱后,不能更换,否则会导致EAB失效",
|
||||
rules: { type: "email", message: "请输入正确的邮箱" },
|
||||
helper: "Google的EAB申请证书,更换邮箱会导致EAB失效,可以在此处绑定一个邮箱避免此问题",
|
||||
required: false,
|
||||
})
|
||||
email = "";
|
||||
|
||||
@@ -144,6 +144,10 @@ export abstract class CertApplyBasePlugin extends AbstractTaskPlugin {
|
||||
this.cert = cert;
|
||||
|
||||
this._result.pipelineVars.certExpiresTime = dayjs(certReader.detail.notAfter).valueOf();
|
||||
if (!this._result.pipelinePrivateVars) {
|
||||
this._result.pipelinePrivateVars = {};
|
||||
}
|
||||
this._result.pipelinePrivateVars.cert = cert;
|
||||
|
||||
if (cert.pfx == null || cert.der == null) {
|
||||
try {
|
||||
|
||||
@@ -146,6 +146,13 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||
})
|
||||
googleCommonEabAccessId!: number;
|
||||
|
||||
@TaskInput({
|
||||
title: "ZeroSSL公共EAB授权",
|
||||
isSys: true,
|
||||
show: false,
|
||||
})
|
||||
zerosslCommonEabAccessId!: number;
|
||||
|
||||
@TaskInput({
|
||||
title: "EAB授权",
|
||||
component: {
|
||||
@@ -159,7 +166,7 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||
mergeScript: `
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return form.sslProvider === 'zerossl' || (form.sslProvider === 'google' && !form.googleCommonEabAccessId)
|
||||
return (form.sslProvider === 'zerossl' && !form.zerosslCommonEabAccessId) || (form.sslProvider === 'google' && !form.googleCommonEabAccessId)
|
||||
})
|
||||
}
|
||||
`,
|
||||
@@ -266,7 +273,11 @@ export class CertApplyPlugin extends CertApplyBasePlugin {
|
||||
}
|
||||
} else if (this.sslProvider === "zerossl") {
|
||||
if (this.eabAccessId) {
|
||||
this.logger.info("当前正在使用 zerossl EAB授权");
|
||||
eab = await this.ctx.accessService.getById(this.eabAccessId);
|
||||
} else if (this.zerosslCommonEabAccessId) {
|
||||
this.logger.info("当前正在使用 zerossl 公共EAB授权");
|
||||
eab = await this.ctx.accessService.getById(this.zerosslCommonEabAccessId);
|
||||
} else {
|
||||
this.logger.error("zerossl需要配置EAB授权");
|
||||
return;
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 密钥备份 ([1c6028a](https://github.com/certd/certd/commit/1c6028abcf8849163462bb2f8441b6838357e09b))
|
||||
* 证书直接查看 ([5dde5bd](https://github.com/certd/certd/commit/5dde5bd3f76db3959d411619d29bfb8064e3b307))
|
||||
* sqlite数据库备份插件 ([77f1631](https://github.com/certd/certd/commit/77f163144f7dcfb0431475c55508fecfd6d969f8))
|
||||
|
||||
## [1.26.7](https://github.com/certd/certd/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/ui-client
|
||||
|
||||
## [1.26.6](https://github.com/certd/certd/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/ui-client
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-client",
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.8",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --open",
|
||||
@@ -61,8 +61,8 @@
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/lib-iframe": "^1.26.6",
|
||||
"@certd/pipeline": "^1.26.6",
|
||||
"@certd/lib-iframe": "^1.26.8",
|
||||
"@certd/pipeline": "^1.26.8",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/chai": "^4.3.12",
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<template>
|
||||
<a-config-provider :locale="locale" :theme="settingStore.themeToken">
|
||||
<contextHolder />
|
||||
<fs-form-provider>
|
||||
<router-view v-if="routerEnabled" />
|
||||
</fs-form-provider>
|
||||
</a-config-provider>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" setup>
|
||||
import zhCN from "ant-design-vue/es/locale/zh_CN";
|
||||
import enUS from "ant-design-vue/es/locale/en_US";
|
||||
import { nextTick, provide, ref } from "vue";
|
||||
@@ -16,44 +17,39 @@ import { useSettingStore } from "/@/store/modules/settings";
|
||||
import "dayjs/locale/zh-cn";
|
||||
import "dayjs/locale/en";
|
||||
import dayjs from "dayjs";
|
||||
import { Modal } from "ant-design-vue";
|
||||
|
||||
export default {
|
||||
name: "App",
|
||||
setup() {
|
||||
//刷新页面方法
|
||||
const routerEnabled = ref(true);
|
||||
const locale = ref(zhCN);
|
||||
async function reload() {
|
||||
// routerEnabled.value = false;
|
||||
// await nextTick();
|
||||
// routerEnabled.value = true;
|
||||
}
|
||||
function localeChanged(value: any) {
|
||||
console.log("locale changed:", value);
|
||||
if (value === "zh-cn") {
|
||||
locale.value = zhCN;
|
||||
dayjs.locale("zh-cn");
|
||||
} else if (value === "en") {
|
||||
locale.value = enUS;
|
||||
dayjs.locale("en");
|
||||
}
|
||||
}
|
||||
localeChanged("zh-cn");
|
||||
provide("fn:router.reload", reload);
|
||||
provide("fn:locale.changed", localeChanged);
|
||||
|
||||
//其他初始化
|
||||
const resourceStore = useResourceStore();
|
||||
resourceStore.init();
|
||||
const pageStore = usePageStore();
|
||||
pageStore.init();
|
||||
const settingStore = useSettingStore();
|
||||
|
||||
return {
|
||||
routerEnabled,
|
||||
locale,
|
||||
settingStore
|
||||
};
|
||||
defineOptions({
|
||||
name: "App"
|
||||
});
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
provide("modal", modal);
|
||||
//刷新页面方法
|
||||
const routerEnabled = ref(true);
|
||||
const locale = ref(zhCN);
|
||||
async function reload() {
|
||||
// routerEnabled.value = false;
|
||||
// await nextTick();
|
||||
// routerEnabled.value = true;
|
||||
}
|
||||
function localeChanged(value: any) {
|
||||
console.log("locale changed:", value);
|
||||
if (value === "zh-cn") {
|
||||
locale.value = zhCN;
|
||||
dayjs.locale("zh-cn");
|
||||
} else if (value === "en") {
|
||||
locale.value = enUS;
|
||||
dayjs.locale("en");
|
||||
}
|
||||
};
|
||||
}
|
||||
localeChanged("zh-cn");
|
||||
provide("fn:router.reload", reload);
|
||||
provide("fn:locale.changed", localeChanged);
|
||||
|
||||
//其他初始化
|
||||
const resourceStore = useResourceStore();
|
||||
resourceStore.init();
|
||||
const pageStore = usePageStore();
|
||||
pageStore.init();
|
||||
const settingStore = useSettingStore();
|
||||
</script>
|
||||
|
||||
@@ -18,7 +18,7 @@ export interface UserInfoRes {
|
||||
id: string | number;
|
||||
username: string;
|
||||
nickName: string;
|
||||
roles: number[];
|
||||
roleIds: number[];
|
||||
}
|
||||
|
||||
export interface LoginRes {
|
||||
|
||||
@@ -43,7 +43,6 @@ async function loginWithOTPCode(otpCode: string) {
|
||||
const [modal, contextHolder] = Modal.useModal();
|
||||
async function getDeviceId() {
|
||||
//打开对话框
|
||||
|
||||
modal.confirm({
|
||||
title: "请输入OTP验证码",
|
||||
maskClosable: true,
|
||||
|
||||
@@ -37,7 +37,7 @@ export const useUserStore = defineStore({
|
||||
return this.token || LocalStorage.get(TOKEN_KEY);
|
||||
},
|
||||
isAdmin(): boolean {
|
||||
return this.getUserInfo.id === 1 || this.getUserInfo.roles?.includes(1);
|
||||
return this.getUserInfo.roleIds?.includes(1) || this.getUserInfo.id === 1;
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
||||
@@ -213,6 +213,7 @@ h1, h2, h3, h4, h5, h6 {
|
||||
|
||||
|
||||
.fs-copyable {
|
||||
display: inline-flex;
|
||||
.text {
|
||||
flex: 1
|
||||
}
|
||||
|
||||
7
packages/ui/certd-client/src/use/use-modal.ts
Normal file
7
packages/ui/certd-client/src/use/use-modal.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { inject } from "vue";
|
||||
import type { ModalStaticFunctions } from "ant-design-vue/es/modal/confirm";
|
||||
import { ModalFuncWithRef } from "ant-design-vue/es/modal/useModal";
|
||||
|
||||
export function useModal(): ModalStaticFunctions<ModalFuncWithRef> {
|
||||
return inject("modal");
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { request } from "/src/api/service";
|
||||
|
||||
const apiPrefix = "/pi/pipeline";
|
||||
const historyApiPrefix = "/pi/history";
|
||||
const certApiPrefix = "/pi/cert";
|
||||
|
||||
export async function GetList(query: any) {
|
||||
return await request({
|
||||
@@ -82,3 +83,19 @@ export async function GetFiles(pipelineId: number) {
|
||||
params: { pipelineId }
|
||||
});
|
||||
}
|
||||
|
||||
export type CertInfo = {
|
||||
crt: string;
|
||||
key: string;
|
||||
ic: string;
|
||||
der: string;
|
||||
pfx: string;
|
||||
};
|
||||
|
||||
export async function GetCert(pipelineId: number): Promise<CertInfo> {
|
||||
return await request({
|
||||
url: certApiPrefix + "/get",
|
||||
method: "post",
|
||||
params: { id: pipelineId }
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="cert-view">
|
||||
<a-list item-layout="vertical" :data-source="certFiles">
|
||||
<template #renderItem="{ item }">
|
||||
<a-list-item key="item.key">
|
||||
<a-list-item-meta>
|
||||
<template #title>
|
||||
<div class="title">
|
||||
<div>{{ item.name }}({{ item.fileName }})</div>
|
||||
<fs-copyable :model-value="item.content" :button="{ show: false }">
|
||||
<a-tag type="success">复制</a-tag>
|
||||
</fs-copyable>
|
||||
</div>
|
||||
</template>
|
||||
</a-list-item-meta>
|
||||
<div>
|
||||
<a-textarea :value="item.content" rows="5" />
|
||||
</div>
|
||||
</a-list-item>
|
||||
</template>
|
||||
</a-list>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { CertInfo } from "/@/views/certd/pipeline/api";
|
||||
import { ref } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
cert: CertInfo;
|
||||
}>();
|
||||
|
||||
const certFiles = ref([
|
||||
{ name: "证书", fileName: "fullchain.pem", key: "crt", content: props.cert.crt },
|
||||
{ name: "私钥", fileName: "private.pem", key: "key", content: props.cert.key },
|
||||
{ name: "中间证书", fileName: "chain.pem", key: "ic", content: props.cert.ic }
|
||||
]);
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.cert-view {
|
||||
.title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.ant-list-item-meta {
|
||||
margin-block-end: 0px !important;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -11,7 +11,9 @@ import { useUserStore } from "/@/store/modules/user";
|
||||
import dayjs from "dayjs";
|
||||
import { useSettingStore } from "/@/store/modules/settings";
|
||||
import _ from "lodash-es";
|
||||
|
||||
import { useModal } from "/@/use/use-modal";
|
||||
import CertView from "./cert-view.vue";
|
||||
import { eachRunnable, eachStages } from "./utils";
|
||||
export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const router = useRouter();
|
||||
const { t } = useI18n();
|
||||
@@ -148,6 +150,51 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
router.push({ path: "/certd/pipeline/detail", query: { id, editMode: "true" } });
|
||||
});
|
||||
}
|
||||
|
||||
const model = useModal();
|
||||
const viewCert = async (row: any) => {
|
||||
const cert = await api.GetCert(row.id);
|
||||
if (!cert) {
|
||||
notification.error({ message: "还没有产生证书,请先运行流水线" });
|
||||
return;
|
||||
}
|
||||
|
||||
model.success({
|
||||
title: "查看证书",
|
||||
maskClosable: true,
|
||||
okText: "关闭",
|
||||
width: 800,
|
||||
content: () => {
|
||||
return <CertView cert={cert}></CertView>;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const downloadCert = async (row: any) => {
|
||||
const files = await api.GetFiles(row.id);
|
||||
model.success({
|
||||
title: "文件下载",
|
||||
maskClosable: true,
|
||||
okText: "↑↑↑ 点击上面链接下载",
|
||||
content: () => {
|
||||
const children = [];
|
||||
for (const file of files) {
|
||||
const downloadUrl = `${env.API}/pi/history/download?pipelineId=${row.id}&fileId=${file.id}`;
|
||||
children.push(
|
||||
<div>
|
||||
<div>
|
||||
<a href={downloadUrl} target={"_blank"}>
|
||||
{file.filename}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <div class={"mt-3"}>{children}</div>;
|
||||
}
|
||||
});
|
||||
};
|
||||
const userStore = useUserStore();
|
||||
const settingStore = useSettingStore();
|
||||
return {
|
||||
@@ -190,7 +237,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
buttons: {
|
||||
play: {
|
||||
order: -999,
|
||||
title: null,
|
||||
title: "运行流水线",
|
||||
type: "link",
|
||||
icon: "ant-design:play-circle-outlined",
|
||||
click({ row }) {
|
||||
@@ -205,6 +252,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
}
|
||||
},
|
||||
view: {
|
||||
show: false,
|
||||
click({ row }) {
|
||||
router.push({ path: "/certd/pipeline/detail", query: { id: row.id, editMode: "false" } });
|
||||
}
|
||||
@@ -226,7 +274,7 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
},
|
||||
config: {
|
||||
order: 1,
|
||||
title: null,
|
||||
title: "修改流水线内容",
|
||||
type: "link",
|
||||
icon: "ant-design:edit-outlined",
|
||||
click({ row }) {
|
||||
@@ -235,35 +283,25 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
},
|
||||
edit: {
|
||||
order: 2,
|
||||
title: "修改流水线运行配置",
|
||||
icon: "ant-design:setting-outlined"
|
||||
},
|
||||
download: {
|
||||
viewCert: {
|
||||
order: 3,
|
||||
title: null,
|
||||
title: "查看证书",
|
||||
type: "link",
|
||||
icon: "ph:certificate",
|
||||
async click({ row }) {
|
||||
viewCert(row);
|
||||
}
|
||||
},
|
||||
download: {
|
||||
order: 4,
|
||||
type: "link",
|
||||
title: "下载证书",
|
||||
icon: "ant-design:download-outlined",
|
||||
async click({ row }) {
|
||||
const files = await api.GetFiles(row.id);
|
||||
Modal.success({
|
||||
title: "文件下载",
|
||||
maskClosable: true,
|
||||
okText: "↑↑↑ 点击上面链接下载",
|
||||
content: () => {
|
||||
const children = [];
|
||||
for (const file of files) {
|
||||
const downloadUrl = `${env.API}/pi/history/download?pipelineId=${row.id}&fileId=${file.id}`;
|
||||
children.push(
|
||||
<p>
|
||||
<a href={downloadUrl} target={"_blank"}>
|
||||
{file.filename}
|
||||
</a>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
return <div class={"mt-3"}>{children}</div>;
|
||||
}
|
||||
});
|
||||
downloadCert(row);
|
||||
}
|
||||
},
|
||||
remove: {
|
||||
@@ -330,32 +368,53 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
}
|
||||
},
|
||||
content: {
|
||||
title: "定时任务数",
|
||||
type: "number",
|
||||
title: "流水线内容",
|
||||
form: { show: false },
|
||||
column: {
|
||||
align: "center",
|
||||
width: 100,
|
||||
cellRender({ value }) {
|
||||
if (value && value.triggers) {
|
||||
return value.triggers?.length > 0 ? value.triggers.length : "-";
|
||||
}
|
||||
return "-";
|
||||
}
|
||||
show: false
|
||||
},
|
||||
valueBuilder({ row }) {
|
||||
if (row.content) {
|
||||
row.content = JSON.parse(row.content);
|
||||
const pipeline = row.content;
|
||||
let stepCount = 0;
|
||||
eachStages(pipeline.stages, (item, runnableType) => {
|
||||
if (runnableType === "step") {
|
||||
stepCount++;
|
||||
}
|
||||
});
|
||||
row._stepCount = stepCount;
|
||||
if (pipeline.triggers) {
|
||||
row._triggerCount = pipeline.triggers?.length > 0 ? pipeline.triggers.length : "-";
|
||||
}
|
||||
}
|
||||
},
|
||||
valueResolve({ row }) {
|
||||
if (row.content) {
|
||||
row.content = JSON.stringify(row.content);
|
||||
}
|
||||
}
|
||||
},
|
||||
_triggerCount: {
|
||||
title: "定时任务数",
|
||||
type: "number",
|
||||
column: {
|
||||
align: "center",
|
||||
width: 100
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
_stepCount: {
|
||||
title: "部署任务数",
|
||||
type: "number",
|
||||
form: { show: false },
|
||||
column: {
|
||||
align: "center",
|
||||
width: 100
|
||||
}
|
||||
},
|
||||
lastVars: {
|
||||
title: "到期剩余",
|
||||
type: "number",
|
||||
@@ -375,18 +434,6 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
width: 150
|
||||
}
|
||||
},
|
||||
lastHistoryTime: {
|
||||
title: "最后运行",
|
||||
type: "datetime",
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
column: {
|
||||
sorter: true,
|
||||
width: 150,
|
||||
align: "center"
|
||||
}
|
||||
},
|
||||
status: {
|
||||
title: "状态",
|
||||
type: "dict-select",
|
||||
@@ -402,7 +449,18 @@ export default function ({ crudExpose, context: { certdFormRef } }: CreateCrudOp
|
||||
align: "center"
|
||||
}
|
||||
},
|
||||
|
||||
lastHistoryTime: {
|
||||
title: "最后运行",
|
||||
type: "datetime",
|
||||
form: {
|
||||
show: false
|
||||
},
|
||||
column: {
|
||||
sorter: true,
|
||||
width: 150,
|
||||
align: "center"
|
||||
}
|
||||
},
|
||||
disabled: {
|
||||
title: "启用",
|
||||
type: "dict-switch",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<a-modal v-model:open="taskModal.open" class="pi-task-view" title="任务日志" style="width: 80%" v-bind="taskModal">
|
||||
<a-tabs v-model:activeKey="activeKey" tab-position="left" animated>
|
||||
<a-tabs v-model:active-key="activeKey" tab-position="left" animated>
|
||||
<a-tab-pane v-for="item of detail.nodes" :key="item.node.id">
|
||||
<template #tab>
|
||||
<div class="tab-title" :title="item.node.title">
|
||||
@@ -8,7 +8,7 @@
|
||||
<pi-status-show :status="item.node.status?.result" type="icon"></pi-status-show>
|
||||
</div>
|
||||
</template>
|
||||
<pre class="pi-task-view-logs" style="overflow: auto;"><template v-for="(text, index) of item.logs" :key="index">{{ text }}</template></pre>
|
||||
<pre class="pi-task-view-logs" style="overflow: auto"><template v-for="(text, index) of item.logs" :key="index">{{ text }}</template></pre>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</a-modal>
|
||||
@@ -56,8 +56,8 @@ export default {
|
||||
for (let node of nodes) {
|
||||
if (currentHistory?.value?.logs != null) {
|
||||
node.logs = computed(() => {
|
||||
if(currentHistory?.value?.logs && currentHistory.value?.logs[node.node.id]!= null){
|
||||
return currentHistory.value?.logs[node.node.id];
|
||||
if (currentHistory?.value?.logs && currentHistory.value?.logs[node.node.id] != null) {
|
||||
return currentHistory.value?.logs[node.node.id];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
@@ -94,10 +94,11 @@ export default {
|
||||
display: flex;
|
||||
.tab-title-text {
|
||||
display: inline-block;
|
||||
width: 150px;
|
||||
width: 180px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
15
packages/ui/certd-client/src/views/certd/pipeline/utils.ts
Normal file
15
packages/ui/certd-client/src/views/certd/pipeline/utils.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { forEach } from "lodash-es";
|
||||
|
||||
export function eachStages(list: any[], exec: (item: any, runnableType: string) => void, runnableType: string = "stage") {
|
||||
if (!list || list.length <= 0) {
|
||||
return;
|
||||
}
|
||||
forEach(list, (item) => {
|
||||
exec(item, runnableType);
|
||||
if (runnableType === "stage") {
|
||||
eachStages(item.tasks, exec, "task");
|
||||
} else if (runnableType === "task") {
|
||||
eachStages(item.steps, exec, "step");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -67,6 +67,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
}
|
||||
},
|
||||
rowHandle: {
|
||||
show: false,
|
||||
minWidth: 200,
|
||||
fixed: "right",
|
||||
buttons: {
|
||||
@@ -81,18 +82,21 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
}
|
||||
}
|
||||
},
|
||||
table: {
|
||||
rowKey: "name"
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 100
|
||||
},
|
||||
form: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
// id: {
|
||||
// title: "ID",
|
||||
// key: "id",
|
||||
// type: "number",
|
||||
// column: {
|
||||
// width: 100
|
||||
// },
|
||||
// form: {
|
||||
// show: false
|
||||
// }
|
||||
// },
|
||||
name: {
|
||||
title: "插件名称",
|
||||
type: "text",
|
||||
@@ -147,7 +151,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
}
|
||||
},
|
||||
disabled: {
|
||||
title: "禁用/启用",
|
||||
title: "点击禁用/启用",
|
||||
type: "dict-switch",
|
||||
dict: dict({
|
||||
data: [
|
||||
@@ -159,7 +163,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
value: false
|
||||
},
|
||||
column: {
|
||||
width: 100,
|
||||
width: 120,
|
||||
align: "center",
|
||||
component: {
|
||||
title: "点击可禁用/启用",
|
||||
|
||||
@@ -3,6 +3,23 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.8](https://github.com/certd/certd/compare/v1.26.7...v1.26.8) (2024-10-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复无法设置角色的bug ([02fe704](https://github.com/certd/certd/commit/02fe704769edb25fea5ffd85a51a5530864b37b3))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 角色删除安全 ([28bb485](https://github.com/certd/certd/commit/28bb4856bee03569153f6471527c9b9f28cb3d14))
|
||||
* 密钥备份 ([1c6028a](https://github.com/certd/certd/commit/1c6028abcf8849163462bb2f8441b6838357e09b))
|
||||
* 证书直接查看 ([5dde5bd](https://github.com/certd/certd/commit/5dde5bd3f76db3959d411619d29bfb8064e3b307))
|
||||
* sqlite数据库备份插件 ([77f1631](https://github.com/certd/certd/commit/77f163144f7dcfb0431475c55508fecfd6d969f8))
|
||||
|
||||
## [1.26.7](https://github.com/certd/certd/compare/v1.26.6...v1.26.7) (2024-10-14)
|
||||
|
||||
**Note:** Version bump only for package @certd/ui-server
|
||||
|
||||
## [1.26.6](https://github.com/certd/certd/compare/v1.26.5...v1.26.6) (2024-10-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-server",
|
||||
"version": "1.26.6",
|
||||
"version": "1.26.8",
|
||||
"description": "fast-server base midway",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
@@ -27,17 +27,17 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@certd/acme-client": "^1.26.6",
|
||||
"@certd/commercial-core": "^1.26.6",
|
||||
"@certd/lib-huawei": "^1.26.6",
|
||||
"@certd/lib-jdcloud": "^1.26.6",
|
||||
"@certd/lib-k8s": "^1.26.6",
|
||||
"@certd/lib-server": "^1.26.6",
|
||||
"@certd/midway-flyway-js": "^1.26.6",
|
||||
"@certd/pipeline": "^1.26.6",
|
||||
"@certd/plugin-cert": "^1.26.6",
|
||||
"@certd/plugin-plus": "^1.26.6",
|
||||
"@certd/plus-core": "^1.26.6",
|
||||
"@certd/acme-client": "^1.26.8",
|
||||
"@certd/commercial-core": "^1.26.8",
|
||||
"@certd/lib-huawei": "^1.26.8",
|
||||
"@certd/lib-jdcloud": "^1.26.8",
|
||||
"@certd/lib-k8s": "^1.26.8",
|
||||
"@certd/lib-server": "^1.26.8",
|
||||
"@certd/midway-flyway-js": "^1.26.8",
|
||||
"@certd/pipeline": "^1.26.8",
|
||||
"@certd/plugin-cert": "^1.26.8",
|
||||
"@certd/plugin-plus": "^1.26.8",
|
||||
"@certd/plus-core": "^1.26.8",
|
||||
"@koa/cors": "^5.0.0",
|
||||
"@midwayjs/bootstrap": "~3.17.1",
|
||||
"@midwayjs/cache": "~3.14.0",
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Controller, Inject, Post, Provide, Query } from '@midwayjs/core';
|
||||
import { PipelineService } from '../../modules/pipeline/service/pipeline-service.js';
|
||||
import { BaseController, Constants } from '@certd/lib-server';
|
||||
import { StorageService } from '../../modules/pipeline/service/storage-service.js';
|
||||
|
||||
@Provide()
|
||||
@Controller('/api/pi/cert')
|
||||
export class CertController extends BaseController {
|
||||
@Inject()
|
||||
pipelineService: PipelineService;
|
||||
@Inject()
|
||||
storeService: StorageService;
|
||||
|
||||
@Post('/get', { summary: Constants.per.authOnly })
|
||||
async getCert(@Query('id') id: number) {
|
||||
const userId = this.getUserId();
|
||||
await this.pipelineService.checkUserId(id, userId);
|
||||
const privateVars = await this.storeService.getPipelinePrivateVars(id);
|
||||
return this.ok(privateVars.cert);
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,10 @@ export class UserController extends CrudController<UserService> {
|
||||
id: number
|
||||
) {
|
||||
if (id === 1) {
|
||||
throw new Error('不能删除默认的管理员用户');
|
||||
throw new Error('不能删除默认的管理员角色');
|
||||
}
|
||||
if (id === 3) {
|
||||
throw new Error('不能删除默认的普通用户角色');
|
||||
}
|
||||
return await super.delete(id);
|
||||
}
|
||||
|
||||
@@ -80,8 +80,8 @@ export class SysSettingsController extends CrudController<SysSettingsService> {
|
||||
@Post('/getSysSettings', { summary: 'sys:settings:edit' })
|
||||
async getSysSettings() {
|
||||
const publicSettings = await this.service.getPublicSettings();
|
||||
const privateSettings = await this.service.getPrivateSettings();
|
||||
privateSettings.removeSecret();
|
||||
let privateSettings = await this.service.getPrivateSettings();
|
||||
privateSettings = privateSettings.removeSecret();
|
||||
return this.ok({ public: publicSettings, private: privateSettings });
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ export class AutoInitSite {
|
||||
await this.sysSettingsService.saveSetting(privateInfo);
|
||||
}
|
||||
|
||||
await this.sysSettingsService.backupSecret();
|
||||
|
||||
await this.sysSettingsService.reloadPrivateSettings();
|
||||
|
||||
// 授权许可
|
||||
|
||||
@@ -237,7 +237,7 @@ export class PipelineService extends BaseService<PipelineEntity> {
|
||||
});
|
||||
}
|
||||
|
||||
async delete(id: number) {
|
||||
async delete(id: any) {
|
||||
await this.clearTriggers(id);
|
||||
//TODO 删除storage
|
||||
// const storage = new DbStorage(pipeline.userId, this.storageService);
|
||||
|
||||
@@ -44,6 +44,9 @@ export class StorageService extends BaseService<StorageEntity> {
|
||||
}
|
||||
|
||||
async findPipelineVars(pipelineIds: number[]) {
|
||||
if (pipelineIds == null || pipelineIds.length === 0) {
|
||||
throw new Error('pipelineIds 不能为空');
|
||||
}
|
||||
return await this.repository.find({
|
||||
where: {
|
||||
scope: 'pipeline',
|
||||
@@ -52,4 +55,22 @@ export class StorageService extends BaseService<StorageEntity> {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getPipelinePrivateVars(pipelineId: number) {
|
||||
if (pipelineId == null) {
|
||||
throw new Error('pipelineId 不能为空');
|
||||
}
|
||||
const res = await this.repository.findOne({
|
||||
where: {
|
||||
scope: 'pipeline',
|
||||
namespace: pipelineId + '',
|
||||
key: 'privateVars',
|
||||
},
|
||||
});
|
||||
if (!res) {
|
||||
return {};
|
||||
}
|
||||
const value = JSON.parse(res.value);
|
||||
return value.value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,8 @@ export class RoleService extends BaseService<RoleEntity> {
|
||||
ttl: 1000 * 60 * 10,
|
||||
});
|
||||
|
||||
//@ts-ignore
|
||||
getRepository() {
|
||||
//@ts-ignore
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ getRepository() {
|
||||
return;
|
||||
}
|
||||
//先删除所有
|
||||
await this.userRoleService.delete({ userId });
|
||||
await this.userRoleService.deleteWhere({ userId });
|
||||
//再添加
|
||||
await this.addRoles(userId, roles);
|
||||
|
||||
@@ -95,7 +95,7 @@ getRepository() {
|
||||
}
|
||||
|
||||
async authz(roleId: any, permissionIds: any) {
|
||||
await this.rolePermissionService.delete({ roleId });
|
||||
await this.rolePermissionService.deleteWhere({ roleId });
|
||||
for (const permissionId of permissionIds) {
|
||||
await this.rolePermissionService.add({
|
||||
roleId,
|
||||
@@ -125,4 +125,12 @@ getRepository() {
|
||||
this.permissionCache.set(roleIdsKey, permissionSet);
|
||||
return permissionSet;
|
||||
}
|
||||
|
||||
async delete(id: any) {
|
||||
const idArr = this.resolveIdArr(id);
|
||||
const urs = await this.userRoleService.find({ where: { roleId: In(idArr) } });
|
||||
if (urs.length > 0) {
|
||||
throw new Error('该角色已被用户使用,无法删除');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +178,8 @@ export class AsyncSsh2Client {
|
||||
end() {
|
||||
if (this.conn) {
|
||||
this.conn.end();
|
||||
this.conn.destroy();
|
||||
this.conn = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,9 +244,8 @@ export class SshClient {
|
||||
mkdirCmd = `if not exist "${filePath}" mkdir "${filePath}"`;
|
||||
}
|
||||
}
|
||||
await conn.shell(mkdirCmd);
|
||||
await conn.exec(mkdirCmd);
|
||||
}
|
||||
|
||||
await conn.fastPut({ sftp, ...transport });
|
||||
}
|
||||
this.logger.info('文件全部上传成功');
|
||||
|
||||
@@ -31,6 +31,7 @@ export class HostShellExecutePlugin extends AbstractTaskPlugin {
|
||||
name: 'a-textarea',
|
||||
vModel: 'value',
|
||||
rows: 6,
|
||||
placeholder: 'systemctl restart nginx',
|
||||
},
|
||||
helper: '注意:如果目标主机是windows,且终端是cmd,系统会自动将多行命令通过“&&”连接成一行',
|
||||
required: true,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './plugin-restart.js';
|
||||
export * from './plugin-script.js';
|
||||
export * from './plugin-wait.js';
|
||||
export * from './plugin-db-backup.js';
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
import { IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from '@certd/pipeline';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import dayjs from 'dayjs';
|
||||
import { SshAccess, SshClient } from '../../plugin-host/index.js';
|
||||
import { AbstractPlusTaskPlugin } from '@certd/plugin-plus';
|
||||
|
||||
const defaultBackupDir = 'certd_backup';
|
||||
const defaultFilePrefix = 'db-backup';
|
||||
@IsTaskPlugin({
|
||||
name: 'DBBackupPlugin',
|
||||
title: '数据库备份',
|
||||
icon: 'ri:rest-time-line',
|
||||
desc: '仅支持备份SQLite数据库',
|
||||
group: pluginGroups.other.key,
|
||||
default: {
|
||||
strategy: {
|
||||
runStrategy: RunStrategy.AlwaysRun,
|
||||
},
|
||||
},
|
||||
needPlus: true,
|
||||
})
|
||||
export class DBBackupPlugin extends AbstractPlusTaskPlugin {
|
||||
@TaskInput({
|
||||
title: '备份方式',
|
||||
value: 'local',
|
||||
component: {
|
||||
name: 'a-select',
|
||||
options: [
|
||||
{ label: '本地复制', value: 'local' },
|
||||
{ label: 'ssh上传', value: 'ssh' },
|
||||
],
|
||||
placeholder: '',
|
||||
},
|
||||
helper: '支持本地复制、ssh上传',
|
||||
required: true,
|
||||
})
|
||||
backupMode = 'local';
|
||||
|
||||
@TaskInput({
|
||||
title: '主机登录授权',
|
||||
component: {
|
||||
name: 'access-selector',
|
||||
type: 'ssh',
|
||||
},
|
||||
mergeScript: `
|
||||
return {
|
||||
show:ctx.compute(({form})=>{
|
||||
return form.backupMode === 'ssh';
|
||||
})
|
||||
}
|
||||
`,
|
||||
required: true,
|
||||
})
|
||||
sshAccessId!: number;
|
||||
|
||||
@TaskInput({
|
||||
title: '备份保存目录',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
type: 'value',
|
||||
placeholder: `默认${defaultBackupDir}`,
|
||||
},
|
||||
helper: `ssh方式默认保存在当前用户的${defaultBackupDir}目录下,本地方式默认保存在data/${defaultBackupDir}目录下,也可以填写绝对路径`,
|
||||
required: false,
|
||||
})
|
||||
backupDir: string = defaultBackupDir;
|
||||
|
||||
@TaskInput({
|
||||
title: '备份文件前缀',
|
||||
component: {
|
||||
name: 'a-input',
|
||||
vModel: 'value',
|
||||
placeholder: `默认${defaultFilePrefix}`,
|
||||
},
|
||||
required: false,
|
||||
})
|
||||
filePrefix: string = defaultFilePrefix;
|
||||
|
||||
@TaskInput({
|
||||
title: '删除过期备份',
|
||||
component: {
|
||||
name: 'a-input-number',
|
||||
vModel: 'value',
|
||||
placeholder: '20',
|
||||
},
|
||||
helper: '删除多少天前的备份,不填则不删除,windows暂不支持',
|
||||
required: false,
|
||||
})
|
||||
retainDays!: number;
|
||||
|
||||
async onInstance() {}
|
||||
async execute(): Promise<void> {
|
||||
this.logger.info('开始备份数据库');
|
||||
|
||||
let dbPath = process.env.certd_typeorm_dataSource_default_database;
|
||||
dbPath = dbPath || './data/db.sqlite';
|
||||
if (!fs.existsSync(dbPath)) {
|
||||
this.logger.error('数据库文件不存在:', dbPath);
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.info('当前备份方式:', this.backupMode);
|
||||
const backupDir = this.backupDir || defaultBackupDir;
|
||||
const backupFile = `${backupDir}/${this.filePrefix}.${dayjs().format('YYYYMMDD.HHmmss')}.sqlite`;
|
||||
|
||||
if (this.backupMode === 'local') {
|
||||
await this.localBackup(dbPath, backupDir, backupFile);
|
||||
} else if (this.backupMode === 'ssh') {
|
||||
await this.sshBackup(dbPath, backupDir, backupFile);
|
||||
} else if (this.backupMode === 'oss') {
|
||||
await this.ossBackup(dbPath, backupDir, backupFile);
|
||||
} else {
|
||||
throw new Error(`不支持的备份方式:${this.backupMode}`);
|
||||
}
|
||||
|
||||
this.logger.info('数据库备份完成');
|
||||
}
|
||||
|
||||
private async localBackup(dbPath: string, backupDir: string, backupPath: string) {
|
||||
if (!backupPath.startsWith('/')) {
|
||||
backupPath = path.join('./data/', backupPath);
|
||||
}
|
||||
const dir = path.dirname(backupPath);
|
||||
if (!fs.existsSync(dir)) {
|
||||
await fs.promises.mkdir(dir, { recursive: true });
|
||||
}
|
||||
backupPath = path.resolve(backupPath);
|
||||
await fs.promises.copyFile(dbPath, backupPath);
|
||||
this.logger.info('备份文件路径:', backupPath);
|
||||
|
||||
if (this.retainDays > 0) {
|
||||
// 删除过期备份
|
||||
this.logger.info('开始删除过期备份文件');
|
||||
const files = fs.readdirSync(dir);
|
||||
const now = Date.now();
|
||||
let count = 0;
|
||||
files.forEach(file => {
|
||||
const filePath = path.join(dir, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
if (now - stat.mtimeMs > this.retainDays * 24 * 60 * 60 * 1000) {
|
||||
fs.unlinkSync(filePath as fs.PathLike);
|
||||
count++;
|
||||
this.logger.info('删除过期备份文件:', filePath);
|
||||
}
|
||||
});
|
||||
this.logger.info('删除过期备份文件数:', count);
|
||||
}
|
||||
}
|
||||
|
||||
private async sshBackup(dbPath: string, backupDir: string, backupPath: string) {
|
||||
const access: SshAccess = await this.ctx.accessService.getById(this.sshAccessId);
|
||||
const sshClient = new SshClient(this.logger);
|
||||
this.logger.info('备份目录:', backupPath);
|
||||
await sshClient.uploadFiles({
|
||||
connectConf: access,
|
||||
transports: [{ localPath: dbPath, remotePath: backupPath }],
|
||||
mkdirs: true,
|
||||
});
|
||||
this.logger.info('备份文件上传完成');
|
||||
|
||||
if (this.retainDays > 0) {
|
||||
// 删除过期备份
|
||||
this.logger.info('开始删除过期备份文件');
|
||||
const isWin = access.windows;
|
||||
let script: string[] = [];
|
||||
if (isWin) {
|
||||
throw new Error('删除过期文件暂不支持windows系统');
|
||||
// script = `forfiles /p ${backupDir} /s /d -${this.retainDays} /c "cmd /c del @path"`;
|
||||
} else {
|
||||
script = [`cd ${backupDir}`, 'echo 备份目录', 'pwd', `find . -type f -mtime +${this.retainDays} -name '${this.filePrefix}*' -exec rm -f {} \\;`];
|
||||
}
|
||||
|
||||
await sshClient.exec({
|
||||
connectConf: access,
|
||||
script,
|
||||
});
|
||||
this.logger.info('删除过期备份文件完成');
|
||||
}
|
||||
}
|
||||
|
||||
private async ossBackup(dbPath: string, backupDir: string, backupPath: string) {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
new DBBackupPlugin();
|
||||
Reference in New Issue
Block a user