Merge branch 'v2-dev' into v2

pull/409/head
xiaojunnuo 2025-04-17 23:38:29 +08:00
commit 4159534a64
65 changed files with 1021 additions and 417 deletions

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
### Performance Improvements
* 登录支持双重认证 ([48aef25](https://github.com/certd/certd/commit/48aef25b3f6499d674ca4e4ef16f4c62399fb735))
* 多重认证登录 ([0f82cf4](https://github.com/certd/certd/commit/0f82cf409bc60706ab07e4ca4f272b9a1ca7eecb))
* 优化部署到华为云CDN支持先上传到ccm再使用证书id部署修复offline状态下导致部署报错的bug ([79df39a](https://github.com/certd/certd/commit/79df39acabab10ae7e1864dadcdc186bb007a3c5))
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
### Bug Fixes ### Bug Fixes

View File

@ -1 +1 @@
23:49 23:37

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
### Performance Improvements
* 登录支持双重认证 ([48aef25](https://github.com/certd/certd/commit/48aef25b3f6499d674ca4e4ef16f4c62399fb735))
* 多重认证登录 ([0f82cf4](https://github.com/certd/certd/commit/0f82cf409bc60706ab07e4ca4f272b9a1ca7eecb))
* 优化部署到华为云CDN支持先上传到ccm再使用证书id部署修复offline状态下导致部署报错的bug ([79df39a](https://github.com/certd/certd/commit/79df39acabab10ae7e1864dadcdc186bb007a3c5))
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
### Bug Fixes ### Bug Fixes

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -22,9 +22,11 @@ Certd 存储了证书以及授权等敏感数据,所以需要严格保障安
* [站点隐藏设置说明](./hidden/) * [站点隐藏设置说明](./hidden/)
![](./images/hidden.png) ![](./images/hidden.png)
## 4、登录二次验证 ## 4、登录双重验证
待实现 支持2FA双重认证
![](./images/2fa.png)
## 5、数据库自动备份【建议开启】 ## 5、数据库自动备份【建议开启】
* [自动备份设置说明](../../use/backup/) * [自动备份设置说明](../../use/backup/)

View File

@ -9,5 +9,5 @@
} }
}, },
"npmClient": "pnpm", "npmClient": "pnpm",
"version": "1.33.4" "version": "1.33.5"
} }

View File

@ -3,6 +3,10 @@
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.33.5](https://github.com/publishlab/node-acme-client/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/acme-client
## [1.33.4](https://github.com/publishlab/node-acme-client/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/publishlab/node-acme-client/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/acme-client **Note:** Version bump only for package @certd/acme-client

View File

@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client", "description": "Simple and unopinionated ACME client",
"private": false, "private": false,
"author": "nmorsman", "author": "nmorsman",
"version": "1.33.4", "version": "1.33.5",
"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.33.4", "@certd/basic": "^1.33.5",
"@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",
@ -67,5 +67,5 @@
"bugs": { "bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues" "url": "https://github.com/publishlab/node-acme-client/issues"
}, },
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f" "gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1"
} }

View File

@ -3,6 +3,12 @@
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.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
### Performance Improvements
* 多重认证登录 ([0f82cf4](https://github.com/certd/certd/commit/0f82cf409bc60706ab07e4ca4f272b9a1ca7eecb))
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/basic **Note:** Version bump only for package @certd/basic

View File

@ -1 +1 @@
23:45 23:32

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/basic", "name": "@certd/basic",
"private": false, "private": false,
"version": "1.33.4", "version": "1.33.5",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@ -44,5 +44,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f" "gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1"
} }

View File

@ -1,4 +1,4 @@
import { customAlphabet } from "nanoid"; import { customAlphabet } from "nanoid";
export const randomNumber = customAlphabet("1234567890", 4); export const randomNumber = customAlphabet("1234567890", 4);
export const simpleNanoId = customAlphabet("1234567890abcdefghijklmopqrstuvwxyz", 12); export const simpleNanoId = customAlphabet("1234567890abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ", 12);

View File

@ -3,6 +3,10 @@
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.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/pipeline
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/pipeline **Note:** Version bump only for package @certd/pipeline

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/pipeline", "name": "@certd/pipeline",
"private": false, "private": false,
"version": "1.33.4", "version": "1.33.5",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@ -16,8 +16,8 @@
"test": "mocha --loader=ts-node/esm" "test": "mocha --loader=ts-node/esm"
}, },
"dependencies": { "dependencies": {
"@certd/basic": "^1.33.4", "@certd/basic": "^1.33.5",
"@certd/plus-core": "^1.33.4", "@certd/plus-core": "^1.33.5",
"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"
@ -43,5 +43,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f" "gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1"
} }

View File

@ -3,6 +3,10 @@
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.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/lib-huawei
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/lib-huawei **Note:** Version bump only for package @certd/lib-huawei

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-huawei", "name": "@certd/lib-huawei",
"private": false, "private": false,
"version": "1.33.4", "version": "1.33.5",
"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",
@ -23,5 +23,5 @@
"prettier": "^2.8.8", "prettier": "^2.8.8",
"tslib": "^2.8.1" "tslib": "^2.8.1"
}, },
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f" "gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1"
} }

View File

@ -3,6 +3,10 @@
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.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/lib-iframe
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/lib-iframe **Note:** Version bump only for package @certd/lib-iframe

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-iframe", "name": "@certd/lib-iframe",
"private": false, "private": false,
"version": "1.33.4", "version": "1.33.5",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@ -30,5 +30,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f" "gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1"
} }

View File

@ -3,6 +3,10 @@
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.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/jdcloud
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/jdcloud **Note:** Version bump only for package @certd/jdcloud

View File

@ -1,6 +1,6 @@
{ {
"name": "@certd/jdcloud", "name": "@certd/jdcloud",
"version": "1.33.4", "version": "1.33.5",
"description": "jdcloud openApi sdk", "description": "jdcloud openApi sdk",
"main": "./dist/bundle.js", "main": "./dist/bundle.js",
"module": "./dist/bundle.js", "module": "./dist/bundle.js",
@ -60,5 +60,5 @@
"fetch" "fetch"
] ]
}, },
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f" "gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1"
} }

View File

@ -3,6 +3,10 @@
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.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/lib-k8s
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/lib-k8s **Note:** Version bump only for package @certd/lib-k8s

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-k8s", "name": "@certd/lib-k8s",
"private": false, "private": false,
"version": "1.33.4", "version": "1.33.5",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@ -16,7 +16,7 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@certd/basic": "^1.33.4", "@certd/basic": "^1.33.5",
"@kubernetes/client-node": "0.21.0" "@kubernetes/client-node": "0.21.0"
}, },
"devDependencies": { "devDependencies": {
@ -31,5 +31,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f" "gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1"
} }

View File

@ -3,6 +3,12 @@
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.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
### Performance Improvements
* 登录支持双重认证 ([48aef25](https://github.com/certd/certd/commit/48aef25b3f6499d674ca4e4ef16f4c62399fb735))
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/lib-server **Note:** Version bump only for package @certd/lib-server

View File

@ -1,6 +1,6 @@
{ {
"name": "@certd/lib-server", "name": "@certd/lib-server",
"version": "1.33.4", "version": "1.33.5",
"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.33.4", "@certd/acme-client": "^1.33.5",
"@certd/basic": "^1.33.4", "@certd/basic": "^1.33.5",
"@certd/pipeline": "^1.33.4", "@certd/pipeline": "^1.33.5",
"@certd/plus-core": "^1.33.4", "@certd/plus-core": "^1.33.5",
"@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": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f" "gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1"
} }

View File

@ -75,6 +75,10 @@ export const Constants = {
code: 10010, code: 10010,
message: '站点已关闭', message: '站点已关闭',
}, },
need2fa:{
code: 10020,
message: '需要2FA认证',
},
openKeyError: { openKeyError: {
code: 20000, code: 20000,
message: 'ApiToken错误', message: 'ApiToken错误',

View File

@ -1,10 +1,19 @@
import { Constants } from '../constants.js'; import { Constants } from '../constants.js';
import { BaseException } from './base-exception.js'; import { BaseException } from './base-exception.js';
import { TextException } from "./common-exception.js";
/** /**
* *
*/ */
export class AuthException extends BaseException { export class AuthException extends BaseException {
constructor(message) { constructor(message?:string) {
super('AuthException', Constants.res.auth.code, message ? message : Constants.res.auth.message); super('AuthException', Constants.res.auth.code, message ? message : Constants.res.auth.message);
} }
} }
export class Need2FAException extends TextException {
constructor(message:string,data:any) {
super('Need2FAException', Constants.res.need2fa.code, message ? message : Constants.res.need2fa.message,data);
}
}

View File

@ -3,9 +3,11 @@
*/ */
export class BaseException extends Error { export class BaseException extends Error {
code: number; code: number;
constructor(name, code, message) { data?:any
constructor(name, code, message,data?:any) {
super(message); super(message);
this.name = name; this.name = name;
this.code = code; this.code = code;
this.data = data;
} }
} }

View File

@ -1,16 +1,23 @@
import { Constants } from '../constants.js'; import { Constants } from "../constants.js";
import { BaseException } from './base-exception.js'; import { BaseException } from "./base-exception.js";
/** /**
* *
*/ */
export class CommonException extends BaseException { export class CommonException extends BaseException {
constructor(message) { constructor(message) {
super('CommonException', Constants.res.error.code, message ? message : Constants.res.error.message); super("CommonException", Constants.res.error.code, message ? message : Constants.res.error.message);
} }
} }
export class CodeException extends BaseException { export class CodeException extends BaseException {
constructor(res: { code: number; message: string }) { constructor(res: { code: number; message: string }) {
super('CodeException', res.code, res.message); super("CodeException", res.code, res.message);
}
}
export class TextException extends BaseException {
constructor(name, code,message, data?) {
super(name, code, message, data);
} }
} }

View File

@ -2,14 +2,15 @@ export class Result<T> {
code: number; code: number;
msg: string; msg: string;
data: T; data: T;
constructor(code, msg, data?) { constructor(code, msg, data?) {
this.code = code; this.code = code;
this.msg = msg; this.msg = msg;
this.data = data; this.data = data;
} }
static error(code = 1, msg) { static error(code = 1, msg, data?: any) {
return new Result(code, msg); return new Result(code, msg, data);
} }
static success(msg, data?) { static success(msg, data?) {

View File

@ -3,6 +3,10 @@
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.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/midway-flyway-js **Note:** Version bump only for package @certd/midway-flyway-js

View File

@ -1,6 +1,6 @@
{ {
"name": "@certd/midway-flyway-js", "name": "@certd/midway-flyway-js",
"version": "1.33.4", "version": "1.33.5",
"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": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f" "gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1"
} }

View File

@ -3,6 +3,10 @@
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.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/plugin-cert
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
**Note:** Version bump only for package @certd/plugin-cert **Note:** Version bump only for package @certd/plugin-cert

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/plugin-cert", "name": "@certd/plugin-cert",
"private": false, "private": false,
"version": "1.33.4", "version": "1.33.5",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@ -15,10 +15,10 @@
"preview": "vite preview" "preview": "vite preview"
}, },
"dependencies": { "dependencies": {
"@certd/acme-client": "^1.33.4", "@certd/acme-client": "^1.33.5",
"@certd/basic": "^1.33.4", "@certd/basic": "^1.33.5",
"@certd/pipeline": "^1.33.4", "@certd/pipeline": "^1.33.5",
"@certd/plugin-lib": "^1.33.4", "@certd/plugin-lib": "^1.33.5",
"@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",
@ -41,5 +41,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f" "gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1"
} }

View File

@ -3,6 +3,10 @@
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.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
**Note:** Version bump only for package @certd/plugin-lib
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
### Performance Improvements ### Performance Improvements

View File

@ -1,7 +1,7 @@
{ {
"name": "@certd/plugin-lib", "name": "@certd/plugin-lib",
"private": false, "private": false,
"version": "1.33.4", "version": "1.33.5",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@ -16,8 +16,8 @@
}, },
"dependencies": { "dependencies": {
"@alicloud/pop-core": "^1.7.10", "@alicloud/pop-core": "^1.7.10",
"@certd/basic": "^1.33.4", "@certd/basic": "^1.33.5",
"@certd/pipeline": "^1.33.4", "@certd/pipeline": "^1.33.5",
"@kubernetes/client-node": "0.21.0", "@kubernetes/client-node": "0.21.0",
"ali-oss": "^6.21.0", "ali-oss": "^6.21.0",
"basic-ftp": "^5.0.5", "basic-ftp": "^5.0.5",
@ -48,5 +48,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "5b3fb7387df65ed67811623ef9a2c5adadc8bf4f" "gitHead": "198a97b00c75219ea8efdc6db4676158506a07c1"
} }

View File

@ -3,6 +3,13 @@
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.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
### Performance Improvements
* 登录支持双重认证 ([48aef25](https://github.com/certd/certd/commit/48aef25b3f6499d674ca4e4ef16f4c62399fb735))
* 多重认证登录 ([0f82cf4](https://github.com/certd/certd/commit/0f82cf409bc60706ab07e4ca4f272b9a1ca7eecb))
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
### Performance Improvements ### Performance Improvements

View File

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

View File

@ -3,6 +3,17 @@ import { get } from "lodash-es";
import { errorLog, errorCreate } from "./tools"; import { errorLog, errorCreate } from "./tools";
import { env } from "/src/utils/util.env"; import { env } from "/src/utils/util.env";
import { useUserStore } from "/@/store/user"; import { useUserStore } from "/@/store/user";
export class CodeError extends Error {
code: number;
data?: any;
constructor(message: string, code: number, data?: any) {
super(message);
this.code = code;
this.data = data;
}
}
/** /**
* @description * @description
*/ */
@ -56,12 +67,13 @@ function createService() {
const errorMessage = dataAxios.msg || dataAxios.message || "未知错误"; const errorMessage = dataAxios.msg || dataAxios.message || "未知错误";
// @ts-ignore // @ts-ignore
if (response?.config?.onError) { if (response?.config?.onError) {
// @ts-ignore const err = new CodeError(errorMessage, dataAxios.code, dataAxios.data);
response.config.onError(new Error(errorMessage)); response.config.onError(err);
return;
} }
//@ts-ignore //@ts-ignore
const showErrorNotify = response?.config?.showErrorNotify; const showErrorNotify = response?.config?.showErrorNotify;
errorCreate(`${errorMessage}: ${response.config.url}`, showErrorNotify); errorCreate(`${errorMessage}: ${response.config.url}`, showErrorNotify, dataAxios);
return dataAxios; return dataAxios;
} }
} }

View File

@ -4,6 +4,7 @@
* @param {String} defaultValue * @param {String} defaultValue
*/ */
import { uiContext } from "@fast-crud/fast-crud"; import { uiContext } from "@fast-crud/fast-crud";
import { CodeError } from "/@/api/service";
export function parse(jsonString = "{}", defaultValue = {}) { export function parse(jsonString = "{}", defaultValue = {}) {
let result = defaultValue; let result = defaultValue;
@ -68,8 +69,8 @@ export function errorLog(error: any, notify = true) {
* @description * @description
* @param {String} msg * @param {String} msg
*/ */
export function errorCreate(msg: string, notify = true) { export function errorCreate(msg: string, notify = true, data?: any) {
const err = new Error(msg); const err = new CodeError(msg, data.code, data.data);
console.error("errorCreate", err); console.error("errorCreate", err);
if (notify) { if (notify) {
uiContext.get().notification.error({ message: err.message }); uiContext.get().notification.error({ message: err.message });

View File

@ -143,6 +143,17 @@ export const certdResources = [
keepAlive: true, keepAlive: true,
}, },
}, },
{
title: "认证安全设置",
name: "UserSecurity",
path: "/certd/mine/security",
component: "/certd/mine/security/index.vue",
meta: {
icon: "fluent:shield-keyhole-16-regular",
auth: true,
isMenu: true,
},
},
{ {
title: "账号信息", title: "账号信息",
name: "UserProfile", name: "UserProfile",

View File

@ -66,3 +66,11 @@ export async function mine(): Promise<UserInfoRes> {
method: "post", method: "post",
}); });
} }
export async function loginByTwoFactor(data: any) {
return await request({
url: "/loginByTwoFactor",
method: "post",
data,
});
}

View File

@ -51,7 +51,7 @@ export const useUserStore = defineStore({
setUserInfo(info: UserInfoRes) { setUserInfo(info: UserInfoRes) {
this.userInfo = info; this.userInfo = info;
const userStore = vbenUserStore(); const userStore = vbenUserStore();
userStore.setUserInfo(info); userStore.setUserInfo(info as any);
LocalStorage.set(USER_INFO_KEY, info); LocalStorage.set(USER_INFO_KEY, info);
}, },
resetState() { resetState() {
@ -71,23 +71,18 @@ export const useUserStore = defineStore({
* @description: login * @description: login
*/ */
async login(loginType: string, params: LoginReq | SmsLoginReq): Promise<any> { async login(loginType: string, params: LoginReq | SmsLoginReq): Promise<any> {
try { let loginRes: any = null;
let loginRes: any = null; if (loginType === "sms") {
if (loginType === "sms") { loginRes = await UserApi.loginBySms(params as SmsLoginReq);
loginRes = await UserApi.loginBySms(params as SmsLoginReq); } else {
} else { loginRes = await UserApi.login(params as LoginReq);
loginRes = await UserApi.login(params as LoginReq);
}
const { token, expire } = loginRes;
// save token
this.setToken(token, expire);
// get user info
return await this.onLoginSuccess(loginRes);
} catch (error) {
console.error(error);
return null;
} }
return await this.onLoginSuccess(loginRes);
},
async loginByTwoFactor(form: any) {
const loginRes = await UserApi.loginByTwoFactor(form);
return await this.onLoginSuccess(loginRes);
}, },
async getUserInfoAction(): Promise<UserInfoRes> { async getUserInfoAction(): Promise<UserInfoRes> {
const userInfo = await UserApi.mine(); const userInfo = await UserApi.mine();
@ -100,9 +95,13 @@ export const useUserStore = defineStore({
}, },
async onLoginSuccess(loginData: any) { async onLoginSuccess(loginData: any) {
const { token, expire } = loginData;
// save token
this.setToken(token, expire);
// get user info
// await this.getUserInfoAction(); // await this.getUserInfoAction();
// const userInfo = await this.getUserInfoAction(); // const userInfo = await this.getUserInfoAction();
mitter.emit("app.login", { token: loginData }); mitter.emit("app.login", { ...loginData });
await router.replace("/"); await router.replace("/");
}, },

View File

@ -105,4 +105,19 @@ span.fs-icon-svg{
svg{ svg{
vertical-align:0 !important; vertical-align:0 !important;
} }
}
.fs-button{
span{
&:first-child{
margin-right: 5px;
}
&:last-child{
margin-left: 5px;
}
}
.fs-icon,.fs-button-icon{
margin: 0 !important;
}
} }

View File

@ -0,0 +1,47 @@
// @ts-ignore
import { request } from "/@/api/service";
const apiPrefix = "/user/settings";
export type UserTwoFactorSetting = {
authenticator: {
enabled: boolean;
verified: boolean;
};
};
export type AuthenticatorSaveReq = {
verifyCode?: string;
};
export async function TwoFactorSettingsGet() {
const res = await request({
url: apiPrefix + "/twoFactor/get",
method: "post",
});
if (!res) {
return {};
}
return res as UserTwoFactorSetting;
}
export async function TwoFactorAuthenticatorGet() {
const res = await request({
url: apiPrefix + "/twoFactor/authenticator/qrcode",
method: "post",
});
return res as string; //base64
}
export async function TwoFactorAuthenticatorSave(req: AuthenticatorSaveReq) {
return await request({
url: apiPrefix + "/twoFactor/authenticator/save",
method: "post",
data: req,
});
}
export async function TwoFactorAuthenticatorOff() {
return await request({
url: apiPrefix + "/twoFactor/authenticator/off",
method: "post",
});
}

View File

@ -0,0 +1,162 @@
<template>
<fs-page class="page-user-settings page-two-factor">
<template #header>
<div class="title">认证安全设置</div>
</template>
<div class="user-settings-form settings-form">
<a-form :model="formState" name="basic" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off">
<a-form-item label="2FA多重验证登录" :name="['authenticator', 'enabled']">
<div class="flex mt-5">
<a-switch v-model:checked="formState.authenticator.enabled" :disabled="!settingsStore.isPlus" @change="onAuthenticatorEnabledChanged" />
<a-button
v-if="formState.authenticator.enabled && formState.authenticator.verified"
:disabled="authenticatorOpenRef || !settingsStore.isPlus"
size="small"
class="ml-5"
type="primary"
@click="authenticatorForm.open = true"
>
重新绑定
</a-button>
<vip-button class="ml-5" mode="button"></vip-button>
</div>
<div class="helper">是否开启多重验证登录</div>
</a-form-item>
<a-form-item v-if="authenticatorOpenRef" label="绑定设备" class="authenticator-config">
<h3 class="font-bold m-5">1. 安装任意一款支持Authenticator的验证APP比如</h3>
<div class="ml-20">
<ul>
<li>
<a-tooltip title="如果报没有找到谷歌服务的错误您可以安装KK谷歌助手">
<a href="https://appgallery.huawei.com/app/C100262999" target="_blank"> Microsoft Authenticator</a>
</a-tooltip>
</li>
<li>
<a href="https://sj.qq.com/appdetail/com.tencent.authenticator" target="_blank">腾讯身份验证器</a>
</li>
<li>
<a href="https://www.synology.cn/zh-cn/dsm/feature/authentication" target="_blank">群晖身份验证器</a>
</li>
<li>
<a-tooltip title="如果报没有找到谷歌服务的错误您可以安装KK谷歌助手">
<a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2" target="_blank">Google Authenticator</a>
</a-tooltip>
</li>
<li>
<a href="https://play.google.com/store/apps/details?id=com.authy.authy" target="_blank">Authy</a>
</li>
</ul>
</div>
<h3 class="font-bold m-10">2. 扫描二维码添加账号</h3>
<div v-if="authenticatorForm.qrcodeSrc" class="qrcode">
<div class="ml-20">
<img class="full-w" :src="authenticatorForm.qrcodeSrc" />
</div>
</div>
<h3 class="font-bold m-10">3. 输入验证码</h3>
<div class="ml-20">
<a-input v-model:value="authenticatorForm.verifyCode" placeholder="请输入验证码" />
</div>
<div class="ml-20 flex mt-10">
<loading-button type="primary" html-type="button" :click="doAuthenticatorSave">确认</loading-button>
<a-button class="ml-1" @click="authenticatorForm.open = false">取消</a-button>
</div>
</a-form-item>
</a-form>
</div>
</fs-page>
</template>
<script setup lang="tsx">
import { computed, reactive, watch } from "vue";
import * as api from "./api";
import { UserTwoFactorSetting } from "./api";
import { Modal, notification } from "ant-design-vue";
import { merge } from "lodash-es";
import { useSettingStore } from "/@/store/settings";
const settingsStore = useSettingStore();
defineOptions({
name: "UserSecurity",
});
const formState = reactive<Partial<UserTwoFactorSetting>>({
authenticator: {
enabled: false,
verified: false,
},
});
const authenticatorForm = reactive({
qrcodeSrc: "",
verifyCode: "",
open: false,
});
const authenticatorOpenRef = computed(() => {
return formState.authenticator.enabled && (authenticatorForm.open || !formState.authenticator.verified);
});
watch(
() => {
return authenticatorOpenRef.value;
},
async open => {
if (open) {
//base64
authenticatorForm.qrcodeSrc = await api.TwoFactorAuthenticatorGet();
} else {
authenticatorForm.qrcodeSrc = "";
authenticatorForm.verifyCode = "";
}
}
);
async function loadUserSettings() {
const data: any = await api.TwoFactorSettingsGet();
merge(formState, data);
}
loadUserSettings();
const doAuthenticatorSave = async (form: any) => {
await api.TwoFactorAuthenticatorSave({
verifyCode: authenticatorForm.verifyCode,
});
notification.success({
message: "保存成功",
});
authenticatorForm.open = false;
};
function onAuthenticatorEnabledChanged(value: any) {
if (!value) {
//
if (formState.authenticator.verified) {
Modal.confirm({
title: "确认",
content: `确定要关闭多重验证登录吗?`,
async onOk() {
await api.TwoFactorAuthenticatorOff();
notification.success({
message: "关闭成功",
});
loadUserSettings();
},
onCancel() {
formState.authenticator.enabled = true;
},
});
}
}
}
</script>
<style lang="less">
.page-user-settings {
.user-settings-form {
width: 600px;
margin: 20px;
}
}
</style>

View File

@ -4,10 +4,14 @@
<a-tab-pane v-for="item of detail.nodes" :key="item.node.id"> <a-tab-pane v-for="item of detail.nodes" :key="item.node.id">
<template #tab> <template #tab>
<div class="tab-title flex-between" :title="item.node.title"> <div class="tab-title flex-between" :title="item.node.title">
<span class="tab-title-text flex items-center md:w-40"> <span class="tab-title-text flex items-center md:w-48">
<pi-status-show class="mr-1" :status="item.node.status?.result" type="icon"></pi-status-show> <pi-status-show class="mr-1" :status="item.node.status?.result" type="icon"></pi-status-show>
<!-- <fs-icon icon="ion:chevron-forward-circle" class="text-md mr-1"></fs-icon>--> <!-- <fs-icon icon="ion:chevron-forward-circle" class="text-md mr-1"></fs-icon>-->
{{ item.node.title }} <span class="flex-1 ellipsis">{{ item.node.title }}</span>
<a-tooltip title="强制重新执行此步骤">
<fs-icon class="pointer color-blue ml-1" style="font-size: 16px" title="强制重新执行此步骤" icon="icon-park-outline:replay-music" @click="triggerRun(item.node.id)"></fs-icon>
</a-tooltip>
</span> </span>
</div> </div>
</template> </template>
@ -31,13 +35,14 @@ export default {
name: "PiTaskView", name: "PiTaskView",
components: { PiStatusShow }, components: { PiStatusShow },
props: {}, props: {},
emits: ["run"],
setup(props: any, ctx: any) { setup(props: any, ctx: any) {
const taskModal = ref({ const taskModal = ref({
open: false, open: false,
onOk() { onOk() {
taskViewClose(); taskViewClose();
}, },
cancelText: "关闭" cancelText: "关闭",
}); });
const { isMobile } = usePreferences(); const { isMobile } = usePreferences();
const tabPosition = computed(() => { const tabPosition = computed(() => {
@ -65,7 +70,7 @@ export default {
node: step, node: step,
type: "步骤", type: "步骤",
tab: 2, tab: 2,
logs: [] logs: [],
}); });
} }
for (let node of nodes) { for (let node of nodes) {
@ -82,7 +87,7 @@ export default {
list.push({ list.push({
time, time,
content, content,
color color,
}); });
} }
return list; return list;
@ -111,12 +116,12 @@ export default {
if (isBottom && el) { if (isBottom && el) {
el?.scrollTo({ el?.scrollTo({
top: el.scrollHeight, top: el.scrollHeight,
behavior: "smooth" behavior: "smooth",
}); });
} }
}, },
{ {
immediate: true immediate: true,
} }
); );
} }
@ -135,15 +140,21 @@ export default {
taskModal.value.open = false; taskModal.value.open = false;
}; };
function triggerRun(id: string) {
ctx.emit("run", id);
taskModal.value.open = false;
}
return { return {
detail, detail,
taskModal, taskModal,
activeKey, activeKey,
taskViewOpen, taskViewOpen,
taskViewClose, taskViewClose,
tabPosition tabPosition,
triggerRun,
}; };
} },
}; };
</script> </script>
@ -154,7 +165,7 @@ export default {
.tab-title-text { .tab-title-text {
display: flex; display: flex;
max-width: 180px; //max-width: 180px;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;

View File

@ -249,7 +249,7 @@
<pi-task-form ref="taskFormRef" :edit-mode="editMode"></pi-task-form> <pi-task-form ref="taskFormRef" :edit-mode="editMode"></pi-task-form>
<pi-trigger-form ref="triggerFormRef" :edit-mode="editMode"></pi-trigger-form> <pi-trigger-form ref="triggerFormRef" :edit-mode="editMode"></pi-trigger-form>
<pi-task-view ref="taskViewRef"></pi-task-view> <pi-task-view ref="taskViewRef" @run="run"></pi-task-view>
<PiNotificationForm ref="notificationFormRef" :edit-mode="editMode"></PiNotificationForm> <PiNotificationForm ref="notificationFormRef" :edit-mode="editMode"></PiNotificationForm>
</fs-page> </fs-page>
</template> </template>

View File

@ -1,26 +0,0 @@
// @ts-ignore
import { request } from "/@/api/service";
const apiPrefix = "/user/settings";
export type UserSettings = {
defaultNotification?: number;
defaultCron?: string;
};
export async function UserSettingsGet() {
const res = await request({
url: apiPrefix + "/getDefault",
method: "post",
});
if (!res) {
return {};
}
return res;
}
export async function UserSettingsSave(setting: any) {
return await request({
url: apiPrefix + "/saveDefault",
method: "post",
data: setting,
});
}

View File

@ -1,74 +0,0 @@
<template>
<fs-page class="page-user-settings">
<template #header>
<div class="title">设置</div>
</template>
<div class="user-settings-form settings-form">
<a-form
:model="formState"
name="basic"
:label-col="{ span: 8 }"
:wrapper-col="{ span: 16 }"
autocomplete="off"
@finish="onFinish"
@finish-failed="onFinishFailed"
>
<a-form-item label="默认定时设置" name="defaultCron">
<notification-selector v-model="formState.defaultCron" />
<div class="helper">创建流水线时默认使用此定时时间</div>
</a-form-item>
<a-form-item :wrapper-col="{ offset: 8, span: 16 }">
<a-button :loading="saveLoading" type="primary" html-type="submit">保存</a-button>
</a-form-item>
</a-form>
</div>
</fs-page>
</template>
<script setup lang="tsx">
import { reactive, ref } from "vue";
import * as api from "./api";
import { UserSettings } from "./api";
import { notification } from "ant-design-vue";
import { merge } from "lodash-es";
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
defineOptions({
name: "UserSettings"
});
const formState = reactive<Partial<UserSettings>>({});
async function loadUserSettings() {
const data: any = await api.UserSettingsGet();
merge(formState, data);
}
const saveLoading = ref(false);
loadUserSettings();
const onFinish = async (form: any) => {
try {
saveLoading.value = true;
await api.UserSettingsSave(form);
notification.success({
message: "保存成功"
});
} finally {
saveLoading.value = false;
}
};
const onFinishFailed = (errorInfo: any) => {
// console.log("Failed:", errorInfo);
};
</script>
<style lang="less">
.page-user-settings {
.user-settings-form {
width: 500px;
margin: 20px;
}
}
</style>

View File

@ -1,14 +1,6 @@
<template> <template>
<div class="main login-page"> <div class="main login-page">
<a-form <a-form v-if="!twoFactor.loginId" ref="formRef" class="user-layout-login" name="custom-validation" :model="formState" v-bind="layout" @finish="handleFinish" @finish-failed="handleFinishFailed">
ref="formRef"
class="user-layout-login"
name="custom-validation"
:model="formState"
v-bind="layout"
@finish="handleFinish"
@finish-failed="handleFinishFailed"
>
<!-- <div class="login-title">登录</div>--> <!-- <div class="login-title">登录</div>-->
<a-tabs v-model:active-key="formState.loginType" :tab-bar-style="{ textAlign: 'center', borderBottom: 'unset' }"> <a-tabs v-model:active-key="formState.loginType" :tab-bar-style="{ textAlign: 'center', borderBottom: 'unset' }">
<a-tab-pane key="password" tab="密码登录" :disabled="sysPublicSettings.passwordLoginEnabled !== true"> <a-tab-pane key="password" tab="密码登录" :disabled="sysPublicSettings.passwordLoginEnabled !== true">
@ -44,13 +36,7 @@
</a-form-item> </a-form-item>
<a-form-item name="smsCode" :rules="rules.smsCode"> <a-form-item name="smsCode" :rules="rules.smsCode">
<sms-code <sms-code v-model:value="formState.smsCode" :img-code="formState.imgCode" :mobile="formState.mobile" :phone-code="formState.phoneCode" :random-str="formState.randomStr" />
v-model:value="formState.smsCode"
:img-code="formState.imgCode"
:mobile="formState.mobile"
:phone-code="formState.phoneCode"
:random-str="formState.randomStr"
/>
</a-form-item> </a-form-item>
</template> </template>
</a-tab-pane> </a-tab-pane>
@ -63,6 +49,23 @@
<router-link v-if="hasRegisterTypeEnabled()" class="register" :to="{ name: 'register' }"> </router-link> <router-link v-if="hasRegisterTypeEnabled()" class="register" :to="{ name: 'register' }"> </router-link>
</a-form-item> </a-form-item>
</a-form> </a-form>
<a-form v-else ref="twoFactorFormRef" class="user-layout-login" :model="twoFactor" v-bind="layout">
<div class="mb-10 flex flex-center">请打开您的Authenticator APP获取动态验证码</div>
<a-form-item name="verifyCode">
<a-input v-model:value="twoFactor.verifyCode" placeholder="请输入动态验证码" allow-clear>
<template #prefix>
<fs-icon icon="ion:lock-closed-outline"></fs-icon>
</template>
</a-input>
</a-form-item>
<a-form-item>
<loading-button type="primary" size="large" html-type="button" class="login-button" :click="handleTwoFactorSubmit">OTP验证登录</loading-button>
</a-form-item>
<a-form-item class="user-login-other">
<a class="register" @click="twoFactor.loginId = null"> 返回 </a>
</a-form-item>
</a-form>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -89,42 +92,51 @@ export default defineComponent({
loginType: "password", //password loginType: "password", //password
imgCode: "", imgCode: "",
smsCode: "", smsCode: "",
randomStr: "" randomStr: "",
}); });
const rules = { const rules = {
mobile: [ mobile: [
{ {
required: true, required: true,
message: "请输入手机号" message: "请输入手机号",
} },
], ],
username: [ username: [
{ {
required: true, required: true,
message: "请输入用户名" message: "请输入用户名",
} },
], ],
password: [ password: [
{ {
required: true, required: true,
message: "请输入登录密码" message: "请输入登录密码",
} },
], ],
smsCode: [ smsCode: [
{ {
required: true, required: true,
message: "请输入短信验证码" message: "请输入短信验证码",
} },
] ],
}; };
const layout = { const layout = {
labelCol: { labelCol: {
span: 0 span: 0,
}, },
wrapperCol: { wrapperCol: {
span: 24 span: 24,
} },
};
const twoFactor = reactive({
loginId: "",
verifyCode: "",
});
const handleTwoFactorSubmit = async () => {
await userStore.loginByTwoFactor(twoFactor);
}; };
const handleFinish = async (values: any) => { const handleFinish = async (values: any) => {
@ -132,6 +144,14 @@ export default defineComponent({
try { try {
const loginType = formState.loginType; const loginType = formState.loginType;
await userStore.login(loginType, toRaw(formState)); await userStore.login(loginType, toRaw(formState));
} catch (e: any) {
//@ts-ignore
if (e.code === 10020) {
//@ts-ignore
twoFactor.loginId = e.data;
} else {
throw e;
}
} finally { } finally {
loading.value = false; loading.value = false;
} }
@ -163,9 +183,11 @@ export default defineComponent({
resetForm, resetForm,
isLoginError, isLoginError,
sysPublicSettings, sysPublicSettings,
hasRegisterTypeEnabled hasRegisterTypeEnabled,
twoFactor,
handleTwoFactorSubmit,
}; };
} },
}); });
</script> </script>

View File

@ -66,34 +66,34 @@ import { useSettingStore } from "/@/store/settings";
import { notification } from "ant-design-vue"; import { notification } from "ant-design-vue";
defineOptions({ defineOptions({
name: "SettingRegister" name: "SettingRegister",
}); });
const testMobile = ref(""); const testMobile = ref("");
async function testSendSms() { async function testSendSms() {
if (!testMobile.value) { if (!testMobile.value) {
notification.error({ notification.error({
message: "请输入测试手机号" message: "请输入测试手机号",
}); });
return; return;
} }
await api.TestSms({ await api.TestSms({
mobile: testMobile.value mobile: testMobile.value,
}); });
notification.success({ notification.success({
message: "发送成功" message: "发送成功",
}); });
} }
const formState = reactive<Partial<SysSettings>>({ const formState = reactive<Partial<SysSettings>>({
public: { public: {
registerEnabled: false registerEnabled: false,
}, },
private: { private: {
sms: { sms: {
type: "aliyun", type: "aliyun",
config: {} config: {},
} },
} },
}); });
const rules = { const rules = {
@ -103,13 +103,13 @@ const rules = {
return Promise.reject("密码登录和手机号登录至少开启一个"); return Promise.reject("密码登录和手机号登录至少开启一个");
} }
return Promise.resolve(); return Promise.resolve();
} },
}, },
required: { required: {
required: true, required: true,
trigger: "change", trigger: "change",
message: "此项必填" message: "此项必填",
} },
}; };
async function smsTypeChange(value: string) { async function smsTypeChange(value: string) {
@ -124,13 +124,13 @@ async function loadTypeDefine(type: string) {
const define: any = await api.GetSmsTypeDefine(type); const define: any = await api.GetSmsTypeDefine(type);
const keys = Object.keys(define.input); const keys = Object.keys(define.input);
const inputs: any = {}; const inputs: any = {};
keys.forEach((key) => { keys.forEach(key => {
const value = define.input[key]; const value = define.input[key];
value.simpleKey = key; value.simpleKey = key;
value.key = "private.sms.config." + key; value.key = "private.sms.config." + key;
if (!value.component) { if (!value.component) {
value.component = { value.component = {
name: "a-input" name: "a-input",
}; };
} }
if (!value.component.name) { if (!value.component.name) {
@ -165,7 +165,7 @@ const onFinish = async (form: any) => {
await api.SysSettingsSave(form); await api.SysSettingsSave(form);
await settingsStore.loadSysSettings(); await settingsStore.loadSysSettings();
notification.success({ notification.success({
message: "保存成功" message: "保存成功",
}); });
} finally { } finally {
saveLoading.value = false; saveLoading.value = false;

View File

@ -3,7 +3,10 @@
<a-form ref="formRef" :model="formState" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off"> <a-form ref="formRef" :model="formState" :label-col="{ span: 8 }" :wrapper-col="{ span: 16 }" autocomplete="off">
<h2>站点隐藏</h2> <h2>站点隐藏</h2>
<a-form-item label="启用站点隐藏" :name="['hidden', 'enabled']" :required="true"> <a-form-item label="启用站点隐藏" :name="['hidden', 'enabled']" :required="true">
<a-switch v-model:checked="formState.hidden.enabled" /> <div class="flex">
<a-switch v-model:checked="formState.hidden.enabled" />
</div>
<div class="helper"> <div class="helper">
可以在平时关闭站点的可访问性需要时再打开增强站点安全性 可以在平时关闭站点的可访问性需要时再打开增强站点安全性
<a href="https://certd.docmirror.cn/guide/feature/safe/hidden" class="flex items-center" target="_blank"> <a href="https://certd.docmirror.cn/guide/feature/safe/hidden" class="flex items-center" target="_blank">
@ -52,10 +55,11 @@ import { merge } from "lodash-es";
import { Modal, notification } from "ant-design-vue"; import { Modal, notification } from "ant-design-vue";
import { request } from "/@/api/service"; import { request } from "/@/api/service";
import { util, utils } from "/@/utils"; import { util, utils } from "/@/utils";
import { useSettingStore } from "/@/store/settings";
defineOptions({ defineOptions({
name: "SettingSafe", name: "SettingSafe",
}); });
const settingsStore = useSettingStore();
const api = { const api = {
async SettingGet() { async SettingGet() {
return await request({ return await request({

View File

@ -3,6 +3,14 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.33.5](https://github.com/certd/certd/compare/v1.33.4...v1.33.5) (2025-04-17)
### Performance Improvements
* 登录支持双重认证 ([48aef25](https://github.com/certd/certd/commit/48aef25b3f6499d674ca4e4ef16f4c62399fb735))
* 多重认证登录 ([0f82cf4](https://github.com/certd/certd/commit/0f82cf409bc60706ab07e4ca4f272b9a1ca7eecb))
* 优化部署到华为云CDN支持先上传到ccm再使用证书id部署修复offline状态下导致部署报错的bug ([79df39a](https://github.com/certd/certd/commit/79df39acabab10ae7e1864dadcdc186bb007a3c5))
## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15) ## [1.33.4](https://github.com/certd/certd/compare/v1.33.3...v1.33.4) (2025-04-15)
### Bug Fixes ### Bug Fixes

View File

@ -1,6 +1,6 @@
{ {
"name": "@certd/ui-server", "name": "@certd/ui-server",
"version": "1.33.4", "version": "1.33.5",
"description": "fast-server base midway", "description": "fast-server base midway",
"private": true, "private": true,
"type": "module", "type": "module",
@ -38,19 +38,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.33.4", "@certd/acme-client": "^1.33.5",
"@certd/basic": "^1.33.4", "@certd/basic": "^1.33.5",
"@certd/commercial-core": "^1.33.4", "@certd/commercial-core": "^1.33.5",
"@certd/jdcloud": "^1.33.4", "@certd/jdcloud": "^1.33.5",
"@certd/lib-huawei": "^1.33.4", "@certd/lib-huawei": "^1.33.5",
"@certd/lib-k8s": "^1.33.4", "@certd/lib-k8s": "^1.33.5",
"@certd/lib-server": "^1.33.4", "@certd/lib-server": "^1.33.5",
"@certd/midway-flyway-js": "^1.33.4", "@certd/midway-flyway-js": "^1.33.5",
"@certd/pipeline": "^1.33.4", "@certd/pipeline": "^1.33.5",
"@certd/plugin-cert": "^1.33.4", "@certd/plugin-cert": "^1.33.5",
"@certd/plugin-lib": "^1.33.4", "@certd/plugin-lib": "^1.33.5",
"@certd/plugin-plus": "^1.33.4", "@certd/plugin-plus": "^1.33.5",
"@certd/plus-core": "^1.33.4", "@certd/plus-core": "^1.33.5",
"@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",
@ -97,9 +97,11 @@
"nanoid": "^5.0.7", "nanoid": "^5.0.7",
"node-forge": "^1.3.1", "node-forge": "^1.3.1",
"nodemailer": "^6.9.16", "nodemailer": "^6.9.16",
"otplib": "^12.0.1",
"pg": "^8.12.0", "pg": "^8.12.0",
"psl": "^1.9.0", "psl": "^1.9.0",
"qiniu": "^7.12.0", "qiniu": "^7.12.0",
"qrcode": "^1.5.4",
"qs": "^6.13.1", "qs": "^6.13.1",
"querystring": "^0.2.1", "querystring": "^0.2.1",
"reflect-metadata": "^0.2.2", "reflect-metadata": "^0.2.2",

View File

@ -1,7 +1,7 @@
import {ALL, Body, Controller, Inject, Post, Provide} from '@midwayjs/core'; import { ALL, Body, Controller, Inject, Post, Provide } from "@midwayjs/core";
import {BaseController, SysSafeSetting} from '@certd/lib-server'; import { BaseController, SysSafeSetting } from "@certd/lib-server";
import {cloneDeep} from 'lodash-es'; import { cloneDeep } from "lodash-es";
import {SafeService} from "../../../modules/sys/settings/safe-service.js"; import { SafeService } from "../../../modules/sys/settings/safe-service.js";
/** /**

View File

@ -23,13 +23,16 @@ export class LoginController extends BaseController {
user: any user: any
) { ) {
const token = await this.loginService.loginByPassword(user); const token = await this.loginService.loginByPassword(user);
this.ctx.cookies.set('token', token.token, { this.writeTokenCookie(token);
maxAge: 1000 * token.expire,
});
return this.ok(token); return this.ok(token);
} }
private writeTokenCookie(token: { expire: any; token: any }) {
this.ctx.cookies.set("token", token.token, {
maxAge: 1000 * token.expire
});
}
@Post('/loginBySms', { summary: Constants.per.guest }) @Post('/loginBySms', { summary: Constants.per.guest })
public async loginBySms( public async loginBySms(
@Body(ALL) @Body(ALL)
@ -48,10 +51,23 @@ export class LoginController extends BaseController {
randomStr: body.randomStr, randomStr: body.randomStr,
}); });
this.ctx.cookies.set('token', token.token, { this.writeTokenCookie(token);
maxAge: 1000 * token.expire,
return this.ok(token);
}
@Post('/loginByTwoFactor', { summary: Constants.per.guest })
public async loginByTwoFactor(
@Body(ALL)
body: any
) {
const token = await this.loginService.loginByTwoFactor({
loginId: body.loginId,
verifyCode: body.verifyCode,
}); });
this.writeTokenCookie(token);
return this.ok(token); return this.ok(token);
} }

View File

@ -0,0 +1,75 @@
import { ALL, Body, Controller, Inject, Post, Provide } from "@midwayjs/core";
import { BaseController, Constants } from "@certd/lib-server";
import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js";
import { UserTwoFactorSetting } from "../../../modules/mine/service/models.js";
import { merge } from "lodash-es";
import { TwoFactorService } from "../../../modules/mine/service/two-factor-service.js";
import {isPlus} from "@certd/plus-core";
/**
*/
@Provide()
@Controller("/api/user/settings/twoFactor")
export class UserTwoFactorSettingController extends BaseController {
@Inject()
service: UserSettingsService;
@Inject()
twoFactorService: TwoFactorService;
@Post("/get", { summary: Constants.per.authOnly })
async get() {
const userId = this.getUserId();
const setting = await this.service.getSetting<UserTwoFactorSetting>(userId, UserTwoFactorSetting);
return this.ok(setting);
}
@Post("/save", { summary: Constants.per.authOnly })
async save(@Body(ALL) bean: any) {
if (!isPlus()) {
throw new Error('本功能需要开通专业版')
}
const userId = this.getUserId();
const setting = new UserTwoFactorSetting();
merge(setting, bean);
// 禁用时清除
if(!setting.authenticator.enabled){
setting.authenticator.secret = null;
setting.authenticator.verified = false;
}
await this.service.saveSetting(userId, setting);
return this.ok({});
}
@Post("/authenticator/qrcode", { summary: Constants.per.authOnly })
async authenticatorQrcode() {
const userId = this.getUserId();
const qrcode = await this.twoFactorService.getAuthenticatorQrCode(userId);
return this.ok(qrcode);
}
@Post("/authenticator/save", { summary: Constants.per.authOnly })
async authenticatorSave(@Body(ALL) bean: any) {
if (!isPlus()) {
throw new Error('本功能需要开通专业版')
}
const userId = this.getUserId();
await this.twoFactorService.saveAuthenticator({
userId,
verifyCode: bean.verifyCode,
});
return this.ok();
}
@Post("/authenticator/off", { summary: Constants.per.authOnly })
async authenticatorOff() {
const userId = this.getUserId();
await this.twoFactorService.offAuthenticator(userId);
return this.ok();
}
}

View File

@ -1,8 +1,7 @@
import { ALL, Body, Controller, Inject, Post, Provide, Query } from '@midwayjs/core'; import { ALL, Body, Controller, Inject, Post, Provide, Query } from "@midwayjs/core";
import { CrudController } from '@certd/lib-server'; import { Constants, CrudController } from "@certd/lib-server";
import { Constants } from '@certd/lib-server'; import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js";
import { UserSettingsService } from '../../../modules/mine/service/user-settings-service.js'; import { UserSettingsEntity } from "../../../modules/mine/entity/user-settings.js";
import { UserSettingsEntity } from '../../../modules/mine/entity/user-settings.js';
/** /**
*/ */
@ -66,4 +65,6 @@ export class UserSettingsController extends CrudController<UserSettingsService>
const entity = await this.service.getByKey(key, this.getUserId()); const entity = await this.service.getByKey(key, this.getUserId());
return this.ok(entity); return this.ok(entity);
} }
} }

View File

@ -1,7 +1,7 @@
import { Provide } from '@midwayjs/core'; import { Provide } from '@midwayjs/core';
import { IMidwayKoaContext, IWebMiddleware, NextFunction } from '@midwayjs/koa'; import { IMidwayKoaContext, IWebMiddleware, NextFunction } from '@midwayjs/koa';
import { logger } from '@certd/basic'; import { logger } from '@certd/basic';
import { Result } from '@certd/lib-server'; import { Result, TextException } from "@certd/lib-server";
@Provide() @Provide()
export class GlobalExceptionMiddleware implements IWebMiddleware { export class GlobalExceptionMiddleware implements IWebMiddleware {
@ -14,12 +14,15 @@ export class GlobalExceptionMiddleware implements IWebMiddleware {
await next(); await next();
logger.info('请求完成:', url, Date.now() - startTime + 'ms'); logger.info('请求完成:', url, Date.now() - startTime + 'ms');
} catch (err) { } catch (err) {
if(err instanceof TextException){
delete err.stack
}
logger.error('请求异常:', url, Date.now() - startTime + 'ms', err); logger.error('请求异常:', url, Date.now() - startTime + 'ms', err);
ctx.status = 200; ctx.status = 200;
if (err.code == null || typeof err.code !== 'number') { if (err.code == null || typeof err.code !== 'number') {
err.code = 1; err.code = 1;
} }
ctx.body = Result.error(err.code, err.message); ctx.body = Result.error(err.code, err.message,err.data);
} }
}; };
} }

View File

@ -1,14 +1,17 @@
import {Config, Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core'; import {Config, Inject, Provide, Scope, ScopeEnum} from '@midwayjs/core';
import {UserService} from '../../sys/authority/service/user-service.js'; import {UserService} from '../../sys/authority/service/user-service.js';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import {CommonException} from '@certd/lib-server'; import {AuthException, CommonException, Need2FAException} from "@certd/lib-server";
import {RoleService} from '../../sys/authority/service/role-service.js'; import {RoleService} from '../../sys/authority/service/role-service.js';
import {UserEntity} from '../../sys/authority/entity/user.js'; import {UserEntity} from '../../sys/authority/entity/user.js';
import {SysSettingsService} from '@certd/lib-server'; import {SysSettingsService} from '@certd/lib-server';
import {SysPrivateSettings} from '@certd/lib-server'; import {SysPrivateSettings} from '@certd/lib-server';
import {cache} from '@certd/basic'; import {cache, utils} from '@certd/basic';
import {LoginErrorException} from '@certd/lib-server/dist/basic/exception/login-error-exception.js'; import {LoginErrorException} from '@certd/lib-server/dist/basic/exception/login-error-exception.js';
import {CodeService} from '../../basic/service/code-service.js'; import {CodeService} from '../../basic/service/code-service.js';
import {TwoFactorService} from "../../mine/service/two-factor-service.js";
import {UserSettingsService} from '../../mine/service/user-settings-service.js';
import {isPlus} from "@certd/plus-core";
/** /**
* *
@ -28,6 +31,10 @@ export class LoginService {
@Inject() @Inject()
sysSettingsService: SysSettingsService; sysSettingsService: SysSettingsService;
@Inject()
userSettingsService: UserSettingsService;
@Inject()
twoFactorService: TwoFactorService;
checkIsBlocked(username: string) { checkIsBlocked(username: string) {
const blockDurationKey = `login_block_duration:${username}`; const blockDurationKey = `login_block_duration:${username}`;
@ -138,21 +145,56 @@ export class LoginService {
return this.onLoginSuccess(info); return this.onLoginSuccess(info);
} }
private async onLoginSuccess(info: UserEntity) { async checkTwoFactorEnabled(userId: number) {
//检查是否开启多重认证
if (!isPlus()) {
return true
}
const twoFactorSetting = await this.twoFactorService.getSetting(userId)
const authenticatorSetting = twoFactorSetting.authenticator
if (authenticatorSetting.enabled) {
//要检查
const randomKey = utils.id.simpleNanoId(12)
cache.set(`login_2fa_code:${randomKey}`, userId, {
ttl: 60 * 1000 * 2,
})
throw new Need2FAException('已开启多重认证请在2分钟内输入OPT验证码',randomKey)
}
}
async loginByTwoFactor(req: { loginId: string; verifyCode: string }) {
//检查是否开启多重认证
if (!isPlus()) {
throw new Error('本功能需要开通专业版')
}
const userId = cache.get(`login_2fa_code:${req.loginId}`)
if (!userId) {
throw new AuthException('已超时,请返回重新登录')
}
await this.twoFactorService.verifyAuthenticatorCode(userId, req.verifyCode)
return this.generateToken(await this.userService.findOne(userId))
}
private async onLoginSuccess(info: UserEntity) {
if (info.status === 0) { if (info.status === 0) {
throw new CommonException('用户已被禁用'); throw new CommonException('用户已被禁用');
} }
const roleIds = await this.roleService.getRoleIdsByUserId(info.id); await this.checkTwoFactorEnabled(info.id)
return this.generateToken(info, roleIds); return this.generateToken(info);
} }
/** /**
* token * token
* @param user * @param user
* @param roleIds * @param roleIds
*/ */
async generateToken(user: UserEntity, roleIds: number[]) { async generateToken(user: UserEntity) {
const roleIds = await this.roleService.getRoleIdsByUserId(user.id);
const tokenInfo = { const tokenInfo = {
username: user.username, username: user.username,
id: user.id, id: user.id,

View File

@ -0,0 +1,21 @@
import { BaseSettings } from "@certd/lib-server";
export type TwoFactorAuthenticator = {
enabled: boolean;
secret?: string;
type?: string;
verified?:boolean;
}
export class UserTwoFactorSetting extends BaseSettings {
static __title__ = "用户多重认证设置";
static __key__ = "user.two.factor";
authenticator: TwoFactorAuthenticator = {
enabled:false,
verified:false,
};
}

View File

@ -0,0 +1,92 @@
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { UserSettingsService } from "./user-settings-service.js";
import { UserTwoFactorSetting } from "./models.js";
import { UserService } from "../../sys/authority/service/user-service.js";
/**
*
*/
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class TwoFactorService {
@Inject()
userSettingsService: UserSettingsService;
@Inject()
userService: UserService;
async getAuthenticatorQrCode(userId: any) {
const setting = await this.getSetting(userId)
const authenticatorSetting = setting.authenticator;
if (!authenticatorSetting.secret) {
const { authenticator } = await import("otplib");
authenticatorSetting.secret = authenticator.generateSecret()
await this.userSettingsService.saveSetting(userId, setting);
}
const user = await this.userService.info(userId);
const username = user.username;
const secret = authenticatorSetting.secret;
const qrcodeContent = `otpauth://totp/Certd:${username}?secret=${secret}&issuer=Certd`;
//生成qrcode base64
const qrcode = await import("qrcode");
return await qrcode.toDataURL(qrcodeContent);
}
async saveAuthenticator(req: { userId: any; verifyCode: any }) {
const userId = req.userId;
const { authenticator } = await import("otplib");
const setting = await this.getSetting(userId)
const authenticatorSetting = setting.authenticator;
if (!authenticatorSetting.secret) {
throw new Error("secret is required");
}
const secret = authenticatorSetting.secret;
const token = req.verifyCode;
const isValid = authenticator.verify({ token, secret });
if (!isValid) {
throw new Error("authenticator 校验错误");
}
//校验成功,保存开启状态
authenticatorSetting.enabled = true;
authenticatorSetting.verified = true;
await this.userSettingsService.saveSetting(userId, setting);
}
async offAuthenticator(userId:number) {
if (!userId) {
throw new Error("userId is required");
}
const setting = await this.getSetting(userId)
setting.authenticator.enabled = false;
setting.authenticator.verified = false;
setting.authenticator.secret = '';
await this.userSettingsService.saveSetting(userId, setting);
}
async getSetting(userId:number) {
return await this.userSettingsService.getSetting<UserTwoFactorSetting>(userId, UserTwoFactorSetting);
}
async verifyAuthenticatorCode(userId: any, verifyCode: string) {
const { authenticator } = await import("otplib");
const setting = await this.getSetting(userId)
if (!setting.authenticator.enabled) {
throw new Error("authenticator 未开启");
}
if (!authenticator.verify({ token: verifyCode, secret: setting.authenticator.secret })) {
throw new Error("验证码错误");
}
return true;
}
}

View File

@ -1,8 +1,9 @@
import { Provide, Scope, ScopeEnum } from '@midwayjs/core'; import { Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { InjectEntityModel } from '@midwayjs/typeorm'; import { InjectEntityModel } from "@midwayjs/typeorm";
import { Repository } from 'typeorm'; import { Repository } from "typeorm";
import { BaseService } 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";
/** /**
* *
@ -27,23 +28,29 @@ export class UserSettingsService extends BaseService<UserSettingsEntity> {
const setting = JSON.parse(entity.setting); const setting = JSON.parse(entity.setting);
return { return {
id: entity.id, id: entity.id,
...setting, ...setting
}; };
} }
async getByKey(key: string, userId: number): Promise<UserSettingsEntity | null> { async getByKey(key: string, userId: number): Promise<UserSettingsEntity | null> {
if(!userId){
throw new Error('userId is required');
}
if (!key || !userId) { if (!key || !userId) {
return null; return null;
} }
return await this.repository.findOne({ return await this.repository.findOne({
where: { where: {
key, key,
userId, userId
}, }
}); });
} }
async getSettingByKey(key: string, userId: number): Promise<any | null> { async getSettingByKey(key: string, userId: number): Promise<any | null> {
if(!userId){
throw new Error('userId is required');
}
const entity = await this.getByKey(key, userId); const entity = await this.getByKey(key, userId);
if (!entity) { if (!entity) {
return null; return null;
@ -55,8 +62,8 @@ export class UserSettingsService extends BaseService<UserSettingsEntity> {
const entity = await this.repository.findOne({ const entity = await this.repository.findOne({
where: { where: {
key: bean.key, key: bean.key,
userId: bean.userId, userId: bean.userId
}, }
}); });
if (entity) { if (entity) {
entity.setting = bean.setting; entity.setting = bean.setting;
@ -66,4 +73,42 @@ export class UserSettingsService extends BaseService<UserSettingsEntity> {
await this.repository.save(bean); await this.repository.save(bean);
} }
} }
async getSetting<T>( userId: number,type: any): Promise<T> {
if(!userId){
throw new Error('userId is required');
}
const key = type.__key__;
let newSetting: T = new type();
const savedSettings = await this.getSettingByKey(key, userId);
newSetting = merge(newSetting, savedSettings);
return newSetting;
}
async saveSetting<T extends BaseSettings>(userId:number,bean: T) {
if(!userId){
throw new Error('userId is required');
}
const old = await this.getSetting(userId,bean.constructor)
bean = merge(old,bean)
const type: any = bean.constructor;
const key = type.__key__;
if(!key){
throw new Error(`${type.name} must have __key__`);
}
const entity = await this.getByKey(key,userId);
const newEntity = new UserSettingsEntity();
if (entity) {
newEntity.id = entity.id;
}else{
newEntity.key = key;
newEntity.title = type.__title__;
newEntity.userId = userId;
}
newEntity.setting = JSON.stringify(bean);
await this.repository.save(newEntity);
}
} }

View File

@ -22,11 +22,11 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书', helper: '请选择前置任务输出的域名证书',
component: { component: {
name: 'output-selector', name: 'output-selector',
from: [...CertApplyPluginNames], from: [...CertApplyPluginNames,'HauweiUploadToCCM'],
}, },
required: true, required: true,
}) })
cert!: CertInfo; cert!: CertInfo | string;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } })) @TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[]; certDomains!: string[];
@ -53,14 +53,25 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin {
domains!: string[]; domains!: string[];
async execute(): Promise<void> { async execute(): Promise<void> {
if (!this.cert) {
throw new Error('域名证书不能为空');
}
this.logger.info('开始部署证书到华为云cdn'); this.logger.info('开始部署证书到华为云cdn');
const { cdn, client } = await this.getCdnClient(); const { cdn, client } = await this.getCdnClient();
const httpsConfig = new cdn.HttpPutBody() let httpsConfig = new cdn.HttpPutBody()
.withHttpsStatus('on') .withHttpsStatus('on')
.withCertificateType('server') .withCertificateType('server')
.withCertificateName(this.appendTimeSuffix('certd'))
.withCertificateValue(this.cert.crt) if(typeof this.cert === 'object'){
.withPrivateKey(this.cert.key); httpsConfig= httpsConfig.withCertificateSource(0)
.withCertificateName(this.appendTimeSuffix('certd'))
.withCertificateValue(this.cert.crt)
.withPrivateKey(this.cert.key);
}else{
this.logger.info('使用已有域名证书:', this.cert);
httpsConfig= httpsConfig.withCertificateSource(2)//scm证书
.withScmCertificateId(this.cert)
}
const config = new cdn.Configs().withHttps(httpsConfig); const config = new cdn.Configs().withHttps(httpsConfig);
const body = new cdn.ModifyDomainConfigRequestBody().withConfigs(config); const body = new cdn.ModifyDomainConfigRequestBody().withConfigs(config);
@ -70,9 +81,28 @@ export class HauweiDeployCertToCDN extends AbstractTaskPlugin {
this.logger.info('部署域名:', JSON.stringify(this.domains)); this.logger.info('部署域名:', JSON.stringify(this.domains));
for (const domain of this.domains) { for (const domain of this.domains) {
this.logger.info('部署到域名:', domain); this.logger.info('部署到域名:', domain);
const req = new cdn.UpdateDomainFullConfigRequest().withDomainName(domain).withBody(body);
await client.updateDomainFullConfig(req); const queryReq = new cdn.ShowDomainDetailByNameRequest(domain);
this.logger.info(`部署到域名${domain}完成:`); const domainDetail = await client.showDomainDetailByName(queryReq);
//@ts-ignore
const status = domainDetail.domain.domainStatus || domainDetail.domain.domain_status
this.logger.info(`当前域名状态:`, status);
let ignoreError = false
if (status === 'offline') {
ignoreError = true
}
try{
const req = new cdn.UpdateDomainFullConfigRequest().withDomainName(domain).withBody(body);
await client.updateDomainFullConfig(req);
this.logger.info(`部署到域名${domain}完成:`);
}catch (e) {
if (ignoreError){
this.logger.warn(`部署到域名${domain}失败由于其处于offline状态忽略部署错误继续执行:`, e);
}else{
throw e
}
}
} }
this.logger.info('部署证书到华为云cdn完成'); this.logger.info('部署证书到华为云cdn完成');

View File

@ -46,7 +46,7 @@ importers:
packages/core/acme-client: packages/core/acme-client:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../basic version: link:../basic
'@peculiar/x509': '@peculiar/x509':
specifier: ^1.11.0 specifier: ^1.11.0
@ -204,11 +204,11 @@ importers:
packages/core/pipeline: packages/core/pipeline:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../basic version: link:../basic
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.33.2 specifier: ^1.33.4
version: 1.33.2 version: link:../../pro/plus-core
dayjs: dayjs:
specifier: ^1.11.7 specifier: ^1.11.7
version: 1.11.13 version: 1.11.13
@ -412,7 +412,7 @@ importers:
packages/libs/lib-k8s: packages/libs/lib-k8s:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/basic version: link:../../core/basic
'@kubernetes/client-node': '@kubernetes/client-node':
specifier: 0.21.0 specifier: 0.21.0
@ -452,17 +452,17 @@ importers:
packages/libs/lib-server: packages/libs/lib-server:
dependencies: dependencies:
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.33.2 specifier: ^1.33.4
version: 1.33.2 version: link:../../pro/plus-core
'@midwayjs/cache': '@midwayjs/cache':
specifier: ~3.14.0 specifier: ~3.14.0
version: 3.14.0 version: 3.14.0
@ -604,16 +604,16 @@ importers:
packages/plugins/plugin-cert: packages/plugins/plugin-cert:
dependencies: dependencies:
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../plugin-lib version: link:../plugin-lib
'@google-cloud/publicca': '@google-cloud/publicca':
specifier: ^1.3.0 specifier: ^1.3.0
@ -680,10 +680,10 @@ importers:
specifier: ^1.7.10 specifier: ^1.7.10
version: 1.8.0 version: 1.8.0
'@certd/basic': '@certd/basic':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/basic version: link:../../core/basic
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/pipeline version: link:../../core/pipeline
'@kubernetes/client-node': '@kubernetes/client-node':
specifier: 0.21.0 specifier: 0.21.0
@ -771,19 +771,19 @@ importers:
packages/pro/commercial-core: packages/pro/commercial-core:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.32.0 specifier: ^1.33.4
version: link:../../core/basic version: link:../../core/basic
'@certd/lib-server': '@certd/lib-server':
specifier: ^1.32.0 specifier: ^1.33.4
version: link:../../libs/lib-server version: link:../../libs/lib-server
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.32.0 specifier: ^1.33.4
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-plus': '@certd/plugin-plus':
specifier: ^1.32.0 specifier: ^1.33.4
version: link:../plugin-plus version: link:../plugin-plus
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.32.0 specifier: ^1.33.4
version: link:../plus-core version: link:../plus-core
'@midwayjs/core': '@midwayjs/core':
specifier: ~3.20.3 specifier: ~3.20.3
@ -868,22 +868,22 @@ importers:
specifier: ^1.0.2 specifier: ^1.0.2
version: 1.0.2 version: 1.0.2
'@certd/basic': '@certd/basic':
specifier: ^1.32.0 specifier: ^1.33.4
version: link:../../core/basic version: link:../../core/basic
'@certd/lib-k8s': '@certd/lib-k8s':
specifier: ^1.32.0 specifier: ^1.33.4
version: link:../../libs/lib-k8s version: link:../../libs/lib-k8s
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.32.0 specifier: ^1.33.4
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-cert': '@certd/plugin-cert':
specifier: ^1.32.0 specifier: ^1.33.4
version: link:../../plugins/plugin-cert version: link:../../plugins/plugin-cert
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.32.0 specifier: ^1.33.4
version: link:../../plugins/plugin-lib version: link:../../plugins/plugin-lib
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.32.0 specifier: ^1.33.4
version: link:../plus-core version: link:../plus-core
ali-oss: ali-oss:
specifier: ^6.21.0 specifier: ^6.21.0
@ -980,7 +980,7 @@ importers:
packages/pro/plus-core: packages/pro/plus-core:
dependencies: dependencies:
'@certd/basic': '@certd/basic':
specifier: ^1.32.0 specifier: ^1.33.4
version: link:../../core/basic version: link:../../core/basic
dayjs: dayjs:
specifier: ^1.11.7 specifier: ^1.11.7
@ -1270,10 +1270,10 @@ importers:
version: 0.1.3(zod@3.24.2) version: 0.1.3(zod@3.24.2)
devDependencies: devDependencies:
'@certd/lib-iframe': '@certd/lib-iframe':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../libs/lib-iframe version: link:../../libs/lib-iframe
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/pipeline version: link:../../core/pipeline
'@rollup/plugin-commonjs': '@rollup/plugin-commonjs':
specifier: ^25.0.7 specifier: ^25.0.7
@ -1453,44 +1453,44 @@ importers:
specifier: ^3.705.0 specifier: ^3.705.0
version: 3.758.0(aws-crt@1.25.3) version: 3.758.0(aws-crt@1.25.3)
'@certd/acme-client': '@certd/acme-client':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/acme-client version: link:../../core/acme-client
'@certd/basic': '@certd/basic':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/basic version: link:../../core/basic
'@certd/commercial-core': '@certd/commercial-core':
specifier: ^1.33.2 specifier: ^1.33.4
version: 1.33.2(better-sqlite3@11.8.1)(encoding@0.1.13)(mysql2@3.14.0)(pg@8.13.3)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.80)(typescript@5.8.2)) version: link:../../pro/commercial-core
'@certd/jdcloud': '@certd/jdcloud':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../libs/lib-jdcloud version: link:../../libs/lib-jdcloud
'@certd/lib-huawei': '@certd/lib-huawei':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../libs/lib-huawei version: link:../../libs/lib-huawei
'@certd/lib-k8s': '@certd/lib-k8s':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../libs/lib-k8s version: link:../../libs/lib-k8s
'@certd/lib-server': '@certd/lib-server':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../libs/lib-server version: link:../../libs/lib-server
'@certd/midway-flyway-js': '@certd/midway-flyway-js':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../libs/midway-flyway-js version: link:../../libs/midway-flyway-js
'@certd/pipeline': '@certd/pipeline':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../core/pipeline version: link:../../core/pipeline
'@certd/plugin-cert': '@certd/plugin-cert':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../plugins/plugin-cert version: link:../../plugins/plugin-cert
'@certd/plugin-lib': '@certd/plugin-lib':
specifier: ^1.33.2 specifier: ^1.33.4
version: link:../../plugins/plugin-lib version: link:../../plugins/plugin-lib
'@certd/plugin-plus': '@certd/plugin-plus':
specifier: ^1.33.2 specifier: ^1.33.4
version: 1.33.2(encoding@0.1.13) version: link:../../pro/plugin-plus
'@certd/plus-core': '@certd/plus-core':
specifier: ^1.33.2 specifier: ^1.33.4
version: 1.33.2 version: link:../../pro/plus-core
'@corsinvest/cv4pve-api-javascript': '@corsinvest/cv4pve-api-javascript':
specifier: ^8.3.0 specifier: ^8.3.0
version: 8.3.0 version: 8.3.0
@ -1629,6 +1629,9 @@ importers:
nodemailer: nodemailer:
specifier: ^6.9.16 specifier: ^6.9.16
version: 6.10.0 version: 6.10.0
otplib:
specifier: ^12.0.1
version: 12.0.1
pg: pg:
specifier: ^8.12.0 specifier: ^8.12.0
version: 8.13.3 version: 8.13.3
@ -1638,6 +1641,9 @@ importers:
qiniu: qiniu:
specifier: ^7.12.0 specifier: ^7.12.0
version: 7.14.0 version: 7.14.0
qrcode:
specifier: ^1.5.4
version: 1.5.4
qs: qs:
specifier: ^6.13.1 specifier: ^6.13.1
version: 6.14.0 version: 6.14.0
@ -2620,15 +2626,6 @@ packages:
'@better-scroll/zoom@2.5.1': '@better-scroll/zoom@2.5.1':
resolution: {integrity: sha512-aGvFY5ooeZWS4RcxQLD+pGLpQHQxpPy0sMZV3yadcd2QK53PK9gS4Dp+BYfRv8lZ4/P2LoNEhr6Wq1DN6+uPlA==} resolution: {integrity: sha512-aGvFY5ooeZWS4RcxQLD+pGLpQHQxpPy0sMZV3yadcd2QK53PK9gS4Dp+BYfRv8lZ4/P2LoNEhr6Wq1DN6+uPlA==}
'@certd/commercial-core@1.33.2':
resolution: {integrity: sha512-LTRvwRAkMEU+knG+/eA8QbyK3EE2Z2eyn4763ILadCY/qHLAWqxg7NUD+fwsuAoByvsr3l5qLPPOF73p5s1iEA==}
'@certd/plugin-plus@1.33.2':
resolution: {integrity: sha512-x8qdJ1qtYfqVNBQ3uWJuFVYUHHnteoaH/3vnYKjgAnEAcosruZAz8h5RwwOjDhR+Vsdda/sSRdjlTCVDLidHZg==}
'@certd/plus-core@1.33.2':
resolution: {integrity: sha512-CsD+/P3Ycne3KzxrZAkFXGpCO9ZQNY4p28WAA2XfKQ6sLIYLuuDBP18V/E7EygfJEZ/c9KJWMkYgx/4wGGvb6w==}
'@colors/colors@1.5.0': '@colors/colors@1.5.0':
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
engines: {node: '>=0.1.90'} engines: {node: '>=0.1.90'}
@ -3787,6 +3784,21 @@ packages:
'@one-ini/wasm@0.1.1': '@one-ini/wasm@0.1.1':
resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
'@otplib/core@12.0.1':
resolution: {integrity: sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==}
'@otplib/plugin-crypto@12.0.1':
resolution: {integrity: sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==}
'@otplib/plugin-thirty-two@12.0.1':
resolution: {integrity: sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==}
'@otplib/preset-default@12.0.1':
resolution: {integrity: sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==}
'@otplib/preset-v11@12.0.1':
resolution: {integrity: sha512-9hSetMI7ECqbFiKICrNa4w70deTUfArtwXykPUvSHWOdzOlfa9ajglu7mNCntlvxycTiOAXkQGwjQCzzDEMRMg==}
'@panva/asn1.js@1.0.0': '@panva/asn1.js@1.0.0':
resolution: {integrity: sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==} resolution: {integrity: sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==}
engines: {node: '>=10.13.0'} engines: {node: '>=10.13.0'}
@ -9899,6 +9911,9 @@ packages:
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
hasBin: true hasBin: true
otplib@12.0.1:
resolution: {integrity: sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg==}
output-file-sync@1.1.2: output-file-sync@1.1.2:
resolution: {integrity: sha512-uQLlclru4xpCi+tfs80l3QF24KL81X57ELNMy7W/dox+JTtxUf1bLyQ8968fFCmSqqbokjW0kn+WBIlO+rSkNg==} resolution: {integrity: sha512-uQLlclru4xpCi+tfs80l3QF24KL81X57ELNMy7W/dox+JTtxUf1bLyQ8968fFCmSqqbokjW0kn+WBIlO+rSkNg==}
@ -11985,6 +12000,10 @@ packages:
thenify@3.3.1: thenify@3.3.1:
resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
thirty-two@1.0.2:
resolution: {integrity: sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==}
engines: {node: '>=0.2.6'}
throttle-debounce@5.0.2: throttle-debounce@5.0.2:
resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
engines: {node: '>=12.22'} engines: {node: '>=12.22'}
@ -14803,75 +14822,6 @@ snapshots:
dependencies: dependencies:
'@better-scroll/core': 2.5.1 '@better-scroll/core': 2.5.1
'@certd/commercial-core@1.33.2(better-sqlite3@11.8.1)(encoding@0.1.13)(mysql2@3.14.0)(pg@8.13.3)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.80)(typescript@5.8.2))':
dependencies:
'@certd/basic': link:packages/core/basic
'@certd/lib-server': link:packages/libs/lib-server
'@certd/pipeline': link:packages/core/pipeline
'@certd/plugin-plus': 1.33.2(encoding@0.1.13)
'@certd/plus-core': 1.33.2
'@midwayjs/core': 3.20.3
'@midwayjs/koa': 3.20.3
'@midwayjs/logger': 3.4.2
'@midwayjs/typeorm': 3.20.3
alipay-sdk: 4.13.0
dayjs: 1.11.13
typeorm: 0.3.21(better-sqlite3@11.8.1)(mysql2@3.14.0)(pg@8.13.3)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.80)(typescript@5.8.2))
wechatpay-node-v3: 2.2.1
transitivePeerDependencies:
- '@google-cloud/spanner'
- '@sap/hana-client'
- better-sqlite3
- encoding
- hdb-pool
- ioredis
- mongodb
- mssql
- mysql2
- oracledb
- pg
- pg-native
- pg-query-stream
- proxy-agent
- redis
- reflect-metadata
- sql.js
- sqlite3
- supports-color
- ts-node
- typeorm-aurora-data-api-driver
'@certd/plugin-plus@1.33.2(encoding@0.1.13)':
dependencies:
'@alicloud/pop-core': 1.8.0
'@baiducloud/sdk': 1.0.2
'@certd/basic': link:packages/core/basic
'@certd/lib-k8s': link:packages/libs/lib-k8s
'@certd/pipeline': link:packages/core/pipeline
'@certd/plugin-cert': link:packages/plugins/plugin-cert
'@certd/plugin-lib': link:packages/plugins/plugin-lib
'@certd/plus-core': 1.33.2
ali-oss: 6.22.0
baidu-aip-sdk: 4.16.16
basic-ftp: 5.0.5
cos-nodejs-sdk-v5: 2.14.6
crypto-js: 4.2.0
dayjs: 1.11.13
form-data: 4.0.2
https-proxy-agent: 7.0.6
jsencrypt: 3.3.2
qiniu: 7.14.0
tencentcloud-sdk-nodejs: 4.0.1045(encoding@0.1.13)
transitivePeerDependencies:
- encoding
- proxy-agent
- supports-color
'@certd/plus-core@1.33.2':
dependencies:
'@certd/basic': link:packages/core/basic
dayjs: 1.11.13
'@colors/colors@1.5.0': '@colors/colors@1.5.0':
optional: true optional: true
@ -16306,6 +16256,29 @@ snapshots:
'@one-ini/wasm@0.1.1': {} '@one-ini/wasm@0.1.1': {}
'@otplib/core@12.0.1': {}
'@otplib/plugin-crypto@12.0.1':
dependencies:
'@otplib/core': 12.0.1
'@otplib/plugin-thirty-two@12.0.1':
dependencies:
'@otplib/core': 12.0.1
thirty-two: 1.0.2
'@otplib/preset-default@12.0.1':
dependencies:
'@otplib/core': 12.0.1
'@otplib/plugin-crypto': 12.0.1
'@otplib/plugin-thirty-two': 12.0.1
'@otplib/preset-v11@12.0.1':
dependencies:
'@otplib/core': 12.0.1
'@otplib/plugin-crypto': 12.0.1
'@otplib/plugin-thirty-two': 12.0.1
'@panva/asn1.js@1.0.0': {} '@panva/asn1.js@1.0.0': {}
'@peculiar/asn1-cms@2.3.15': '@peculiar/asn1-cms@2.3.15':
@ -20700,13 +20673,13 @@ snapshots:
resolve: 1.22.10 resolve: 1.22.10
semver: 6.3.1 semver: 6.3.1
eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8): eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8):
dependencies: dependencies:
eslint: 7.32.0 eslint: 7.32.0
prettier: 2.8.8 prettier: 2.8.8
prettier-linter-helpers: 1.0.0 prettier-linter-helpers: 1.0.0
optionalDependencies: optionalDependencies:
eslint-config-prettier: 8.10.0(eslint@8.57.0) eslint-config-prettier: 8.10.0(eslint@7.32.0)
eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8): eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8):
dependencies: dependencies:
@ -23420,7 +23393,7 @@ snapshots:
eslint: 7.32.0 eslint: 7.32.0
eslint-config-prettier: 8.10.0(eslint@7.32.0) eslint-config-prettier: 8.10.0(eslint@7.32.0)
eslint-plugin-node: 11.1.0(eslint@7.32.0) eslint-plugin-node: 11.1.0(eslint@7.32.0)
eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8) eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8)
execa: 5.1.1 execa: 5.1.1
inquirer: 7.3.3 inquirer: 7.3.3
json5: 2.2.3 json5: 2.2.3
@ -23853,6 +23826,12 @@ snapshots:
dependencies: dependencies:
minimist: 1.2.8 minimist: 1.2.8
otplib@12.0.1:
dependencies:
'@otplib/core': 12.0.1
'@otplib/preset-default': 12.0.1
'@otplib/preset-v11': 12.0.1
output-file-sync@1.1.2: output-file-sync@1.1.2:
dependencies: dependencies:
graceful-fs: 4.2.11 graceful-fs: 4.2.11
@ -26255,6 +26234,8 @@ snapshots:
dependencies: dependencies:
any-promise: 1.3.0 any-promise: 1.3.0
thirty-two@1.0.2: {}
throttle-debounce@5.0.2: {} throttle-debounce@5.0.2: {}
through2@2.0.5: through2@2.0.5: