mirror of
https://github.com/certd/certd.git
synced 2026-05-13 19:47:55 +08:00
Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev
This commit is contained in:
@@ -142,6 +142,12 @@ Certd 是一个支持私有化部署的 SSL/TLS 证书自动化管理平台。
|
||||
|
||||
如果只是某个服务商或部署目标的问题,不要轻易修改共享 pipeline/core 行为,除非确实是可复用的公共能力。
|
||||
|
||||
### ACME / EAB 注意事项
|
||||
|
||||
- 公共 EAB(尤其是 Google EAB)可能只能创建一次 ACME 账号。要跨用户复用公共 EAB,应保存并复用同一个 ACME account private key;`accountUrl` 如果存到 `userContext` 里,只能视为当前用户缓存,因为 `userContext` 跟用户 id 走。
|
||||
- ACME 协议的 `newAccount` 支持 `onlyReturnExisting`。使用同一个 account private key 调用 `newAccount({ onlyReturnExisting: true })` 可以取回已创建账号的 URL,且不会再次消费 EAB。
|
||||
- 修改 EAB 的 `kid` 后,应重新生成绑定该 `kid` 的 account private key;否则应阻止继续申请并提示用户刷新账号私钥。
|
||||
|
||||
## 数据与迁移
|
||||
|
||||
后端使用 TypeORM 实体加 SQL 迁移。
|
||||
@@ -161,6 +167,7 @@ Certd 是一个支持私有化部署的 SSL/TLS 证书自动化管理平台。
|
||||
- 优先沿用现有模块、插件、服务模式,再考虑新增抽象。
|
||||
- `packages/ui/certd-server/data/`、`logs/`、生成的 metadata/dist 等通常视为运行时或构建产物,除非任务明确要求处理它们。
|
||||
- 注意本地数据和配置里可能包含凭据、证书材料等敏感信息。
|
||||
- 本仓库代码注释优先使用中文,尤其是解释业务规则、兼容逻辑、协议细节和隐藏风险时;除非文件已有明确英文注释风格或引用外部英文术语,否则不要新增英文说明性注释。
|
||||
|
||||
## 插件开发技能
|
||||
|
||||
@@ -207,6 +214,7 @@ Get-ChildItem packages\ui\certd-client\src\views\certd
|
||||
- 实现新功能或修复行为缺陷前,先补对应单元测试,并先运行测试确认它处于失败状态;再实现功能或修复代码,反复运行聚焦单元测试直到通过。若某项改动确实不适合先写单元测试,应在回复中说明原因和替代验证方式。
|
||||
- 后补单元测试时,应先基于对正确行为的实际预期编写测试,而不是为了迎合现有实现改写预期;如果运行后出现红灯,且通过测试需要修改已有实现,应先向用户确认这是确实的 bug,还是原本需求/既有行为就是如此;确认后再修改原始实现,避免把测试补充变成未经确认的行为改动。
|
||||
- 后端纯单元测试用例放在 `src` 目录内,并尽量与被测文件相邻,例如 `src/utils/random.test.ts`;对应 `test:unit` 只跑 `src/**/*.test.ts`,构建/打包配置应排除这些 `*.test.ts` 文件。
|
||||
- 单元测试需要 mock ESM 静态 import 时,优先使用 `esmock`,不要为了测试把业务代码改成构造函数注入或把逻辑挪到调用方;各包 `test:unit` 脚本应显式设置 `NODE_ENV=unittest`。
|
||||
- 单个 monorepo 包运行单元测试时,优先使用 `corepack pnpm --dir <包目录> test:unit`,例如 `corepack pnpm --dir packages\ui\certd-server test:unit`、`corepack pnpm --dir packages\core\basic test:unit`、`corepack pnpm --dir packages\plugins\plugin-lib test:unit`;也可以用包名过滤,例如 `corepack pnpm --filter @certd/ui-server test:unit`。前端 `packages\ui\certd-client` 暂时不跑单元测试。
|
||||
- 前端 TS/Vue/locale 等文件改动后,优先只对本次改动文件运行项目现有自动格式化/修复;Windows/PowerShell 下 Prettier 已验证可用命令为 `packages\ui\certd-client\node_modules\.bin\prettier.cmd --write <files>`,ESLint 可用命令为 `packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix <files>`;不要为了格式化无关文件而扩大 diff。项目保留了 `tslint` 依赖,但当前主要使用 ESLint + Prettier。
|
||||
- 优先对改动包运行聚焦的测试或类型检查;只有跨包影响明显时再考虑全 monorepo 构建。
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **aliyun-access:** 添加阿里云密钥校验失败的错误处理 ([b75c625](https://github.com/certd/certd/commit/b75c625ddcc0b3110699d8e6175681ef157b25df))
|
||||
* cnameProvider域名支持设置子域名托管 ([7266af1](https://github.com/certd/certd/commit/7266af17491a98338022cfb18cfedfb93ca6ef8f))
|
||||
* **plugin-aliyun:** 过滤非CAS证书并优化日志信息 ([c4b01da](https://github.com/certd/certd/commit/c4b01da384bc40a241a673ea8bc01ca733c04d83))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **设置:** 添加首页启用开关配置 ([25ad1e6](https://github.com/certd/certd/commit/25ad1e6f861e43288cc8bd90d4903628e6faec29))
|
||||
* 新增agents.md ([aa176b0](https://github.com/certd/certd/commit/aa176b081a92837d2d6809d16546a8dfc2e5dd36))
|
||||
* **用户资料:** 新增手机号邮箱绑定功能 ([e0eb0e2](https://github.com/certd/certd/commit/e0eb0e21f6dae24b639c944f9aba2c90496ab1c0))
|
||||
* 域名注册过期时间获取再次优化 ([91a1b97](https://github.com/certd/certd/commit/91a1b9755066bf280e194dabf7c3a9f936e2643f))
|
||||
* **证书流水线:** 添加批量更新证书申请参数功能 ([63be1c1](https://github.com/certd/certd/commit/63be1c1cbd9b09a3b48f26130c296b1cedcca1ac))
|
||||
* 支持火山云vke ([bb46cb0](https://github.com/certd/certd/commit/bb46cb08f71f6ae921543f7e4a6c5f4e0190556e))
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
* **domain:** 添加域名过期时间同步进度显示功能 ([9d2937d](https://github.com/certd/certd/commit/9d2937dd4b14ffab73e9b096edd2aa8539811182))
|
||||
* **plugin-volcengine:** 支持火山引擎VKE部署插件 ([b8a64a6](https://github.com/certd/certd/commit/b8a64a6b5bf3691a47177de42bc49b798e795feb))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **aliyun-access:** 添加阿里云密钥校验失败的错误处理 ([b75c625](https://github.com/certd/certd/commit/b75c625ddcc0b3110699d8e6175681ef157b25df))
|
||||
* cnameProvider域名支持设置子域名托管 ([7266af1](https://github.com/certd/certd/commit/7266af17491a98338022cfb18cfedfb93ca6ef8f))
|
||||
* **plugin-aliyun:** 过滤非CAS证书并优化日志信息 ([c4b01da](https://github.com/certd/certd/commit/c4b01da384bc40a241a673ea8bc01ca733c04d83))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **设置:** 添加首页启用开关配置 ([25ad1e6](https://github.com/certd/certd/commit/25ad1e6f861e43288cc8bd90d4903628e6faec29))
|
||||
* 新增agents.md ([aa176b0](https://github.com/certd/certd/commit/aa176b081a92837d2d6809d16546a8dfc2e5dd36))
|
||||
* **用户资料:** 新增手机号邮箱绑定功能 ([e0eb0e2](https://github.com/certd/certd/commit/e0eb0e21f6dae24b639c944f9aba2c90496ab1c0))
|
||||
* 域名注册过期时间获取再次优化 ([91a1b97](https://github.com/certd/certd/commit/91a1b9755066bf280e194dabf7c3a9f936e2643f))
|
||||
* **证书流水线:** 添加批量更新证书申请参数功能 ([63be1c1](https://github.com/certd/certd/commit/63be1c1cbd9b09a3b48f26130c296b1cedcca1ac))
|
||||
* 支持火山云vke ([bb46cb0](https://github.com/certd/certd/commit/bb46cb08f71f6ae921543f7e4a6c5f4e0190556e))
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
* **domain:** 添加域名过期时间同步进度显示功能 ([9d2937d](https://github.com/certd/certd/commit/9d2937dd4b14ffab73e9b096edd2aa8539811182))
|
||||
* **plugin-volcengine:** 支持火山引擎VKE部署插件 ([b8a64a6](https://github.com/certd/certd/commit/b8a64a6b5bf3691a47177de42bc49b798e795feb))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 任务插件
|
||||
共 `132` 款任务插件
|
||||
共 `131` 款任务插件
|
||||
## 1. 证书申请
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|
||||
+1
-1
@@ -9,5 +9,5 @@
|
||||
}
|
||||
},
|
||||
"npmClient": "pnpm",
|
||||
"version": "1.39.12"
|
||||
"version": "1.39.13"
|
||||
}
|
||||
|
||||
+2
-1
@@ -9,6 +9,7 @@
|
||||
"@lerna-lite/run": "^3.9.3",
|
||||
"@lerna-lite/version": "^3.9.3",
|
||||
"axios": "^1.9.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"medium-zoom": "^1.1.0",
|
||||
"vitepress": "^2.0.0-alpha.4",
|
||||
"vitepress-plugin-lightbox": "^1.0.2"
|
||||
@@ -35,7 +36,7 @@
|
||||
"docs:dev": "vitepress dev docs",
|
||||
"docs:build": "pnpm run copylogs && vitepress build docs",
|
||||
"docs:preview": "vitepress preview docs",
|
||||
"test:unit": "pnpm -r --workspace-concurrency=1 run test:unit",
|
||||
"test:unit": "cross-env NODE_ENV=unittest pnpm -r --workspace-concurrency=1 run test:unit",
|
||||
"pub": "echo 1",
|
||||
"dev": "pnpm run -r --parallel compile ",
|
||||
"pub_all":"pnpm run -r --parallel pub ",
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/publishlab/node-acme-client/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/publishlab/node-acme-client/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
|
||||
## [1.39.12](https://github.com/publishlab/node-acme-client/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "Simple and unopinionated ACME client",
|
||||
"private": false,
|
||||
"author": "nmorsman",
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"type": "module",
|
||||
"module": "./dist/index.js",
|
||||
"main": "./dist/index.js",
|
||||
@@ -18,7 +18,7 @@
|
||||
"types"
|
||||
],
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.39.12",
|
||||
"@certd/basic": "^1.39.13",
|
||||
"@peculiar/x509": "^1.11.0",
|
||||
"asn1js": "^3.0.5",
|
||||
"axios": "^1.9.0",
|
||||
@@ -35,10 +35,12 @@
|
||||
"@typescript-eslint/parser": "^8.26.1",
|
||||
"chai": "^4.4.1",
|
||||
"chai-as-promised": "^7.1.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"esmock": "^2.7.5",
|
||||
"jsdoc-to-markdown": "^8.0.1",
|
||||
"mocha": "^10.6.0",
|
||||
"nock": "^13.5.4",
|
||||
@@ -55,7 +57,7 @@
|
||||
"prepublishOnly": "npm run build && npm run build-docs",
|
||||
"test": "mocha -t 60000 \"test/setup.js\" \"test/**/*.spec.js\"",
|
||||
"before-test:unit": "node -e \"const fs=require('fs');fs.rmSync('dist-test',{recursive:true,force:true});fs.rmSync('tsconfig.test.tsbuildinfo',{force:true});\"",
|
||||
"test:unit": "npm run before-test:unit && tsc -p tsconfig.test.json --skipLibCheck && mocha -t 60000 \"dist-test/**/*.test.js\"",
|
||||
"test:unit": "cross-env NODE_ENV=unittest npm run before-test:unit && cross-env NODE_ENV=unittest tsc -p tsconfig.test.json --skipLibCheck && cross-env NODE_ENV=unittest mocha -t 60000 \"dist-test/**/*.test.js\"",
|
||||
"pub": "npm publish",
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
@@ -74,5 +76,5 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||
},
|
||||
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||
"gitHead": "9f7d766cb386b299d4098141f4a47d23e16975e3"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -1 +1 @@
|
||||
23:06
|
||||
00:21
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/basic",
|
||||
"private": false,
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -13,7 +13,7 @@
|
||||
"dev-build": "npm run build",
|
||||
"preview": "vite preview",
|
||||
"test": "mocha --loader=ts-node/esm",
|
||||
"test:unit": "mocha --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||
"test:unit": "cross-env NODE_ENV=unittest mocha --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||
"pub": "npm publish",
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
@@ -40,9 +40,11 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||
"@typescript-eslint/parser": "^8.26.1",
|
||||
"chai": "4.3.10",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"esmock": "^2.7.5",
|
||||
"mocha": "^10.2.0",
|
||||
"prettier": "^2.8.8",
|
||||
"rimraf": "^5.0.5",
|
||||
@@ -50,5 +52,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||
"gitHead": "9f7d766cb386b299d4098141f4a47d23e16975e3"
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export class HttpError extends Error {
|
||||
return;
|
||||
}
|
||||
|
||||
let message = error?.message || error?.response.statusText || error?.code;
|
||||
let message = error?.message || error?.response?.statusText || error?.code;
|
||||
if (message && typeof message === "string" && message.indexOf) {
|
||||
for (const key in errorMap) {
|
||||
if (message.indexOf(key) > -1) {
|
||||
@@ -267,7 +267,7 @@ export function createAxiosService({ logger }: { logger: ILogger }) {
|
||||
logger.error(`请求出错:${errorMessage} status:${status},statusText:${error.response?.statusText || error.code},url:${error.config?.url},method:${error.config?.method}。`);
|
||||
logger.error("返回数据:", JSON.stringify(error.response?.data));
|
||||
if (error.response?.data) {
|
||||
const message = error.response.data.message || error.response.data.msg || error.response.data.error;
|
||||
const message = error.response?.data?.message || error.response?.data?.msg || error.response?.data?.error;
|
||||
if (typeof message === "string") {
|
||||
error.message = message;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,16 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* cnameProvider域名支持设置子域名托管 ([7266af1](https://github.com/certd/certd/commit/7266af17491a98338022cfb18cfedfb93ca6ef8f))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/pipeline",
|
||||
"private": false,
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -14,13 +14,13 @@
|
||||
"build3": "rollup -c",
|
||||
"preview": "vite preview",
|
||||
"test": "mocha --loader=ts-node/esm",
|
||||
"test:unit": "mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||
"test:unit": "cross-env NODE_ENV=unittest mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||
"pub": "npm publish",
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.39.12",
|
||||
"@certd/plus-core": "^1.39.12",
|
||||
"@certd/basic": "^1.39.13",
|
||||
"@certd/plus-core": "^1.39.13",
|
||||
"dayjs": "^1.11.7",
|
||||
"lodash-es": "^4.17.21",
|
||||
"reflect-metadata": "^0.1.13"
|
||||
@@ -37,9 +37,11 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||
"@typescript-eslint/parser": "^8.26.1",
|
||||
"chai": "4.3.10",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.41.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"esmock": "^2.7.5",
|
||||
"mocha": "^10.2.0",
|
||||
"prettier": "^2.8.8",
|
||||
"rimraf": "^5.0.5",
|
||||
@@ -47,5 +49,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||
"gitHead": "9f7d766cb386b299d4098141f4a47d23e16975e3"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { IAccess } from "../access/index.js";
|
||||
export type CnameProvider = {
|
||||
id: any;
|
||||
domain: string;
|
||||
subdomain?: string;
|
||||
title?: string;
|
||||
dnsProviderType?: string;
|
||||
access?: IAccess;
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-huawei",
|
||||
"private": false,
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
"types": "./dist/d/index.d.ts",
|
||||
@@ -11,7 +11,7 @@
|
||||
"build": "npm run before-build && rollup -c ",
|
||||
"dev-build": "npm run build",
|
||||
"preview": "vite preview",
|
||||
"test:unit": "echo no unit tests",
|
||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||
"pub": "npm publish"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -22,8 +22,10 @@
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||
"@typescript-eslint/parser": "^8.26.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"esmock": "^2.7.5",
|
||||
"prettier": "^2.8.8",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||
"gitHead": "9f7d766cb386b299d4098141f4a47d23e16975e3"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-iframe",
|
||||
"private": false,
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -14,7 +14,7 @@
|
||||
"build3": "rollup -c",
|
||||
"build2": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview",
|
||||
"test:unit": "echo no unit tests",
|
||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||
"pub": "npm publish"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -24,13 +24,15 @@
|
||||
"@types/chai": "^4.3.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||
"@typescript-eslint/parser": "^8.26.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.24.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"esmock": "^2.7.5",
|
||||
"prettier": "^2.8.8",
|
||||
"rimraf": "^5.0.5",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||
"gitHead": "9f7d766cb386b299d4098141f4a47d23e16975e3"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/jdcloud",
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"description": "jdcloud openApi sdk",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
@@ -8,7 +8,7 @@
|
||||
"scripts": {
|
||||
"build": "rollup -c ",
|
||||
"dev-build": "npm run build",
|
||||
"test:unit": "echo no unit tests",
|
||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||
"pub": "npm publish"
|
||||
},
|
||||
"author": "",
|
||||
@@ -30,7 +30,8 @@
|
||||
"@typescript-eslint/parser": "^8.26.1",
|
||||
"chai": "^4.1.2",
|
||||
"config": "^1.30.0",
|
||||
"cross-env": "^5.1.4",
|
||||
"cross-env": "^7.0.3",
|
||||
"esmock": "^2.7.5",
|
||||
"js-yaml": "^3.11.0",
|
||||
"mocha": "^5.0.0",
|
||||
"prettier": "^2.8.8",
|
||||
@@ -57,5 +58,5 @@
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||
"gitHead": "9f7d766cb386b299d4098141f4a47d23e16975e3"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-k8s",
|
||||
"private": false,
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -14,25 +14,27 @@
|
||||
"build3": "rollup -c",
|
||||
"build2": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview",
|
||||
"test:unit": "echo no unit tests",
|
||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||
"pub": "npm publish",
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.39.12",
|
||||
"@certd/basic": "^1.39.13",
|
||||
"@kubernetes/client-node": "0.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.3.3",
|
||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||
"@typescript-eslint/parser": "^8.26.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.24.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"esmock": "^2.7.5",
|
||||
"prettier": "^2.8.8",
|
||||
"rimraf": "^5.0.5",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||
"gitHead": "9f7d766cb386b299d4098141f4a47d23e16975e3"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,13 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **设置:** 添加首页启用开关配置 ([25ad1e6](https://github.com/certd/certd/commit/25ad1e6f861e43288cc8bd90d4903628e6faec29))
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/lib-server",
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -12,7 +12,7 @@
|
||||
"build": "npm run before-build && tsc --skipLibCheck",
|
||||
"dev-build": "npm run build",
|
||||
"test": "midway-bin test --ts -V",
|
||||
"test:unit": "mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||
"test:unit": "cross-env NODE_ENV=unittest mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||
"test1": "midway-bin test --ts -V -f test/blank.test.ts -t 'hash-check'",
|
||||
"cov": "midway-bin cov --ts",
|
||||
"lint": "mwts check",
|
||||
@@ -29,11 +29,11 @@
|
||||
],
|
||||
"license": "AGPL",
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.39.12",
|
||||
"@certd/basic": "^1.39.12",
|
||||
"@certd/pipeline": "^1.39.12",
|
||||
"@certd/plugin-lib": "^1.39.12",
|
||||
"@certd/plus-core": "^1.39.12",
|
||||
"@certd/acme-client": "^1.39.13",
|
||||
"@certd/basic": "^1.39.13",
|
||||
"@certd/pipeline": "^1.39.13",
|
||||
"@certd/plugin-lib": "^1.39.13",
|
||||
"@certd/plus-core": "^1.39.13",
|
||||
"@midwayjs/cache": "3.14.0",
|
||||
"@midwayjs/core": "3.20.11",
|
||||
"@midwayjs/i18n": "3.20.13",
|
||||
@@ -44,7 +44,6 @@
|
||||
"@midwayjs/upload": "3.20.13",
|
||||
"@midwayjs/validate": "3.20.13",
|
||||
"better-sqlite3": "^11.1.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"dayjs": "^1.11.7",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mwts": "^1.3.0",
|
||||
@@ -57,9 +56,11 @@
|
||||
"@types/node": "^18",
|
||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||
"@typescript-eslint/parser": "^8.26.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.24.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"esmock": "^2.7.5",
|
||||
"mocha": "^10.2.0",
|
||||
"prettier": "^2.8.8",
|
||||
"rimraf": "^5.0.5",
|
||||
@@ -68,5 +69,5 @@
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||
"gitHead": "9f7d766cb386b299d4098141f4a47d23e16975e3"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import assert from "assert";
|
||||
import { AccessService } from "./access-service.js";
|
||||
|
||||
describe("AccessService", () => {
|
||||
it("does not write id into access setting when updating selected fields", async () => {
|
||||
let updateParam: any;
|
||||
const service = new AccessService();
|
||||
service.info = async () => ({
|
||||
id: 12,
|
||||
type: "eab",
|
||||
} as any);
|
||||
service.decryptAccessEntity = () => ({
|
||||
kid: "kid-1",
|
||||
});
|
||||
service.update = async (param: any) => {
|
||||
updateParam = param;
|
||||
return param;
|
||||
};
|
||||
|
||||
await service.updateAccess({
|
||||
id: 12,
|
||||
accountKey: "account-key",
|
||||
});
|
||||
|
||||
assert.deepEqual(JSON.parse(updateParam.setting), {
|
||||
kid: "kid-1",
|
||||
accountKey: "account-key",
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -123,6 +123,25 @@ export class AccessService extends BaseService<AccessEntity> {
|
||||
return await super.update(param);
|
||||
}
|
||||
|
||||
async updateAccess(access: any) {
|
||||
const oldEntity = await this.info(access.id);
|
||||
if (oldEntity == null) {
|
||||
throw new ValidateException('该授权配置不存在,请确认是否已被删除');
|
||||
}
|
||||
const setting = this.decryptAccessEntity(oldEntity);
|
||||
for (const key of Object.keys(access)) {
|
||||
if (key === 'id') {
|
||||
continue;
|
||||
}
|
||||
setting[key] = access[key];
|
||||
}
|
||||
return await this.update({
|
||||
id: access.id,
|
||||
type: oldEntity.type,
|
||||
setting: JSON.stringify(setting),
|
||||
});
|
||||
}
|
||||
|
||||
async getSimpleInfo(id: number) {
|
||||
const entity = await this.info(id);
|
||||
if (entity == null) {
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/midway-flyway-js",
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -13,7 +13,7 @@
|
||||
"dev-build": "npm run build",
|
||||
"test": "midway-bin test --ts -V",
|
||||
"test1": "midway-bin test --ts -V -f test/blank.test.ts -t 'hash-check'",
|
||||
"test:unit": "echo no unit tests",
|
||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||
"cov": "midway-bin cov --ts",
|
||||
"prepublish": "npm run build",
|
||||
"pub": "npm publish"
|
||||
@@ -36,16 +36,18 @@
|
||||
"@types/node": "^18",
|
||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||
"@typescript-eslint/parser": "^8.26.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.24.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"esmock": "^2.7.5",
|
||||
"prettier": "^2.8.8",
|
||||
"rimraf": "^5.0.5",
|
||||
"tslib": "^2.8.1",
|
||||
"typeorm": "^0.3.11",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||
"gitHead": "9f7d766cb386b299d4098141f4a47d23e16975e3"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-cert",
|
||||
"private": false,
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -13,15 +13,15 @@
|
||||
"build3": "rollup -c",
|
||||
"build2": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview",
|
||||
"test:unit": "echo no unit tests",
|
||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||
"pub": "npm publish",
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.39.12",
|
||||
"@certd/basic": "^1.39.12",
|
||||
"@certd/pipeline": "^1.39.12",
|
||||
"@certd/plugin-lib": "^1.39.12",
|
||||
"@certd/acme-client": "^1.39.13",
|
||||
"@certd/basic": "^1.39.13",
|
||||
"@certd/pipeline": "^1.39.13",
|
||||
"@certd/plugin-lib": "^1.39.13",
|
||||
"psl": "^1.9.0",
|
||||
"punycode.js": "^2.3.1"
|
||||
},
|
||||
@@ -31,13 +31,15 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||
"@typescript-eslint/parser": "^8.26.1",
|
||||
"chai": "^4.3.6",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.24.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"esmock": "^2.7.5",
|
||||
"mocha": "^10.1.0",
|
||||
"prettier": "^2.8.8",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||
"gitHead": "9f7d766cb386b299d4098141f4a47d23e16975e3"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-lib",
|
||||
"private": false,
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -13,7 +13,7 @@
|
||||
"build3": "rollup -c",
|
||||
"build2": "vue-tsc --noEmit && vite build",
|
||||
"preview": "vite preview",
|
||||
"test:unit": "mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||
"test:unit": "cross-env NODE_ENV=unittest mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||
"pub": "npm publish",
|
||||
"compile": "tsc --skipLibCheck --watch"
|
||||
},
|
||||
@@ -23,10 +23,10 @@
|
||||
"@alicloud/pop-core": "^1.7.10",
|
||||
"@alicloud/tea-util": "^1.4.11",
|
||||
"@aws-sdk/client-s3": "^3.964.0",
|
||||
"@certd/acme-client": "^1.39.12",
|
||||
"@certd/basic": "^1.39.12",
|
||||
"@certd/pipeline": "^1.39.12",
|
||||
"@certd/plus-core": "^1.39.12",
|
||||
"@certd/acme-client": "^1.39.13",
|
||||
"@certd/basic": "^1.39.13",
|
||||
"@certd/pipeline": "^1.39.13",
|
||||
"@certd/plus-core": "^1.39.13",
|
||||
"@kubernetes/client-node": "0.21.0",
|
||||
"ali-oss": "^6.22.0",
|
||||
"basic-ftp": "^5.0.5",
|
||||
@@ -50,14 +50,16 @@
|
||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||
"@typescript-eslint/parser": "^8.26.1",
|
||||
"chai": "^4.3.6",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.24.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"esmock": "^2.7.5",
|
||||
"mocha": "^10.1.0",
|
||||
"prettier": "^2.8.8",
|
||||
"ts-node": "^10.9.2",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
|
||||
"gitHead": "9f7d766cb386b299d4098141f4a47d23e16975e3"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:22-alpine AS builder
|
||||
FROM node:22-alpine3.21 AS builder
|
||||
|
||||
# RUN apk add build-base
|
||||
# RUN wget -O - https://github.com/jemalloc/jemalloc/releases/download/5.3.0/jemalloc-5.3.0.tar.bz2 | tar -xj && \
|
||||
@@ -18,9 +18,14 @@ RUN npm install -g pnpm@10.33.4
|
||||
RUN cp /workspace/certd-client/dist/* /workspace/certd-server/public/ -rf
|
||||
RUN cd /workspace/certd-server && pnpm install && npm run build-on-docker
|
||||
|
||||
# RUN cd /workspace/certd-server && \
|
||||
# pnpm install --ignore-scripts && \
|
||||
# yes | pnpm approve-builds && \
|
||||
# pnpm rebuild && \
|
||||
# npm run build-on-docker
|
||||
|
||||
|
||||
FROM node:22-alpine
|
||||
FROM node:22-alpine3.21
|
||||
EXPOSE 7001
|
||||
EXPOSE 7002
|
||||
|
||||
|
||||
@@ -3,6 +3,21 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* cnameProvider域名支持设置子域名托管 ([7266af1](https://github.com/certd/certd/commit/7266af17491a98338022cfb18cfedfb93ca6ef8f))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **设置:** 添加首页启用开关配置 ([25ad1e6](https://github.com/certd/certd/commit/25ad1e6f861e43288cc8bd90d4903628e6faec29))
|
||||
* **用户资料:** 新增手机号邮箱绑定功能 ([e0eb0e2](https://github.com/certd/certd/commit/e0eb0e21f6dae24b639c944f9aba2c90496ab1c0))
|
||||
* 域名注册过期时间获取再次优化 ([91a1b97](https://github.com/certd/certd/commit/91a1b9755066bf280e194dabf7c3a9f936e2643f))
|
||||
* **证书流水线:** 添加批量更新证书申请参数功能 ([63be1c1](https://github.com/certd/certd/commit/63be1c1cbd9b09a3b48f26130c296b1cedcca1ac))
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
* **domain:** 添加域名过期时间同步进度显示功能 ([9d2937d](https://github.com/certd/certd/commit/9d2937dd4b14ffab73e9b096edd2aa8539811182))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-client",
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --open",
|
||||
@@ -12,7 +12,7 @@
|
||||
"debug:force": "vite --force --mode debug",
|
||||
"build": "cross-env NODE_OPTIONS=--max-old-space-size=40960 vite build ",
|
||||
"dev-build": "echo 1",
|
||||
"test:unit": "echo no unit tests",
|
||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||
"test:vue": "vitest run",
|
||||
"serve": "vite preview",
|
||||
"preview": "vite preview",
|
||||
@@ -62,7 +62,6 @@
|
||||
"cos-js-sdk-v5": "^1.7.0",
|
||||
"cron-parser": "^4.9.0",
|
||||
"cropperjs": "^1.6.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"cssnano": "^7.0.6",
|
||||
"dayjs": "^1.11.7",
|
||||
"defu": "^6.1.4",
|
||||
@@ -107,8 +106,8 @@
|
||||
"zod-defaults": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/lib-iframe": "^1.39.12",
|
||||
"@certd/pipeline": "^1.39.12",
|
||||
"@certd/lib-iframe": "^1.39.13",
|
||||
"@certd/pipeline": "^1.39.13",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/chai": "^4.3.12",
|
||||
@@ -127,6 +126,7 @@
|
||||
"autoprefixer": "^10.4.21",
|
||||
"caller-path": "^4.0.0",
|
||||
"chai": "^5.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"dependency-cruiser": "^16.2.3",
|
||||
"dot": "^1.1.3",
|
||||
"eslint": "8.57.0",
|
||||
@@ -136,6 +136,7 @@
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-vue": "^9.23.0",
|
||||
"esmock": "^2.7.5",
|
||||
"less": "^4.2.0",
|
||||
"less-loader": "^12.2.0",
|
||||
"postcss": "^8.4.35",
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div class="refresh-input">
|
||||
<div class="refresh-input-line">
|
||||
<a-input class="refresh-input-control" :value="value" :placeholder="placeholder" allow-clear @update:value="emit('update:value', $event)"></a-input>
|
||||
<fs-button :loading="loading" type="primary" :text="buttonText" :icon="icon" @click="doRefresh"></fs-button>
|
||||
</div>
|
||||
<div class="helper" :class="{ error: hasError }">
|
||||
{{ message }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ComponentPropsType, doRequest } from "/@/components/plugins/lib";
|
||||
import { computed, inject, ref } from "vue";
|
||||
import { Form } from "ant-design-vue";
|
||||
import { getInputFromForm } from "./utils";
|
||||
|
||||
defineOptions({
|
||||
name: "RefreshInput",
|
||||
});
|
||||
|
||||
type RefreshInputProps = ComponentPropsType & {
|
||||
buttonText?: string;
|
||||
icon?: string;
|
||||
placeholder?: string;
|
||||
successMessage?: string;
|
||||
};
|
||||
|
||||
const fromType: any = inject("getFromType");
|
||||
const getScope: any = inject("get:scope");
|
||||
const getPluginType: any = inject("get:plugin:type", () => {
|
||||
return "access";
|
||||
});
|
||||
const formItemContext = Form.useInjectFormItemContext();
|
||||
const props = defineProps<RefreshInputProps>();
|
||||
const emit = defineEmits<{
|
||||
"update:value": [value: string];
|
||||
}>();
|
||||
|
||||
const loading = ref(false);
|
||||
const message = ref("");
|
||||
const hasError = ref(false);
|
||||
|
||||
const action = computed(() => props.action);
|
||||
const buttonText = computed(() => props.buttonText || "刷新");
|
||||
const icon = computed(() => props.icon || "ion:refresh-outline");
|
||||
const placeholder = computed(() => props.placeholder || "");
|
||||
const successMessage = computed(() => props.successMessage || "刷新成功,请保存配置");
|
||||
|
||||
const doRefresh = async () => {
|
||||
if (loading.value) {
|
||||
return;
|
||||
}
|
||||
if (!action.value) {
|
||||
hasError.value = true;
|
||||
message.value = "缺少刷新动作配置";
|
||||
return;
|
||||
}
|
||||
|
||||
formItemContext.onFieldChange();
|
||||
|
||||
const { form } = getScope();
|
||||
const pluginType = getPluginType();
|
||||
const { input, record } = getInputFromForm(form, pluginType);
|
||||
|
||||
loading.value = true;
|
||||
message.value = "";
|
||||
hasError.value = false;
|
||||
try {
|
||||
const res = await doRequest(
|
||||
{
|
||||
type: pluginType,
|
||||
typeName: form.type,
|
||||
action: action.value,
|
||||
input,
|
||||
record,
|
||||
fromType,
|
||||
},
|
||||
{
|
||||
onError(err: any) {
|
||||
hasError.value = true;
|
||||
message.value = err.message;
|
||||
},
|
||||
showErrorNotify: false,
|
||||
}
|
||||
);
|
||||
emit("update:value", res);
|
||||
message.value = successMessage.value;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.refresh-input-line {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.refresh-input-control {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -12,6 +12,7 @@ import AccessSelector from "/@/views/certd/access/access-selector/index.vue";
|
||||
import InputPassword from "./common/input-password.vue";
|
||||
import CertInfoUpdater from "/@/views/certd/pipeline/cert-upload/index.vue";
|
||||
import ApiTest from "./common/api-test.vue";
|
||||
import RefreshInput from "./common/refresh-input.vue";
|
||||
import ParamsShow from "./common/params-show.vue";
|
||||
export * from "./cert/index.js";
|
||||
export default {
|
||||
@@ -23,6 +24,7 @@ export default {
|
||||
app.component("CertInfoUpdater", CertInfoUpdater);
|
||||
|
||||
app.component("ApiTest", ApiTest);
|
||||
app.component("RefreshInput", RefreshInput);
|
||||
|
||||
app.component("SynologyDeviceIdGetter", SynologyIdDeviceGetter);
|
||||
app.component("RemoteAutoComplete", RemoteAutoComplete);
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
export default {
|
||||
cnameTitle: "CNAME Service Configuration",
|
||||
cnameDescription:
|
||||
"The domain name configured here serves as a proxy for verifying other domains. When other domains apply for certificates, they map to this domain via CNAME for ownership verification. The advantage is that any domain can apply for a certificate this way without providing an AccessSecret.",
|
||||
"The domain name configured here serves as a proxy for verifying other domains. When other domains apply for certificates, they map to this domain via CNAME for ownership verification. The advantage is that any domain can apply for a certificate this way without providing an AccessSecret.",
|
||||
cnameLinkText: "CNAME principle and usage instructions",
|
||||
cnameDomain: "CNAME Domain",
|
||||
cnameDomainPlaceholder: "cname.handsfree.work",
|
||||
cnameDomainHelper:
|
||||
"Requires a domain registered with a DNS provider on the right (or you can transfer other domain DNS servers here).\nOnce the CNAME domain is set, it cannot be changed. It is recommended to use a first-level subdomain.",
|
||||
"Requires a domain registered with a DNS provider on the right (or you can transfer other domain DNS servers here).\nOnce the CNAME domain is set, it cannot be changed. It is recommended to use a first-level subdomain.",
|
||||
cnameDomainPattern: "Domain name cannot contain *",
|
||||
cnameProviderSubdomain: "Delegated Subdomain",
|
||||
cnameProviderSubdomainPlaceholder: "sub.example.com",
|
||||
cnameProviderSubdomainHelper: "Fill this when the CNAME domain is hosted under a delegated subdomain, for example CNAME domain cname.sub.example.com and DNS zone sub.example.com.",
|
||||
dnsProvider: "DNS Provider",
|
||||
dnsProviderAuthorization: "DNS Provider Authorization",
|
||||
};
|
||||
|
||||
@@ -6,6 +6,9 @@ export default {
|
||||
cnameDomainPlaceholder: "cname.handsfree.work",
|
||||
cnameDomainHelper: "需要一个右边DNS提供商注册的域名(也可以将其他域名的dns服务器转移到这几家来)。\nCNAME域名一旦确定不可修改,建议使用一级子域名",
|
||||
cnameDomainPattern: "域名不能使用星号",
|
||||
cnameProviderSubdomain: "托管子域名",
|
||||
cnameProviderSubdomainPlaceholder: "sub.example.com",
|
||||
cnameProviderSubdomainHelper: "当CNAME域名本身托管在子域名下时填写,例如 CNAME域名为 cname.sub.example.com,实际DNS托管域为 sub.example.com",
|
||||
dnsProvider: "DNS提供商",
|
||||
dnsProviderAuthorization: "DNS提供商授权",
|
||||
};
|
||||
|
||||
@@ -23,6 +23,37 @@ export async function UpdateProfile(form: any) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetContactCapability() {
|
||||
return await request({
|
||||
url: "/mine/contact/capability",
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
|
||||
export async function UpdateMobile(form: any) {
|
||||
return await request({
|
||||
url: "/mine/contact/mobile",
|
||||
method: "POST",
|
||||
data: form,
|
||||
});
|
||||
}
|
||||
|
||||
export async function VerifyContactIdentity(form: any) {
|
||||
return await request({
|
||||
url: "/mine/contact/verifyIdentity",
|
||||
method: "POST",
|
||||
data: form,
|
||||
});
|
||||
}
|
||||
|
||||
export async function UpdateEmail(form: any) {
|
||||
return await request({
|
||||
url: "/mine/contact/email",
|
||||
method: "POST",
|
||||
data: form,
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetOauthBounds() {
|
||||
return await request({
|
||||
url: "/oauth/bounds",
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { defineComponent } from "vue";
|
||||
import SmsCode from "/@/views/framework/login/sms-code.vue";
|
||||
import EmailCode from "/@/views/framework/register/email-code.vue";
|
||||
|
||||
export const ContactCodeInput = defineComponent({
|
||||
name: "ContactCodeInput",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
form: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, { emit }) {
|
||||
const onChange = (value: string) => emit("update:modelValue", value);
|
||||
return () => {
|
||||
if (props.type === "email") {
|
||||
return <EmailCode value={props.modelValue} captcha={props.form.contactCaptcha} email={props.form.email} verificationType="bindEmail" onUpdate:value={onChange} />;
|
||||
}
|
||||
return <SmsCode value={props.modelValue} captcha={props.form.contactCaptcha} mobile={props.form.mobile} phoneCode={props.form.phoneCode} verificationType="bindMobile" onUpdate:value={onChange} />;
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
import { defineComponent } from "vue";
|
||||
import SmsCode from "/@/views/framework/login/sms-code.vue";
|
||||
import EmailCode from "/@/views/framework/register/email-code.vue";
|
||||
|
||||
export const IdentityCodeInput = defineComponent({
|
||||
name: "IdentityCodeInput",
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
form: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
userInfo: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, { emit }) {
|
||||
const onChange = (value: string) => emit("update:modelValue", value);
|
||||
return () => {
|
||||
if (props.form.identityType === "email") {
|
||||
return <EmailCode value={props.modelValue} captcha={props.form.identityCaptcha} email={props.userInfo.email} verificationType="contactIdentity" onUpdate:value={onChange} />;
|
||||
}
|
||||
return (
|
||||
<SmsCode value={props.modelValue} captcha={props.form.identityCaptcha} mobile={props.userInfo.mobile} phoneCode={props.userInfo.phoneCode || "86"} verificationType="contactIdentity" onUpdate:value={onChange} />
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
@@ -1,20 +1,22 @@
|
||||
// useUserProfile, 获取 openEditProfileDialog ,参考 useTemplate方法
|
||||
import { useFormWrapper } from "@fast-crud/fast-crud";
|
||||
import { ref } from "vue";
|
||||
import { cloneDeep, merge } from "lodash-es";
|
||||
import { compute, dict } from "@fast-crud/fast-crud";
|
||||
|
||||
// 假设的 API 导入
|
||||
import * as userProfileApi from "./api";
|
||||
import { useUserStore } from "/@/store/user";
|
||||
import { useI18n } from "/src/locales";
|
||||
import CaptchaInput from "/@/components/captcha/captcha-input.vue";
|
||||
import { message } from "ant-design-vue";
|
||||
import { ContactCodeInput } from "./contact-code-input";
|
||||
import { IdentityCodeInput } from "./identity-code-input";
|
||||
import { useFormDialog } from "/@/use/use-dialog";
|
||||
|
||||
/**
|
||||
* 获取用户资料编辑相关功能
|
||||
* @returns {{openEditProfileDialog: openEditProfileDialog}}
|
||||
*/
|
||||
export function useUserProfile() {
|
||||
const { openCrudFormDialog } = useFormWrapper();
|
||||
const wrapperRef = ref();
|
||||
const { openFormDialog } = useFormDialog();
|
||||
async function openEditProfileDialog(req: { onUpdated?: (ctx: any) => void }) {
|
||||
const detail = await userProfileApi.getMineInfo();
|
||||
if (!detail) {
|
||||
@@ -24,31 +26,28 @@ export function useUserProfile() {
|
||||
const { t } = useI18n();
|
||||
|
||||
const userStore = useUserStore();
|
||||
const userProfileFormRef = ref();
|
||||
async function doSubmit(opts: { form: any }) {
|
||||
const form = opts.form;
|
||||
async function doSubmit(form: any) {
|
||||
const { id } = await userProfileApi.UpdateProfile(form);
|
||||
if (req.onUpdated) {
|
||||
req.onUpdated({ id });
|
||||
}
|
||||
}
|
||||
|
||||
const crudOptions: any = {
|
||||
form: {
|
||||
doSubmit,
|
||||
wrapper: {
|
||||
title: `编辑用户资料`,
|
||||
width: 1100,
|
||||
onOpened(opts: { form: any }) {
|
||||
merge(opts.form, detail);
|
||||
},
|
||||
},
|
||||
await openFormDialog({
|
||||
title: `编辑用户资料`,
|
||||
wrapper: {
|
||||
width: 600,
|
||||
},
|
||||
initialForm: detail,
|
||||
onSubmit: doSubmit,
|
||||
columns: {
|
||||
nickName: {
|
||||
title: t("certd.nickName"),
|
||||
type: "text",
|
||||
form: {
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
component: {
|
||||
placeholder: t("certd.nickName"),
|
||||
},
|
||||
@@ -71,6 +70,9 @@ export function useUserProfile() {
|
||||
},
|
||||
},
|
||||
form: {
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
component: {
|
||||
vModel: "modelValue",
|
||||
valueType: "key",
|
||||
@@ -98,10 +100,7 @@ export function useUserProfile() {
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = await openCrudFormDialog({ crudOptions });
|
||||
wrapperRef.value = wrapper;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -110,26 +109,20 @@ export function useUserProfile() {
|
||||
}
|
||||
|
||||
export function usePasskeyRegister() {
|
||||
const { openCrudFormDialog } = useFormWrapper();
|
||||
const wrapperRef = ref();
|
||||
const { openFormDialog } = useFormDialog();
|
||||
async function openRegisterDialog(req: { onSubmit?: (ctx: any) => void }) {
|
||||
const { t } = useI18n();
|
||||
|
||||
const userStore = useUserStore();
|
||||
const deviceNameRef = ref();
|
||||
|
||||
const crudOptions: any = {
|
||||
form: {
|
||||
wrapper: {
|
||||
title: t("authentication.registerPasskey"),
|
||||
width: 500,
|
||||
onOpened(opts: { form: any }) {
|
||||
opts.form.deviceName = "";
|
||||
},
|
||||
},
|
||||
onSubmit: req.onSubmit,
|
||||
afterSubmit: null,
|
||||
onSuccess: null,
|
||||
await openFormDialog({
|
||||
title: t("authentication.registerPasskey"),
|
||||
wrapper: {
|
||||
width: 500,
|
||||
},
|
||||
initialForm: {
|
||||
deviceName: "",
|
||||
},
|
||||
onSubmit: async (form: any) => {
|
||||
await req.onSubmit?.({ form });
|
||||
},
|
||||
columns: {
|
||||
deviceName: {
|
||||
@@ -147,15 +140,229 @@ export function usePasskeyRegister() {
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = await openCrudFormDialog({ crudOptions });
|
||||
wrapperRef.value = wrapper;
|
||||
|
||||
return wrapper;
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
openRegisterDialog,
|
||||
};
|
||||
}
|
||||
|
||||
export function useContactBind() {
|
||||
const { openFormDialog } = useFormDialog();
|
||||
|
||||
async function openContactBindDialog(req: { type: "mobile" | "email"; userInfo: any; contactCapability: { smsEnabled?: boolean }; onUpdated?: () => Promise<void> | void }) {
|
||||
const methods = [{ label: "密码", value: "password" }];
|
||||
if (req.userInfo.email) {
|
||||
methods.push({ label: "邮箱", value: "email" });
|
||||
}
|
||||
if (req.contactCapability.smsEnabled && req.userInfo.mobile) {
|
||||
methods.push({ label: "手机号", value: "mobile" });
|
||||
}
|
||||
|
||||
async function openChangeDialog(identityValidationCode: string) {
|
||||
const isMobile = req.type === "mobile";
|
||||
await openFormDialog({
|
||||
title: isMobile ? (req.userInfo.mobile ? "修改手机号" : "绑定手机号") : req.userInfo.email ? "修改邮箱" : "绑定邮箱",
|
||||
wrapper: {
|
||||
width: 560,
|
||||
},
|
||||
initialForm: {
|
||||
phoneCode: req.userInfo.phoneCode || "86",
|
||||
mobile: req.userInfo.mobile || "",
|
||||
email: req.userInfo.email || "",
|
||||
contactCaptcha: null,
|
||||
contactValidateCode: "",
|
||||
},
|
||||
async onSubmit(form: any) {
|
||||
if (isMobile) {
|
||||
await userProfileApi.UpdateMobile({
|
||||
phoneCode: form.phoneCode,
|
||||
mobile: form.mobile,
|
||||
validateCode: form.contactValidateCode,
|
||||
identityValidationCode,
|
||||
});
|
||||
} else {
|
||||
await userProfileApi.UpdateEmail({
|
||||
email: form.email,
|
||||
validateCode: form.contactValidateCode,
|
||||
identityValidationCode,
|
||||
});
|
||||
}
|
||||
message.success("绑定信息已更新");
|
||||
await req.onUpdated?.();
|
||||
},
|
||||
columns: {
|
||||
phoneCode: {
|
||||
title: "区号",
|
||||
type: "text",
|
||||
form: {
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
show: isMobile,
|
||||
component: {
|
||||
placeholder: "区号",
|
||||
},
|
||||
rules: [{ required: isMobile, message: "请输入区号" }],
|
||||
},
|
||||
},
|
||||
mobile: {
|
||||
title: "手机号",
|
||||
type: "text",
|
||||
form: {
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
show: isMobile,
|
||||
component: {
|
||||
placeholder: "请输入手机号",
|
||||
},
|
||||
rules: [
|
||||
{ required: isMobile, message: "请输入手机号" },
|
||||
{ pattern: /^\d{4,20}$/, message: "请输入正确的手机号" },
|
||||
],
|
||||
},
|
||||
},
|
||||
email: {
|
||||
title: "邮箱",
|
||||
type: "text",
|
||||
form: {
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
show: !isMobile,
|
||||
component: {
|
||||
placeholder: "请输入邮箱",
|
||||
},
|
||||
rules: [
|
||||
{ required: !isMobile, message: "请输入邮箱" },
|
||||
{ type: "email", message: "请输入正确的邮箱" },
|
||||
],
|
||||
},
|
||||
},
|
||||
contactCaptcha: {
|
||||
title: "图形验证码",
|
||||
form: {
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
component: {
|
||||
name: CaptchaInput,
|
||||
vModel: "modelValue",
|
||||
},
|
||||
rules: [{ required: true, message: "请完成图形验证码" }],
|
||||
},
|
||||
},
|
||||
contactValidateCode: {
|
||||
title: isMobile ? "新手机号验证码" : "新邮箱验证码",
|
||||
form: {
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
component: {
|
||||
name: ContactCodeInput,
|
||||
vModel: "modelValue",
|
||||
form: compute(({ form }) => form),
|
||||
type: req.type,
|
||||
},
|
||||
rules: [{ required: true, message: "请输入验证码" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
await openFormDialog({
|
||||
title: "验证本人操作",
|
||||
wrapper: {
|
||||
width: 520,
|
||||
},
|
||||
initialForm: {
|
||||
identityType: "password",
|
||||
identityPassword: "",
|
||||
identityCaptcha: null,
|
||||
identityValidateCode: "",
|
||||
},
|
||||
async onSubmit(form: any) {
|
||||
const res = await userProfileApi.VerifyContactIdentity({
|
||||
identityType: form.identityType,
|
||||
identityPassword: form.identityPassword,
|
||||
identityValidateCode: form.identityValidateCode,
|
||||
});
|
||||
await openChangeDialog(res.validationCode);
|
||||
},
|
||||
columns: {
|
||||
identityType: {
|
||||
title: "验证方式",
|
||||
form: {
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
component: {
|
||||
name: "fs-dict-radio",
|
||||
vModel: "value",
|
||||
dict: dict({
|
||||
data: methods,
|
||||
}),
|
||||
},
|
||||
rules: [{ required: true, message: "请选择验证方式" }],
|
||||
valueChange({ form }: { form: any }) {
|
||||
form.identityPassword = "";
|
||||
form.identityCaptcha = null;
|
||||
form.identityValidateCode = "";
|
||||
},
|
||||
},
|
||||
},
|
||||
identityPassword: {
|
||||
title: "登录密码",
|
||||
type: "password",
|
||||
form: {
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
show: compute(({ form }) => form.identityType === "password"),
|
||||
component: {
|
||||
placeholder: "请输入登录密码",
|
||||
},
|
||||
rules: [{ required: true, message: "请输入登录密码" }],
|
||||
},
|
||||
},
|
||||
identityCaptcha: {
|
||||
title: "图形验证码",
|
||||
form: {
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
show: compute(({ form }) => form.identityType !== "password"),
|
||||
component: {
|
||||
name: CaptchaInput,
|
||||
vModel: "modelValue",
|
||||
},
|
||||
rules: [{ required: true, message: "请完成图形验证码" }],
|
||||
},
|
||||
},
|
||||
identityValidateCode: {
|
||||
title: "验证码",
|
||||
form: {
|
||||
col: {
|
||||
span: 24,
|
||||
},
|
||||
show: compute(({ form }) => form.identityType !== "password"),
|
||||
component: {
|
||||
name: IdentityCodeInput,
|
||||
vModel: "modelValue",
|
||||
form: compute(({ form }) => form),
|
||||
userInfo: req.userInfo,
|
||||
},
|
||||
rules: [{ required: true, message: "请输入验证码" }],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
openContactBindDialog,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,32 +13,41 @@
|
||||
<a-avatar v-else size="100" class="user-avatar default-avatar">
|
||||
{{ userInfo.username }}
|
||||
</a-avatar>
|
||||
<a-button type="text" size="small" class="avatar-edit-btn" title="修改资料" @click.stop="doUpdate">
|
||||
<template #icon><fs-icon icon="ion:create-outline" /></template>
|
||||
</a-button>
|
||||
<!-- <div class="status-indicator"></div> -->
|
||||
</div>
|
||||
<div class="user-info">
|
||||
<h2 class="user-name flex items-center">
|
||||
{{ userInfo.nickName }}
|
||||
<fs-values-format :model-value="userInfo.roleIds" :dict="roleDict" color="blue" />
|
||||
<span>{{ userInfo.nickName }}</span>
|
||||
<a-button type="text" size="small" class="detail-edit-btn" title="修改资料" @click.stop="doUpdate">
|
||||
<template #icon><fs-icon icon="ion:create-outline" /></template>
|
||||
</a-button>
|
||||
</h2>
|
||||
<div class="user-details">
|
||||
<a-tag color="blue" class="detail-tag">
|
||||
<span class="tag-icon">👤</span>
|
||||
{{ userInfo.username }}
|
||||
<span class="tag-text">{{ userInfo.username }}</span>
|
||||
<fs-values-format :model-value="userInfo.roleIds" type="text" :dict="roleDict" color="blue" />
|
||||
</a-tag>
|
||||
<a-tag v-if="userInfo.email" color="green" class="detail-tag">
|
||||
<a-tag color="green" class="detail-tag">
|
||||
<span class="tag-icon">📧</span>
|
||||
{{ userInfo.email }}
|
||||
<span class="tag-text">{{ userInfo.email || "未绑定邮箱" }}</span>
|
||||
<a-button type="text" size="small" class="detail-edit-btn" title="修改邮箱" @click.stop="openBindContact('email')">
|
||||
<template #icon><fs-icon icon="ion:create-outline" /></template>
|
||||
</a-button>
|
||||
</a-tag>
|
||||
<a-tag v-if="userInfo.mobile" color="purple" class="detail-tag">
|
||||
<a-tag v-if="contactCapability.smsEnabled" color="purple" class="detail-tag">
|
||||
<span class="tag-icon">📱</span>
|
||||
{{ userInfo.mobile }}
|
||||
<span class="tag-text">{{ userInfo.mobile || "未绑定手机号" }}</span>
|
||||
<a-button type="text" size="small" class="detail-edit-btn" title="修改手机号" @click.stop="openBindContact('mobile')">
|
||||
<template #icon><fs-icon icon="ion:create-outline" /></template>
|
||||
</a-button>
|
||||
</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-buttons gap-2">
|
||||
<a-button type="primary" class="action-btn" @click="doUpdate">
|
||||
{{ t("authentication.updateProfile") }}
|
||||
</a-button>
|
||||
<change-password-button :show-button="true" />
|
||||
|
||||
<a-button type="primary" class="action-btn" @click="goSecuritySetting">
|
||||
@@ -139,7 +148,7 @@ import * as api from "./api";
|
||||
import { computed, onMounted, Ref, ref } from "vue";
|
||||
import ChangePasswordButton from "/@/views/certd/mine/change-password-button.vue";
|
||||
import { useI18n } from "/src/locales";
|
||||
import { useUserProfile } from "./use";
|
||||
import { useContactBind, useUserProfile } from "./use";
|
||||
import { usePasskeyRegister } from "./use";
|
||||
import { message, Modal, notification } from "ant-design-vue";
|
||||
import { useSettingStore } from "/@/store/settings";
|
||||
@@ -160,6 +169,9 @@ const settingStore = useSettingStore();
|
||||
const userInfo: Ref = ref({});
|
||||
const passkeys = ref([]);
|
||||
const passkeySupported = ref(false);
|
||||
const contactCapability = ref({
|
||||
smsEnabled: false,
|
||||
});
|
||||
|
||||
const getUserInfo = async () => {
|
||||
userInfo.value = await api.getMineInfo();
|
||||
@@ -177,6 +189,7 @@ function doUpdate() {
|
||||
openEditProfileDialog({
|
||||
onUpdated: async () => {
|
||||
await getUserInfo();
|
||||
userStore.setUserInfo(userInfo.value);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -237,6 +250,23 @@ async function loadPasskeys() {
|
||||
}
|
||||
}
|
||||
|
||||
async function loadContactCapability() {
|
||||
contactCapability.value = await api.GetContactCapability();
|
||||
}
|
||||
|
||||
const { openContactBindDialog } = useContactBind();
|
||||
async function openBindContact(type: "mobile" | "email") {
|
||||
await openContactBindDialog({
|
||||
type,
|
||||
userInfo: userInfo.value,
|
||||
contactCapability: contactCapability.value,
|
||||
onUpdated: async () => {
|
||||
await getUserInfo();
|
||||
userStore.setUserInfo(userInfo.value);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async function unbindPasskey(id: number) {
|
||||
Modal.confirm({
|
||||
title: "确认解绑吗?",
|
||||
@@ -366,6 +396,7 @@ const userAvatar = computed(() => {
|
||||
|
||||
onMounted(async () => {
|
||||
await getUserInfo();
|
||||
await loadContactCapability();
|
||||
await loadOauthBounds();
|
||||
await loadOauthProviders();
|
||||
await loadPasskeys();
|
||||
@@ -567,6 +598,24 @@ onMounted(async () => {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.avatar-edit-btn {
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
bottom: 2px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
min-width: 28px;
|
||||
padding: 0;
|
||||
color: #667eea;
|
||||
background: #ffffff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
border: 4px solid #ffffff;
|
||||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
|
||||
@@ -613,6 +662,18 @@ onMounted(async () => {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.detail-edit-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
min-width: 20px;
|
||||
margin: -2px -6px -2px 0;
|
||||
padding: 0;
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.tag-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -863,18 +924,84 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.card-header {
|
||||
padding: 32px 16px;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.avatar-wrapper {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 88px !important;
|
||||
height: 88px !important;
|
||||
line-height: 88px !important;
|
||||
}
|
||||
|
||||
.avatar-edit-btn {
|
||||
right: -2px;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
justify-content: center;
|
||||
margin-bottom: 14px;
|
||||
font-size: 22px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.user-details {
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.detail-tag {
|
||||
justify-content: flex-start;
|
||||
width: min(230px, calc(100vw - 96px));
|
||||
min-height: 34px;
|
||||
padding: 5px 8px 5px 12px;
|
||||
border-radius: 12px;
|
||||
white-space: nowrap;
|
||||
margin-right: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tag-icon {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.tag-text {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.detail-edit-btn {
|
||||
margin: 0 -4px 0 2px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
gap: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.action-buttons :deep(.ant-btn) {
|
||||
min-width: 90px;
|
||||
height: 32px;
|
||||
padding: 4px 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,21 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
width: 200,
|
||||
},
|
||||
},
|
||||
subdomain: {
|
||||
title: t("certd.cnameProviderSubdomain"),
|
||||
type: "text",
|
||||
form: {
|
||||
component: {
|
||||
placeholder: t("certd.cnameProviderSubdomainPlaceholder"),
|
||||
},
|
||||
helper: t("certd.cnameProviderSubdomainHelper"),
|
||||
rules: [{ pattern: /^[^*]+$/, message: t("certd.cnameDomainPattern") }],
|
||||
},
|
||||
column: {
|
||||
width: 200,
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
dnsProviderType: {
|
||||
title: t("certd.dnsProvider"),
|
||||
type: "dict-select",
|
||||
|
||||
@@ -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.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **aliyun-access:** 添加阿里云密钥校验失败的错误处理 ([b75c625](https://github.com/certd/certd/commit/b75c625ddcc0b3110699d8e6175681ef157b25df))
|
||||
* cnameProvider域名支持设置子域名托管 ([7266af1](https://github.com/certd/certd/commit/7266af17491a98338022cfb18cfedfb93ca6ef8f))
|
||||
* **plugin-aliyun:** 过滤非CAS证书并优化日志信息 ([c4b01da](https://github.com/certd/certd/commit/c4b01da384bc40a241a673ea8bc01ca733c04d83))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **用户资料:** 新增手机号邮箱绑定功能 ([e0eb0e2](https://github.com/certd/certd/commit/e0eb0e21f6dae24b639c944f9aba2c90496ab1c0))
|
||||
* 域名注册过期时间获取再次优化 ([91a1b97](https://github.com/certd/certd/commit/91a1b9755066bf280e194dabf7c3a9f936e2643f))
|
||||
* **证书流水线:** 添加批量更新证书申请参数功能 ([63be1c1](https://github.com/certd/certd/commit/63be1c1cbd9b09a3b48f26130c296b1cedcca1ac))
|
||||
* 支持火山云vke ([bb46cb0](https://github.com/certd/certd/commit/bb46cb08f71f6ae921543f7e4a6c5f4e0190556e))
|
||||
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
|
||||
* **plugin-volcengine:** 支持火山引擎VKE部署插件 ([b8a64a6](https://github.com/certd/certd/commit/b8a64a6b5bf3691a47177de42bc49b798e795feb))
|
||||
|
||||
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE cd_cname_provider ADD COLUMN subdomain varchar(100);
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE cd_cname_provider ADD COLUMN subdomain varchar(100);
|
||||
@@ -0,0 +1 @@
|
||||
ALTER TABLE cd_cname_provider ADD COLUMN subdomain varchar(100);
|
||||
@@ -3,6 +3,26 @@ title: EAB授权
|
||||
desc: ZeroSSL证书申请需要EAB授权
|
||||
icon: ic:outline-lock
|
||||
input:
|
||||
eabType:
|
||||
title: EAB类型
|
||||
component:
|
||||
name: a-select
|
||||
options:
|
||||
- value: google
|
||||
label: Google
|
||||
icon: flat-color-icons:google
|
||||
- value: zerossl
|
||||
label: ZeroSSL
|
||||
icon: emojione:digit-zero
|
||||
- value: litessl
|
||||
label: litessl
|
||||
icon: roentgen:free
|
||||
- value: sslcom
|
||||
label: SSL.com
|
||||
icon: la:expeditedssl
|
||||
helper: 请选择EAB类型
|
||||
required: true
|
||||
encrypt: false
|
||||
kid:
|
||||
title: KID
|
||||
component:
|
||||
@@ -24,8 +44,20 @@ input:
|
||||
rules:
|
||||
- type: email
|
||||
message: 请输入正确的邮箱
|
||||
helper: Google的EAB申请证书,更换邮箱会导致EAB失效,可以在此处绑定一个邮箱避免此问题
|
||||
helper: 绑定一个邮箱,避免失效
|
||||
required: true
|
||||
accountKey:
|
||||
title: ACME账号私钥
|
||||
component:
|
||||
name: refresh-input
|
||||
action: GenerateAccountKey
|
||||
buttonText: 生成
|
||||
successMessage: 账号私钥已生成,请保存授权配置
|
||||
required: true
|
||||
helper: |-
|
||||
如果修改了KID,请点击生成重新生成账号私钥
|
||||
注意:google的EAB只能生成一次账号私钥,更新私钥需要获取一个新的EAB授权
|
||||
encrypt: true
|
||||
pluginType: access
|
||||
type: builtIn
|
||||
scriptFilePath: /plugins/plugin-cert/access/eab-access.js
|
||||
|
||||
@@ -8,6 +8,7 @@ desc: 根据证书id一键更新腾讯云证书并自动部署(Id不变),
|
||||
icon: svg:icon-tencentcloud
|
||||
group: tencent
|
||||
needPlus: false
|
||||
deprecated: 腾讯更新证书(Id不变)接口已失效,本插件已下架,请使用其他接口
|
||||
input:
|
||||
cert:
|
||||
title: 域名证书
|
||||
|
||||
@@ -68,12 +68,12 @@ input:
|
||||
component:
|
||||
name: remote-select
|
||||
vModel: value
|
||||
mode: tags
|
||||
mode: default
|
||||
type: plugin
|
||||
action: onGetClusterList
|
||||
search: false
|
||||
pager: false
|
||||
multi: true
|
||||
multi: false
|
||||
watches:
|
||||
- certDomains
|
||||
- accessId
|
||||
@@ -102,8 +102,6 @@ input:
|
||||
value: Public
|
||||
- label: 私网
|
||||
value: Private
|
||||
- label: 集群内
|
||||
value: TargetCluster
|
||||
value: Public
|
||||
required: true
|
||||
order: 0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-server",
|
||||
"version": "1.39.12",
|
||||
"version": "1.39.13",
|
||||
"description": "fast-server base midway",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
@@ -53,20 +53,20 @@
|
||||
"@aws-sdk/client-sts": "^3.990.0",
|
||||
"@azure/arm-dns": "^5.1.0",
|
||||
"@azure/identity": "^4.13.1",
|
||||
"@certd/acme-client": "^1.39.12",
|
||||
"@certd/basic": "^1.39.12",
|
||||
"@certd/commercial-core": "^1.39.12",
|
||||
"@certd/acme-client": "^1.39.13",
|
||||
"@certd/basic": "^1.39.13",
|
||||
"@certd/commercial-core": "^1.39.13",
|
||||
"@certd/cv4pve-api-javascript": "^8.4.2",
|
||||
"@certd/jdcloud": "^1.39.12",
|
||||
"@certd/lib-huawei": "^1.39.12",
|
||||
"@certd/lib-k8s": "^1.39.12",
|
||||
"@certd/lib-server": "^1.39.12",
|
||||
"@certd/midway-flyway-js": "^1.39.12",
|
||||
"@certd/pipeline": "^1.39.12",
|
||||
"@certd/plugin-cert": "^1.39.12",
|
||||
"@certd/plugin-lib": "^1.39.12",
|
||||
"@certd/plugin-plus": "^1.39.12",
|
||||
"@certd/plus-core": "^1.39.12",
|
||||
"@certd/jdcloud": "^1.39.13",
|
||||
"@certd/lib-huawei": "^1.39.13",
|
||||
"@certd/lib-k8s": "^1.39.13",
|
||||
"@certd/lib-server": "^1.39.13",
|
||||
"@certd/midway-flyway-js": "^1.39.13",
|
||||
"@certd/pipeline": "^1.39.13",
|
||||
"@certd/plugin-cert": "^1.39.13",
|
||||
"@certd/plugin-lib": "^1.39.13",
|
||||
"@certd/plugin-plus": "^1.39.13",
|
||||
"@certd/plus-core": "^1.39.13",
|
||||
"@google-cloud/dns": "^5.3.1",
|
||||
"@google-cloud/publicca": "^1.3.0",
|
||||
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.185",
|
||||
@@ -101,7 +101,6 @@
|
||||
"cache-manager": "^6.1.0",
|
||||
"cos-nodejs-sdk-v5": "^2.14.6",
|
||||
"cron-parser": "^4.9.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"esdk-obs-nodejs": "^3.25.6",
|
||||
@@ -159,6 +158,8 @@
|
||||
"@types/node": "^18",
|
||||
"@types/nodemailer": "^6.4.8",
|
||||
"c8": "^10.1.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"esmock": "^2.7.5",
|
||||
"mocha": "^10.2.0",
|
||||
"prettier": "^2.8.8",
|
||||
"rimraf": "^5.0.5",
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { BaseController, Constants } from '@certd/lib-server';
|
||||
import { BaseController, Constants, SysSettingsService } from '@certd/lib-server';
|
||||
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
|
||||
import { PasskeyService } from '../../../modules/login/service/passkey-service.js';
|
||||
import { RoleService } from '../../../modules/sys/authority/service/role-service.js';
|
||||
import { UserService } from '../../../modules/sys/authority/service/user-service.js';
|
||||
import { ApiTags } from '@midwayjs/swagger';
|
||||
import { CodeService } from '../../../modules/basic/service/code-service.js';
|
||||
|
||||
/**
|
||||
*/
|
||||
@@ -20,8 +21,13 @@ export class MineController extends BaseController {
|
||||
@Inject()
|
||||
passkeyService: PasskeyService;
|
||||
|
||||
@Inject()
|
||||
codeService: CodeService;
|
||||
|
||||
@Post('/info', { description: Constants.per.authOnly, summary: "查询用户信息" })
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@Post('/info', { description: Constants.per.authOnly, summary: '查询用户信息' })
|
||||
public async info() {
|
||||
const userId = this.getUserId();
|
||||
const user = await this.userService.info(userId);
|
||||
@@ -35,21 +41,75 @@ export class MineController extends BaseController {
|
||||
return this.ok(user);
|
||||
}
|
||||
|
||||
@Post('/changePassword', { description: Constants.per.authOnly, summary: "修改密码" })
|
||||
@Post('/changePassword', { description: Constants.per.authOnly, summary: '修改密码' })
|
||||
public async changePassword(@Body(ALL) body: any) {
|
||||
const userId = this.getUserId();
|
||||
await this.userService.changePassword(userId, body);
|
||||
return this.ok({});
|
||||
}
|
||||
|
||||
@Post('/updateProfile', { description: Constants.per.authOnly, summary: "更新用户资料" })
|
||||
@Post('/updateProfile', { description: Constants.per.authOnly, summary: '更新用户资料' })
|
||||
public async updateProfile(@Body(ALL) body: any) {
|
||||
const userId = this.getUserId();
|
||||
|
||||
|
||||
await this.userService.updateProfile(userId, {
|
||||
avatar: body.avatar,
|
||||
nickName: body.nickName,
|
||||
});
|
||||
return this.ok({});
|
||||
}
|
||||
|
||||
@Post('/contact/capability', { description: Constants.per.authOnly, summary: '查询联系方式绑定能力' })
|
||||
public async contactCapability() {
|
||||
const settings = await this.sysSettingsService.getPrivateSettings();
|
||||
return this.ok({
|
||||
smsEnabled: !!settings.sms?.config?.accessId,
|
||||
});
|
||||
}
|
||||
|
||||
@Post('/contact/verifyIdentity', { description: Constants.per.authOnly, summary: '验证本人操作' })
|
||||
public async verifyContactIdentity(@Body(ALL) body: { identityType: 'password' | 'email' | 'mobile'; identityPassword?: string; identityValidateCode?: string }) {
|
||||
const userId = this.getUserId();
|
||||
await this.userService.verifyIdentity(userId, body, this.codeService);
|
||||
const validationCode = this.codeService.setValidationValue({
|
||||
type: 'contactIdentity',
|
||||
userId,
|
||||
identityType: body.identityType,
|
||||
});
|
||||
return this.ok({ validationCode });
|
||||
}
|
||||
|
||||
@Post('/contact/mobile', { description: Constants.per.authOnly, summary: '绑定或修改手机号' })
|
||||
public async updateMobile(@Body(ALL) body: { phoneCode?: string; mobile: string; validateCode: string; identityValidationCode: string }) {
|
||||
const userId = this.getUserId();
|
||||
this.userService.checkContactIdentityValidation(userId, body.identityValidationCode, this.codeService);
|
||||
await this.codeService.checkSmsCode({
|
||||
mobile: body.mobile,
|
||||
phoneCode: body.phoneCode || '86',
|
||||
smsCode: body.validateCode,
|
||||
verificationType: 'bindMobile',
|
||||
throwError: true,
|
||||
});
|
||||
await this.userService.updateMobile(userId, {
|
||||
phoneCode: body.phoneCode,
|
||||
mobile: body.mobile,
|
||||
});
|
||||
return this.ok({});
|
||||
}
|
||||
|
||||
@Post('/contact/email', { description: Constants.per.authOnly, summary: '绑定或修改邮箱' })
|
||||
public async updateEmail(@Body(ALL) body: { email: string; validateCode: string; identityValidationCode: string }) {
|
||||
const userId = this.getUserId();
|
||||
this.userService.checkContactIdentityValidation(userId, body.identityValidationCode, this.codeService);
|
||||
this.codeService.checkEmailCode({
|
||||
email: body.email,
|
||||
validateCode: body.validateCode,
|
||||
verificationType: 'bindEmail',
|
||||
throwError: true,
|
||||
});
|
||||
await this.userService.updateEmail(userId, {
|
||||
email: body.email,
|
||||
});
|
||||
return this.ok({});
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -1,7 +1,7 @@
|
||||
import { logger } from '@certd/basic';
|
||||
import { SysSettingsService, SysSiteInfo } from '@certd/lib-server';
|
||||
import { getPlusInfo, isPlus } from "@certd/plus-core";
|
||||
import { Autoload, Config, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import dayjs from "dayjs";
|
||||
import { Between } from "typeorm";
|
||||
import { DomainService } from '../cert/service/domain-service.js';
|
||||
@@ -14,9 +14,9 @@ import { PipelineService } from '../pipeline/service/pipeline-service.js';
|
||||
import { UserService } from "../sys/authority/service/user-service.js";
|
||||
import { ProjectService } from '../sys/enterprise/service/project-service.js';
|
||||
|
||||
@Autoload()
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class AutoCRegisterCron {
|
||||
export class AutoCron {
|
||||
@Inject()
|
||||
pipelineService: PipelineService;
|
||||
|
||||
@@ -53,7 +53,6 @@ export class AutoCRegisterCron {
|
||||
|
||||
|
||||
|
||||
@Init()
|
||||
async init() {
|
||||
logger.info('加载定时trigger开始');
|
||||
await this.pipelineService.onStartup(this.immediateTriggerOnce, this.onlyAdminUser);
|
||||
@@ -0,0 +1,182 @@
|
||||
import assert from "assert";
|
||||
import esmock from "esmock";
|
||||
import { AutoFix, buildEabAccountKeyValue, buildLegacyGoogleAccountConfigWhere, parseStorageValue } from "./auto-fix.js";
|
||||
|
||||
function createAutoFix(options: { pluginConfigService?: any; accessService?: any; storageService?: any }) {
|
||||
const autoFix = new AutoFix();
|
||||
autoFix.pluginConfigService = options.pluginConfigService;
|
||||
autoFix.accessService = options.accessService;
|
||||
autoFix.storageService = options.storageService;
|
||||
return autoFix;
|
||||
}
|
||||
|
||||
describe("AutoFix", () => {
|
||||
it("parses legacy storage values", () => {
|
||||
const config = parseStorageValue(
|
||||
JSON.stringify({
|
||||
value: {
|
||||
key: "legacy-private-key",
|
||||
accountUrl: "https://example.com/acct/1",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
assert.equal(config.key, "legacy-private-key");
|
||||
});
|
||||
|
||||
it("builds the EAB account key payload", () => {
|
||||
const payload = JSON.parse(buildEabAccountKeyValue("kid-1", "private-key"));
|
||||
|
||||
assert.deepEqual(payload, {
|
||||
kid: "kid-1",
|
||||
privateKey: "private-key",
|
||||
});
|
||||
});
|
||||
|
||||
it("builds legacy Google account config query by exact email key only", () => {
|
||||
assert.deepEqual(buildLegacyGoogleAccountConfigWhere("user@example.com"), {
|
||||
userId: 1,
|
||||
scope: "user",
|
||||
namespace: "1",
|
||||
key: "acme.config.google.user@example.com",
|
||||
});
|
||||
});
|
||||
|
||||
it("finds legacy Google account config by exact email key only", async () => {
|
||||
let findOneWhere: any;
|
||||
let findCalled = false;
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: null as any,
|
||||
accessService: null as any,
|
||||
storageService: {
|
||||
getRepository() {
|
||||
return {
|
||||
async findOne(options: any) {
|
||||
findOneWhere = options.where;
|
||||
return {
|
||||
value: JSON.stringify({
|
||||
value: {
|
||||
privateKey: "legacy-private-key",
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
async find() {
|
||||
findCalled = true;
|
||||
return [];
|
||||
},
|
||||
};
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
const config = await autoFix.getLegacyGoogleAccountConfig("user@example.com");
|
||||
|
||||
assert.equal(config.privateKey, "legacy-private-key");
|
||||
assert.deepEqual(findOneWhere, buildLegacyGoogleAccountConfigWhere("user@example.com"));
|
||||
assert.equal(findCalled, false);
|
||||
});
|
||||
|
||||
it("does not query legacy Google account config without email", async () => {
|
||||
let repositoryCalled = false;
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: null as any,
|
||||
accessService: null as any,
|
||||
storageService: {
|
||||
getRepository() {
|
||||
repositoryCalled = true;
|
||||
return {};
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
|
||||
const config = await autoFix.getLegacyGoogleAccountConfig();
|
||||
|
||||
assert.equal(config, null);
|
||||
assert.equal(repositoryCalled, false);
|
||||
});
|
||||
|
||||
it("skips Google common EAB account key fix outside commercial edition", async () => {
|
||||
let pluginConfigCalled = false;
|
||||
const autoFix = createAutoFix({
|
||||
pluginConfigService: {
|
||||
async getPluginConfig() {
|
||||
pluginConfigCalled = true;
|
||||
return null;
|
||||
},
|
||||
} as any,
|
||||
accessService: null as any,
|
||||
storageService: null as any,
|
||||
});
|
||||
|
||||
await autoFix.init();
|
||||
|
||||
assert.equal(pluginConfigCalled, false);
|
||||
});
|
||||
|
||||
it("fixes Google common EAB account key in commercial edition", async () => {
|
||||
const { AutoFix: MockedAutoFix } = await esmock("./auto-fix.js", {
|
||||
"@certd/plus-core": {
|
||||
isComm: () => true,
|
||||
},
|
||||
});
|
||||
let getAccessByIdArgs: any[] = [];
|
||||
let findOneWhere: any;
|
||||
let updateAccessParam: any;
|
||||
const autoFix = new MockedAutoFix();
|
||||
autoFix.pluginConfigService = {
|
||||
async getPluginConfig(options: any) {
|
||||
assert.deepEqual(options, {
|
||||
name: "CertApply",
|
||||
type: "builtIn",
|
||||
});
|
||||
return {
|
||||
sysSetting: {
|
||||
input: {
|
||||
googleCommonEabAccessId: 12,
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
autoFix.accessService = {
|
||||
async getAccessById(...args: any[]) {
|
||||
getAccessByIdArgs = args;
|
||||
return {
|
||||
kid: "kid-1",
|
||||
email: "user@example.com",
|
||||
};
|
||||
},
|
||||
async updateAccess(param: any) {
|
||||
updateAccessParam = param;
|
||||
},
|
||||
};
|
||||
autoFix.storageService = {
|
||||
getRepository() {
|
||||
return {
|
||||
async findOne(options: any) {
|
||||
findOneWhere = options.where;
|
||||
return {
|
||||
value: JSON.stringify({
|
||||
value: {
|
||||
privateKey: "legacy-private-key",
|
||||
},
|
||||
}),
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
await autoFix.fixGoogleCommonEabAccountKey();
|
||||
|
||||
assert.deepEqual(getAccessByIdArgs, [12, false]);
|
||||
assert.deepEqual(findOneWhere, buildLegacyGoogleAccountConfigWhere("user@example.com"));
|
||||
assert.deepEqual(updateAccessParam, {
|
||||
id: 12,
|
||||
eabType: "google",
|
||||
accountKey: buildEabAccountKeyValue("kid-1", "legacy-private-key"),
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,107 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { logger } from "@certd/basic";
|
||||
import { AccessService } from "@certd/lib-server";
|
||||
import { isComm } from "@certd/plus-core";
|
||||
import { PluginConfigService } from "../plugin/service/plugin-config-service.js";
|
||||
import { StorageService } from "../pipeline/service/storage-service.js";
|
||||
|
||||
export function parseStorageValue(value?: string) {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
return parsed?.value || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function buildEabAccountKeyValue(kid: string, privateKey: string) {
|
||||
return JSON.stringify({
|
||||
kid,
|
||||
privateKey,
|
||||
});
|
||||
}
|
||||
|
||||
export function buildLegacyGoogleAccountConfigWhere(email: string) {
|
||||
return {
|
||||
userId: 1,
|
||||
scope: "user",
|
||||
namespace: "1",
|
||||
key: `acme.config.google.${email}`,
|
||||
};
|
||||
}
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class AutoFix {
|
||||
@Inject()
|
||||
pluginConfigService: PluginConfigService;
|
||||
|
||||
@Inject()
|
||||
accessService: AccessService;
|
||||
|
||||
@Inject()
|
||||
storageService: StorageService;
|
||||
|
||||
async init() {
|
||||
await this.fixGoogleCommonEabAccountKey();
|
||||
}
|
||||
async fixGoogleCommonEabAccountKey() {
|
||||
if (!isComm()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const certApplyConfig = await this.pluginConfigService.getPluginConfig({
|
||||
name: "CertApply",
|
||||
type: "builtIn",
|
||||
});
|
||||
const googleCommonEabAccessId = certApplyConfig?.sysSetting?.input?.googleCommonEabAccessId;
|
||||
if (!googleCommonEabAccessId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const eabAccess = await this.accessService.getAccessById(googleCommonEabAccessId, false);
|
||||
if (eabAccess.accountKey) {
|
||||
return;
|
||||
}
|
||||
if (!eabAccess.kid) {
|
||||
logger.info("公共Google EAB授权缺少KID,跳过历史ACME账号私钥修复");
|
||||
return;
|
||||
}
|
||||
|
||||
const accountConfig = await this.getLegacyGoogleAccountConfig(eabAccess.email);
|
||||
const privateKey = accountConfig?.privateKey || accountConfig?.key || accountConfig?.accountKey;
|
||||
if (!privateKey) {
|
||||
logger.info("未找到可迁移到公共Google EAB授权的历史ACME账号私钥");
|
||||
return;
|
||||
}
|
||||
|
||||
const accountKey = buildEabAccountKeyValue(eabAccess.kid, privateKey);
|
||||
await this.accessService.updateAccess({ id: googleCommonEabAccessId, eabType: "google", accountKey });
|
||||
logger.info(`已修复公共Google EAB授权的ACME账号私钥,accessId=${googleCommonEabAccessId}`);
|
||||
} catch (e: any) {
|
||||
logger.error("修复公共Google EAB授权ACME账号私钥失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
async getLegacyGoogleAccountConfig(email?: string) {
|
||||
if (!email) {
|
||||
return null;
|
||||
}
|
||||
const repository = this.storageService.getRepository();
|
||||
const exact = await repository.findOne({
|
||||
where: buildLegacyGoogleAccountConfigWhere(email),
|
||||
});
|
||||
const exactValue = this.parseStorageValue(exact?.value);
|
||||
if (exactValue?.key || exactValue?.privateKey || exactValue?.accountKey) {
|
||||
return exactValue;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
parseStorageValue(value?: string) {
|
||||
return parseStorageValue(value);
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -1,14 +1,14 @@
|
||||
import { logger } from '@certd/basic';
|
||||
import { PlusService, SysInstallInfo, SysPrivateSettings, SysSettingsService } from '@certd/lib-server';
|
||||
import { Autoload, Config, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import crypto from 'crypto';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { UserService } from '../sys/authority/service/user-service.js';
|
||||
import { SafeService } from "../sys/settings/safe-service.js";
|
||||
|
||||
@Autoload()
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class AutoAInitSite {
|
||||
export class AutoInitSite {
|
||||
@Inject()
|
||||
userService: UserService;
|
||||
|
||||
@@ -22,7 +22,6 @@ export class AutoAInitSite {
|
||||
@Inject()
|
||||
safeService: SafeService;
|
||||
|
||||
@Init()
|
||||
async init() {
|
||||
logger.info('初始化站点开始');
|
||||
await this.startOptimizeDb();
|
||||
+3
-4
@@ -1,16 +1,15 @@
|
||||
import { Autoload, Init, Inject, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { logger } from "@certd/basic";
|
||||
import { PluginService } from "../plugin/service/plugin-service.js";
|
||||
import { registerPaymentProviders } from "../suite/payments/index.js";
|
||||
|
||||
@Autoload()
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class AutoBLoadPlugins {
|
||||
export class AutoLoadPlugins {
|
||||
@Inject()
|
||||
pluginService: PluginService;
|
||||
|
||||
|
||||
@Init()
|
||||
async init() {
|
||||
logger.info(`加载插件开始,加载模式:${process.env.certd_plugin_loadmode}`);
|
||||
if (process.env.certd_plugin_loadmode === "metadata") {
|
||||
+3
-4
@@ -1,14 +1,13 @@
|
||||
import { logger, utils } from '@certd/basic';
|
||||
import { UserSuiteService } from '@certd/commercial-core';
|
||||
import { Autoload, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
|
||||
@Autoload()
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class AutoDMitterRegister {
|
||||
export class AutoMitterRegister {
|
||||
@Inject()
|
||||
userSuiteService: UserSuiteService;
|
||||
|
||||
@Init()
|
||||
async init() {
|
||||
await this.registerOnNewUser();
|
||||
}
|
||||
+5
-5
@@ -1,21 +1,21 @@
|
||||
import { Autoload, Init, Inject, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { CertInfoService } from "../monitor/index.js";
|
||||
import { pipelineEmitter } from "@certd/pipeline";
|
||||
import { CertInfo, EVENT_CERT_APPLY_SUCCESS } from "@certd/plugin-cert";
|
||||
import { PipelineEvent } from "@certd/pipeline";
|
||||
|
||||
@Autoload()
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class AutoEPipelineEmitterRegister {
|
||||
export class AutoPipelineEmitterRegister {
|
||||
@Inject()
|
||||
certInfoService: CertInfoService;
|
||||
|
||||
@Init()
|
||||
async init() {
|
||||
await this.onCertApplySuccess();
|
||||
}
|
||||
|
||||
async onCertApplySuccess() {
|
||||
pipelineEmitter.on(EVENT_CERT_APPLY_SUCCESS, async (event: PipelineEvent<{cert:CertInfo,file:string}>) => {
|
||||
pipelineEmitter.on(EVENT_CERT_APPLY_SUCCESS, async (event: PipelineEvent<{ cert: CertInfo; file: string }>) => {
|
||||
await this.certInfoService.updateCertByPipelineId(event.pipeline.id, event.event.cert, event.event.file);
|
||||
});
|
||||
}
|
||||
+3
-4
@@ -1,4 +1,4 @@
|
||||
import { App, Autoload, Config, Init, Inject, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { App, Config, Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { getPlusInfo, isPlus } from '@certd/plus-core';
|
||||
import { isDev, logger } from '@certd/basic';
|
||||
|
||||
@@ -11,9 +11,9 @@ import { UserService } from '../sys/authority/service/user-service.js';
|
||||
import { UserSettingsService } from '../mine/service/user-settings-service.js';
|
||||
import { startProxyServer } from './proxy/server.js';
|
||||
|
||||
@Autoload()
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class AutoZPrint {
|
||||
export class AutoPrint {
|
||||
@Inject()
|
||||
sysSettingsService: SysSettingsService;
|
||||
|
||||
@@ -34,7 +34,6 @@ export class AutoZPrint {
|
||||
@Config('system.resetAdminPasswd')
|
||||
private resetAdminPasswd: boolean;
|
||||
|
||||
@Init()
|
||||
async init() {
|
||||
//监听https
|
||||
this.startHttpsServer();
|
||||
@@ -0,0 +1,44 @@
|
||||
import { Autoload, Init, Inject, Scope, ScopeEnum } from "@midwayjs/core";
|
||||
import { AutoInitSite } from "./auto-init-site.js";
|
||||
import { AutoLoadPlugins } from "./auto-load-plugins.js";
|
||||
import { AutoCron } from "./auto-cron.js";
|
||||
import { AutoMitterRegister } from "./auto-mitter-register.js";
|
||||
import { AutoPipelineEmitterRegister } from "./auto-pipeline-emitter-register.js";
|
||||
import { AutoFix } from "./auto-fix.js";
|
||||
import { AutoPrint } from "./auto-print.js";
|
||||
|
||||
@Autoload()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class AutoRegister {
|
||||
@Inject()
|
||||
autoInitSite: AutoInitSite;
|
||||
|
||||
@Inject()
|
||||
autoLoadPlugins: AutoLoadPlugins;
|
||||
|
||||
@Inject()
|
||||
autoCron: AutoCron;
|
||||
|
||||
@Inject()
|
||||
autoMitterRegister: AutoMitterRegister;
|
||||
|
||||
@Inject()
|
||||
autoPipelineEmitterRegister: AutoPipelineEmitterRegister;
|
||||
|
||||
@Inject()
|
||||
autoPrint: AutoPrint;
|
||||
|
||||
@Inject()
|
||||
autoFix: AutoFix;
|
||||
|
||||
@Init()
|
||||
async init() {
|
||||
await this.autoInitSite.init();
|
||||
await this.autoLoadPlugins.init();
|
||||
await this.autoCron.init();
|
||||
await this.autoMitterRegister.init();
|
||||
await this.autoPipelineEmitterRegister.init();
|
||||
await this.autoFix.init();
|
||||
await this.autoPrint.init();
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,8 @@ export class CnameProviderEntity {
|
||||
userId: number;
|
||||
@Column({ comment: '域名', length: 100 })
|
||||
domain: string;
|
||||
@Column({ comment: '子域名托管', length: 100, nullable: true })
|
||||
subdomain: string;
|
||||
@Column({ comment: 'DNS提供商类型', name: 'dns_provider_type', length: 20 })
|
||||
dnsProviderType: string;
|
||||
@Column({ comment: 'DNS授权Id', name: 'access_id' })
|
||||
|
||||
@@ -98,6 +98,18 @@ export class CnameProviderService extends BaseService<CnameProviderEntity> {
|
||||
return null;
|
||||
}
|
||||
|
||||
async getSubDomains() {
|
||||
const list = await this.repository.find({
|
||||
select: {
|
||||
subdomain: true,
|
||||
},
|
||||
where: {
|
||||
disabled: false,
|
||||
},
|
||||
});
|
||||
return list.map(item => item.subdomain?.trim()).filter((item): item is string => !!item);
|
||||
}
|
||||
|
||||
async list(req: ListReq): Promise<any[]> {
|
||||
const list = await super.list(req);
|
||||
const sysPrivateSettings = await this.settingsService.getSetting<SysPrivateSettings>(SysPrivateSettings);
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { describe, it } from "mocha";
|
||||
import { SubDomainsGetter } from "./sub-domain-getter.js";
|
||||
|
||||
describe("SubDomainsGetter", () => {
|
||||
it("returns subdomains configured on system cname providers", async () => {
|
||||
const subDomainService = {
|
||||
async getListByUserId() {
|
||||
return ["example.com"];
|
||||
},
|
||||
} as any;
|
||||
const domainService = {
|
||||
async findOne() {
|
||||
return null;
|
||||
},
|
||||
} as any;
|
||||
const cnameProviderService = {
|
||||
async getSubDomains() {
|
||||
return ["cname-hosted.example.com"];
|
||||
},
|
||||
} as any;
|
||||
|
||||
const getter = new SubDomainsGetter(1, 2, subDomainService, domainService, cnameProviderService);
|
||||
|
||||
assert.deepEqual(await getter.getSubDomains(), ["cname-hosted.example.com", "example.com"]);
|
||||
assert.equal(await getter.hasSubDomain("txt.certd.cname-hosted.example.com"), "cname-hosted.example.com");
|
||||
});
|
||||
});
|
||||
@@ -1,22 +1,30 @@
|
||||
import { ISubDomainsGetter } from "@certd/plugin-cert";
|
||||
import { SubDomainService } from "../sub-domain-service.js";
|
||||
import { DomainService } from "../../../cert/service/domain-service.js";
|
||||
import { CnameProviderService } from "../../../cname/service/cname-provider-service.js";
|
||||
|
||||
export class SubDomainsGetter implements ISubDomainsGetter {
|
||||
userId: number;
|
||||
projectId: number;
|
||||
subDomainService: SubDomainService;
|
||||
domainService: DomainService;
|
||||
cnameProviderService: CnameProviderService;
|
||||
|
||||
constructor(userId: number, projectId: number, subDomainService: SubDomainService, domainService: DomainService) {
|
||||
constructor(userId: number, projectId: number, subDomainService: SubDomainService, domainService: DomainService, cnameProviderService: CnameProviderService) {
|
||||
this.userId = userId;
|
||||
this.projectId = projectId;
|
||||
this.subDomainService = subDomainService;
|
||||
this.domainService = domainService;
|
||||
this.cnameProviderService = cnameProviderService;
|
||||
}
|
||||
|
||||
async getSubDomains() {
|
||||
return await this.subDomainService.getListByUserId(this.userId, this.projectId)
|
||||
const projectSubDomains = await this.subDomainService.getListByUserId(this.userId, this.projectId) || [];
|
||||
const cnameProviderSubDomains = await this.cnameProviderService.getSubDomains();
|
||||
return [...projectSubDomains, ...cnameProviderSubDomains]
|
||||
.map(item => item?.trim())
|
||||
.filter((item): item is string => !!item)
|
||||
.sort((a, b) => b.length - a.length);
|
||||
}
|
||||
|
||||
async hasSubDomain(fullDomain: string) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import { SubDomainService } from "../sub-domain-service.js";
|
||||
import { CertInfoGetter } from "./cert-info-getter.js";
|
||||
import { CertInfoService } from "../../../monitor/index.js";
|
||||
import { ICertInfoGetter } from "@certd/plugin-lib";
|
||||
import { CnameProviderService } from "../../../cname/service/cname-provider-service.js";
|
||||
|
||||
const serviceNames = [
|
||||
'ocrService',
|
||||
@@ -53,7 +54,8 @@ export class TaskServiceGetter implements IServiceGetter{
|
||||
async getSubDomainsGetter(): Promise<SubDomainsGetter> {
|
||||
const subDomainsService:SubDomainService = await this.appCtx.getAsync("subDomainService")
|
||||
const domainService:DomainService = await this.appCtx.getAsync("domainService")
|
||||
return new SubDomainsGetter(this.userId,this.projectId, subDomainsService,domainService)
|
||||
const cnameProviderService:CnameProviderService = await this.appCtx.getAsync("cnameProviderService")
|
||||
return new SubDomainsGetter(this.userId,this.projectId, subDomainsService,domainService,cnameProviderService)
|
||||
}
|
||||
|
||||
async getCertInfoGetter(): Promise<ICertInfoGetter> {
|
||||
@@ -102,4 +104,3 @@ export type TaskServiceCreateReq = {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/// <reference types="mocha" />
|
||||
|
||||
import assert from 'node:assert/strict';
|
||||
import { Not } from 'typeorm';
|
||||
import { buildUserContactConflictWhere } from './user-service.js';
|
||||
|
||||
describe('buildUserContactConflictWhere', () => {
|
||||
it('checks username, mobile and email conflicts except current user', () => {
|
||||
const where = buildUserContactConflictWhere('user@example.com', 12);
|
||||
|
||||
assert.deepEqual(where, [
|
||||
{ username: 'user@example.com', id: Not(12) },
|
||||
{ mobile: 'user@example.com', id: Not(12) },
|
||||
{ email: 'user@example.com', id: Not(12) },
|
||||
]);
|
||||
});
|
||||
|
||||
it('trims contact value before building conflict query', () => {
|
||||
const where = buildUserContactConflictWhere(' 13800138000 ', 3);
|
||||
|
||||
assert.deepEqual(where, [
|
||||
{ username: '13800138000', id: Not(3) },
|
||||
{ mobile: '13800138000', id: Not(3) },
|
||||
{ email: '13800138000', id: Not(3) },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Inject, Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import {EntityManager, In, MoreThan, Not, Repository} from 'typeorm';
|
||||
import { EntityManager, In, MoreThan, Not, Repository } from 'typeorm';
|
||||
import { UserEntity } from '../entity/user.js';
|
||||
import * as _ from 'lodash-es';
|
||||
import { BaseService, CommonException, Constants, FileService, SysInstallInfo, SysSettingsService } from '@certd/lib-server';
|
||||
@@ -18,15 +18,22 @@ import { OauthBoundService } from '../../../login/service/oauth-bound-service.js
|
||||
export type RegisterType = 'username' | 'mobile' | 'email';
|
||||
export type ForgotPasswordType = 'mobile' | 'email';
|
||||
|
||||
export const AdminRoleId = 1
|
||||
export const AdminRoleId = 1;
|
||||
|
||||
export function buildUserContactConflictWhere(value: string, userId: number) {
|
||||
const contact = value?.trim();
|
||||
return [
|
||||
{ username: contact, id: Not(userId) },
|
||||
{ mobile: contact, id: Not(userId) },
|
||||
{ email: contact, id: Not(userId) },
|
||||
];
|
||||
}
|
||||
/**
|
||||
* 系统用户
|
||||
*/
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
export class UserService extends BaseService<UserEntity> {
|
||||
|
||||
|
||||
@InjectEntityModel(UserEntity)
|
||||
repository: Repository<UserEntity>;
|
||||
@Inject()
|
||||
@@ -44,10 +51,9 @@ export class UserService extends BaseService<UserEntity> {
|
||||
@Inject()
|
||||
dbAdapter: DbAdapter;
|
||||
|
||||
@Inject()
|
||||
@Inject()
|
||||
oauthBoundService: OauthBoundService;
|
||||
|
||||
|
||||
//@ts-ignore
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
@@ -145,7 +151,7 @@ export class UserService extends BaseService<UserEntity> {
|
||||
return bcrypt.hashSync(plainPassword, salt);
|
||||
}
|
||||
|
||||
async findOne(param: Record<string,any>) {
|
||||
async findOne(param: Record<string, any>) {
|
||||
return this.repository.findOne({
|
||||
where: param,
|
||||
});
|
||||
@@ -177,12 +183,11 @@ export class UserService extends BaseService<UserEntity> {
|
||||
return await this.roleService.getPermissionByRoleIds(roleIds);
|
||||
}
|
||||
|
||||
async register(type: string, user: UserEntity,withTx?:(tx: EntityManager)=>Promise<void>) {
|
||||
async register(type: string, user: UserEntity, withTx?: (tx: EntityManager) => Promise<void>) {
|
||||
if (!user.password) {
|
||||
user.password = simpleNanoId();
|
||||
}
|
||||
|
||||
|
||||
if (user.username) {
|
||||
const username = user.username;
|
||||
const old = await this.findOne([{ username: username }, { mobile: username }, { email: username }]);
|
||||
@@ -208,7 +213,6 @@ export class UserService extends BaseService<UserEntity> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!user.username) {
|
||||
user.username = 'user_' + simpleNanoId();
|
||||
}
|
||||
@@ -235,7 +239,7 @@ export class UserService extends BaseService<UserEntity> {
|
||||
const userRole: UserRoleEntity = UserRoleEntity.of(newUser.id, Constants.role.defaultUser);
|
||||
await txManager.save(userRole);
|
||||
|
||||
if(withTx) {
|
||||
if (withTx) {
|
||||
await withTx(txManager);
|
||||
}
|
||||
});
|
||||
@@ -247,35 +251,26 @@ export class UserService extends BaseService<UserEntity> {
|
||||
return newUser;
|
||||
}
|
||||
|
||||
async forgotPassword(
|
||||
data: {
|
||||
type: ForgotPasswordType;
|
||||
input?: string,
|
||||
phoneCode?: string,
|
||||
validateCode: string,
|
||||
password: string,
|
||||
confirmPassword: string,
|
||||
}
|
||||
) {
|
||||
if(!data.type) {
|
||||
async forgotPassword(data: { type: ForgotPasswordType; input?: string; phoneCode?: string; validateCode: string; password: string; confirmPassword: string }) {
|
||||
if (!data.type) {
|
||||
throw new CommonException('找回类型不能为空');
|
||||
}
|
||||
if(data.password !== data.confirmPassword) {
|
||||
if (data.password !== data.confirmPassword) {
|
||||
throw new CommonException('两次输入的密码不一致');
|
||||
}
|
||||
const where :any= {
|
||||
const where: any = {
|
||||
[data.type]: data.input,
|
||||
};
|
||||
if (data.type === 'mobile' ) {
|
||||
where.phoneCode = data.phoneCode ?? '86';
|
||||
if (data.type === 'mobile') {
|
||||
where.phoneCode = data.phoneCode ?? '86';
|
||||
}
|
||||
const user = await this.findOne({ [data.type]: data.input });
|
||||
console.log('user', user)
|
||||
if(!user) {
|
||||
console.log('user', user);
|
||||
if (!user) {
|
||||
throw new CommonException('用户不存在');
|
||||
// return;
|
||||
}
|
||||
await this.resetPassword(user.id, data.password)
|
||||
await this.resetPassword(user.id, data.password);
|
||||
return user.username;
|
||||
}
|
||||
|
||||
@@ -376,30 +371,102 @@ export class UserService extends BaseService<UserEntity> {
|
||||
}
|
||||
|
||||
async getAdmins() {
|
||||
const admins = await this.userRoleService.find({
|
||||
where: {
|
||||
roleId: AdminRoleId,
|
||||
},
|
||||
});
|
||||
const admins = await this.userRoleService.find({
|
||||
where: {
|
||||
roleId: AdminRoleId,
|
||||
},
|
||||
});
|
||||
|
||||
const userIds = admins.map(item => item.userId);
|
||||
return await this.repository.find({
|
||||
where: {
|
||||
id: In(userIds),
|
||||
status: 1,
|
||||
},
|
||||
order: {
|
||||
updateTime: 'DESC',
|
||||
},
|
||||
})
|
||||
const userIds = admins.map(item => item.userId);
|
||||
return await this.repository.find({
|
||||
where: {
|
||||
id: In(userIds),
|
||||
status: 1,
|
||||
},
|
||||
order: {
|
||||
updateTime: 'DESC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async updateProfile(userId: any, body: any) {
|
||||
|
||||
await this.update({
|
||||
id: userId,
|
||||
...body,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async verifyIdentity(userId: number, body: { identityType: 'password' | 'email' | 'mobile'; identityPassword?: string; identityValidateCode?: string }, codeService: any) {
|
||||
const user = await this.info(userId);
|
||||
if (body.identityType === 'password') {
|
||||
const passwordChecked = await this.checkPassword(body.identityPassword, user.password, user.passwordVersion);
|
||||
if (!passwordChecked) {
|
||||
throw new CommonException('密码错误');
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (body.identityType === 'email') {
|
||||
if (!user.email) {
|
||||
throw new CommonException('当前账号未绑定邮箱');
|
||||
}
|
||||
codeService.checkEmailCode({
|
||||
email: user.email,
|
||||
validateCode: body.identityValidateCode,
|
||||
verificationType: 'contactIdentity',
|
||||
throwError: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (body.identityType === 'mobile') {
|
||||
if (!user.mobile) {
|
||||
throw new CommonException('当前账号未绑定手机号');
|
||||
}
|
||||
await codeService.checkSmsCode({
|
||||
mobile: user.mobile,
|
||||
phoneCode: user.phoneCode || '86',
|
||||
smsCode: body.identityValidateCode,
|
||||
verificationType: 'contactIdentity',
|
||||
throwError: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
throw new CommonException('不支持的验证方式');
|
||||
}
|
||||
|
||||
checkContactIdentityValidation(userId: number, validationCode: string, codeService: any) {
|
||||
const validationValue = codeService.getValidationValue(validationCode);
|
||||
if (!validationValue || validationValue.type !== 'contactIdentity' || validationValue.userId !== userId) {
|
||||
throw new CommonException('请先验证本人操作');
|
||||
}
|
||||
}
|
||||
|
||||
async updateMobile(userId: number, body: { phoneCode?: string; mobile: string }) {
|
||||
const mobile = body.mobile?.trim();
|
||||
if (!mobile) {
|
||||
throw new CommonException('手机号不能为空');
|
||||
}
|
||||
const old = await this.findOne(buildUserContactConflictWhere(mobile, userId));
|
||||
if (old != null) {
|
||||
throw new CommonException('手机号已被占用');
|
||||
}
|
||||
await this.repository.update(userId, {
|
||||
phoneCode: body.phoneCode || '86',
|
||||
mobile,
|
||||
});
|
||||
}
|
||||
|
||||
async updateEmail(userId: number, body: { email: string }) {
|
||||
const email = body.email?.trim();
|
||||
if (!email) {
|
||||
throw new CommonException('邮箱不能为空');
|
||||
}
|
||||
const old = await this.findOne(buildUserContactConflictWhere(email, userId));
|
||||
if (old != null) {
|
||||
throw new CommonException('邮箱已被占用');
|
||||
}
|
||||
await this.repository.update(userId, {
|
||||
email,
|
||||
});
|
||||
}
|
||||
|
||||
async getAllUserIds() {
|
||||
@@ -408,7 +475,7 @@ export class UserService extends BaseService<UserEntity> {
|
||||
where: {
|
||||
status: 1,
|
||||
},
|
||||
})
|
||||
});
|
||||
return users.map(item => item.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,11 +275,13 @@ export class AliyunDeployCertToESA extends AbstractTaskPlugin {
|
||||
}
|
||||
});
|
||||
|
||||
const list = certListRes.Result;
|
||||
let list = certListRes.Result || [];
|
||||
list = list.filter((item: any) => item.Type === "cas");
|
||||
if (!list || list.length === 0) {
|
||||
this.logger.info(`站点[${siteId}]没有证书, 无需删除`);
|
||||
this.logger.info(`站点[${siteId}]没有CAS证书, 无需删除`);
|
||||
return
|
||||
}
|
||||
|
||||
if (list.length < certLimit) {
|
||||
this.logger.info(`站点[${siteId}]证书数量(${list.length})未超限制, 无需删除`);
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import assert from "assert";
|
||||
import { EabAccess } from "./eab-access.js";
|
||||
|
||||
describe("EabAccess", () => {
|
||||
it("generates an account key payload for the current kid", async () => {
|
||||
const access = new EabAccess();
|
||||
access.kid = "kid-1";
|
||||
|
||||
const payload = JSON.parse(await access.onGenerateAccountKey());
|
||||
|
||||
assert.equal(payload.kid, "kid-1");
|
||||
assert.match(payload.privateKey, /BEGIN (RSA )?PRIVATE KEY/);
|
||||
});
|
||||
|
||||
it("requires kid before generating the account key payload", async () => {
|
||||
const access = new EabAccess();
|
||||
|
||||
await assert.rejects(() => access.onGenerateAccountKey(), /请先填写KID/);
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,5 @@
|
||||
import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline";
|
||||
import * as acme from "@certd/acme-client";
|
||||
|
||||
@IsAccess({
|
||||
name: "eab",
|
||||
@@ -7,6 +8,23 @@ import { IsAccess, AccessInput, BaseAccess } from "@certd/pipeline";
|
||||
icon: "ic:outline-lock",
|
||||
})
|
||||
export class EabAccess extends BaseAccess {
|
||||
@AccessInput({
|
||||
title: "EAB类型",
|
||||
component: {
|
||||
name: "a-select",
|
||||
options: [
|
||||
{ value: "google", label: "Google", icon: "flat-color-icons:google" },
|
||||
{ value: "zerossl", label: "ZeroSSL", icon: "emojione:digit-zero" },
|
||||
{ value: "litessl", label: "litessl", icon: "roentgen:free" },
|
||||
{ value: "sslcom", label: "SSL.com", icon: "la:expeditedssl" },
|
||||
],
|
||||
},
|
||||
helper: "请选择EAB类型",
|
||||
required: true,
|
||||
encrypt: false,
|
||||
})
|
||||
eabType = "";
|
||||
|
||||
@AccessInput({
|
||||
title: "KID",
|
||||
component: {
|
||||
@@ -34,10 +52,35 @@ export class EabAccess extends BaseAccess {
|
||||
placeholder: "绑定一个邮箱",
|
||||
},
|
||||
rules: [{ type: "email", message: "请输入正确的邮箱" }],
|
||||
helper: "Google的EAB申请证书,更换邮箱会导致EAB失效,可以在此处绑定一个邮箱避免此问题",
|
||||
helper: "绑定一个邮箱,避免失效",
|
||||
required: true,
|
||||
})
|
||||
email = "";
|
||||
|
||||
@AccessInput({
|
||||
title: "ACME账号私钥",
|
||||
component: {
|
||||
name: "refresh-input",
|
||||
action: "GenerateAccountKey",
|
||||
buttonText: "生成",
|
||||
successMessage: "账号私钥已生成,请保存授权配置",
|
||||
},
|
||||
required: true,
|
||||
helper: "如果修改了KID,请点击生成重新生成账号私钥\n注意:google的EAB只能生成一次账号私钥,更新私钥需要获取一个新的EAB授权",
|
||||
encrypt: true,
|
||||
})
|
||||
accountKey = "";
|
||||
|
||||
async onGenerateAccountKey() {
|
||||
if (!this.kid) {
|
||||
throw new Error("请先填写KID");
|
||||
}
|
||||
const key = await acme.crypto.createPrivateKey(2048);
|
||||
return JSON.stringify({
|
||||
kid: this.kid,
|
||||
privateKey: key.toString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
new EabAccess();
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
import assert from "assert";
|
||||
import { AcmeService } from "./acme.js";
|
||||
|
||||
const logger = {
|
||||
info() {},
|
||||
error() {},
|
||||
warn() {},
|
||||
debug() {},
|
||||
};
|
||||
|
||||
describe("AcmeService account config", () => {
|
||||
it("keeps legacy email-based account config when EAB has no saved account key", async () => {
|
||||
const userContext = {
|
||||
async getObj(key: string) {
|
||||
if (key === "acme.config.google.user@example.com") {
|
||||
return {
|
||||
key: "legacy-email-key",
|
||||
accountUrl: "https://dv.acme-v02.api.pki.goog/acme/acct/legacy",
|
||||
};
|
||||
}
|
||||
return null;
|
||||
},
|
||||
async setObj() {},
|
||||
};
|
||||
const service = new AcmeService({
|
||||
userId: 1,
|
||||
userContext: userContext as any,
|
||||
logger: logger as any,
|
||||
sslProvider: "google",
|
||||
eab: {
|
||||
id: 12,
|
||||
kid: "kid-1",
|
||||
hmacKey: "hmac",
|
||||
} as any,
|
||||
domainParser: {} as any,
|
||||
});
|
||||
|
||||
const conf = await service.getAccountConfig("user@example.com", { enabled: false, mappings: {} });
|
||||
|
||||
assert.equal(conf.key, "legacy-email-key");
|
||||
assert.equal(conf.accountUrl, "https://dv.acme-v02.api.pki.goog/acme/acct/legacy");
|
||||
});
|
||||
|
||||
it("uses the account key saved on the EAB access before legacy email config", async () => {
|
||||
const userContext = {
|
||||
async getObj(key: string) {
|
||||
if (key === "acme.config.google.access.12") {
|
||||
return { accountUrl: "https://dv.acme-v02.api.pki.goog/acme/acct/1" };
|
||||
}
|
||||
if (key === "acme.config.google.user@example.com") {
|
||||
return { key: "legacy-email-key" };
|
||||
}
|
||||
return null;
|
||||
},
|
||||
async setObj() {},
|
||||
};
|
||||
const service = new AcmeService({
|
||||
userId: 1,
|
||||
userContext: userContext as any,
|
||||
logger: logger as any,
|
||||
sslProvider: "google",
|
||||
eab: {
|
||||
id: 12,
|
||||
kid: "kid-1",
|
||||
hmacKey: "hmac",
|
||||
accountKey: JSON.stringify({ kid: "kid-1", privateKey: "eab-account-key" }),
|
||||
} as any,
|
||||
domainParser: {} as any,
|
||||
});
|
||||
|
||||
const conf = await service.getAccountConfig("user@example.com", { enabled: false, mappings: {} });
|
||||
|
||||
assert.equal(conf.key, "eab-account-key");
|
||||
assert.equal(conf.accountUrl, "https://dv.acme-v02.api.pki.goog/acme/acct/1");
|
||||
});
|
||||
|
||||
it("rejects an EAB account key generated for another kid", async () => {
|
||||
const service = new AcmeService({
|
||||
userId: 1,
|
||||
userContext: {} as any,
|
||||
logger: logger as any,
|
||||
sslProvider: "google",
|
||||
eab: {
|
||||
id: 12,
|
||||
kid: "kid-2",
|
||||
hmacKey: "hmac",
|
||||
accountKey: JSON.stringify({ kid: "kid-1", privateKey: "eab-account-key" }),
|
||||
} as any,
|
||||
domainParser: {} as any,
|
||||
});
|
||||
|
||||
assert.throws(() => service.getEabAccountPrivateKey(), /请点击刷新重新生成ACME账号私钥/);
|
||||
});
|
||||
|
||||
it("formats expired EAB errors with a Chinese recovery hint", () => {
|
||||
const service = new AcmeService({
|
||||
userId: 1,
|
||||
userContext: {} as any,
|
||||
logger: logger as any,
|
||||
sslProvider: "google",
|
||||
eab: {
|
||||
id: 12,
|
||||
kid: "kid-1",
|
||||
hmacKey: "hmac",
|
||||
} as any,
|
||||
domainParser: {} as any,
|
||||
});
|
||||
|
||||
const error = service.formatCreateAccountError(new Error("Unknown external account binding (EAB) key. This may be due to the EAB key expiring"));
|
||||
|
||||
assert.match(error.message, /EAB授权已失效或已过期/);
|
||||
assert.match(error.message, /请重新获取EAB授权并刷新ACME账号私钥后重试/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("AcmeService challenge", () => {
|
||||
it("parses cname TXT full record to choose the delegated DNS zone", async () => {
|
||||
const parseCalls: string[] = [];
|
||||
const service = new AcmeService({
|
||||
userId: 1,
|
||||
userContext: {} as any,
|
||||
logger: logger as any,
|
||||
sslProvider: "letsencrypt",
|
||||
domainParser: {
|
||||
async parse(fullDomain: string) {
|
||||
parseCalls.push(fullDomain);
|
||||
if (fullDomain === "certd-key.cname.sub.example.com") {
|
||||
return "sub.example.com";
|
||||
}
|
||||
return "example.com";
|
||||
},
|
||||
} as any,
|
||||
});
|
||||
const dnsProvider = {
|
||||
usePunyCode() {
|
||||
return false;
|
||||
},
|
||||
async createRecord(recordReq: any) {
|
||||
assert.equal(recordReq.domain, "sub.example.com");
|
||||
assert.equal(recordReq.fullRecord, "certd-key.cname.sub.example.com");
|
||||
assert.equal(recordReq.hostRecord, "certd-key.cname");
|
||||
return { id: "record-id" };
|
||||
},
|
||||
} as any;
|
||||
|
||||
await service.challengeCreateFn(
|
||||
{
|
||||
identifier: {
|
||||
value: "www.example.com",
|
||||
},
|
||||
challenges: [
|
||||
{
|
||||
type: "dns-01",
|
||||
},
|
||||
],
|
||||
},
|
||||
async () => "key-auth",
|
||||
{
|
||||
domainsVerifyPlan: {
|
||||
"www.example.com": {
|
||||
type: "cname",
|
||||
domain: "www.example.com",
|
||||
mainDomain: "example.com",
|
||||
cnameVerifyPlan: {
|
||||
domain: "cname.sub.example.com",
|
||||
fullRecord: "certd-key.cname.sub.example.com",
|
||||
dnsProvider,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
assert.deepEqual(parseCalls, ["www.example.com", "certd-key.cname.sub.example.com"]);
|
||||
});
|
||||
});
|
||||
@@ -49,11 +49,15 @@ export type CertInfo = {
|
||||
};
|
||||
export type SSLProvider = "letsencrypt" | "google" | "zerossl" | "sslcom" | "letsencrypt_staging";
|
||||
export type PrivateKeyType = "rsa_1024" | "rsa_2048" | "rsa_3072" | "rsa_4096" | "ec_256" | "ec_384" | "ec_521";
|
||||
type AcmeEabOptions = ClientExternalAccountBindingOptions & {
|
||||
id?: number;
|
||||
accountKey?: string;
|
||||
};
|
||||
type AcmeServiceOptions = {
|
||||
userContext: IContext;
|
||||
logger: ILogger;
|
||||
sslProvider: SSLProvider;
|
||||
eab?: ClientExternalAccountBindingOptions;
|
||||
eab?: AcmeEabOptions;
|
||||
skipLocalVerify?: boolean;
|
||||
useMappingProxy?: boolean;
|
||||
reverseProxy?: string;
|
||||
@@ -71,7 +75,7 @@ export class AcmeService {
|
||||
logger: ILogger;
|
||||
sslProvider: SSLProvider;
|
||||
skipLocalVerify = true;
|
||||
eab?: ClientExternalAccountBindingOptions;
|
||||
eab?: AcmeEabOptions;
|
||||
constructor(options: AcmeServiceOptions) {
|
||||
this.options = options;
|
||||
this.userContext = options.userContext;
|
||||
@@ -85,7 +89,14 @@ export class AcmeService {
|
||||
}
|
||||
|
||||
async getAccountConfig(email: string, urlMapping: UrlMapping): Promise<any> {
|
||||
const conf = (await this.userContext.getObj(this.buildAccountKey(email))) || {};
|
||||
let conf = (await this.userContext.getObj(this.buildAccountKey(email))) || {};
|
||||
const eabAccountKey = this.getEabAccountPrivateKey();
|
||||
if (eabAccountKey) {
|
||||
conf = {
|
||||
...((await this.userContext.getObj(this.buildAccessAccountKey())) || {}),
|
||||
key: eabAccountKey,
|
||||
};
|
||||
}
|
||||
if (urlMapping && urlMapping.mappings) {
|
||||
for (const key in urlMapping.mappings) {
|
||||
if (Object.prototype.hasOwnProperty.call(urlMapping.mappings, key)) {
|
||||
@@ -104,16 +115,49 @@ export class AcmeService {
|
||||
return `acme.config.${this.sslProvider}.${email}`;
|
||||
}
|
||||
|
||||
buildAccessAccountKey() {
|
||||
return `acme.config.${this.sslProvider}.access.${this.eab.id}`;
|
||||
}
|
||||
|
||||
getEabAccountPrivateKey() {
|
||||
if (!this.eab?.accountKey) {
|
||||
return null;
|
||||
}
|
||||
let accountKey;
|
||||
try {
|
||||
accountKey = JSON.parse(this.eab.accountKey);
|
||||
} catch {
|
||||
return this.eab.accountKey;
|
||||
}
|
||||
if (accountKey.kid !== this.eab.kid) {
|
||||
throw new Error("EAB的KID已变化,请点击刷新重新生成ACME账号私钥");
|
||||
}
|
||||
return accountKey.privateKey;
|
||||
}
|
||||
|
||||
formatCreateAccountError(e: any) {
|
||||
const message = e?.message || "";
|
||||
if (message.includes("Unknown external account binding (EAB) key")) {
|
||||
return new Error(`EAB授权已失效或已过期,请重新获取EAB授权并刷新ACME账号私钥后重试。原始错误:${message}`);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
async saveAccountConfig(email: string, conf: any) {
|
||||
if (this.getEabAccountPrivateKey()) {
|
||||
// userContext 跟用户走。公共 EAB 场景下这里仅作为当前用户缓存;
|
||||
// 其他用户会通过 onlyReturnExisting 用同一个账号私钥取回 accountUrl。
|
||||
await this.userContext.setObj(this.buildAccessAccountKey(), { accountUrl: conf.accountUrl });
|
||||
return;
|
||||
}
|
||||
await this.userContext.setObj(this.buildAccountKey(email), conf);
|
||||
}
|
||||
|
||||
async getAcmeClient(email: string): Promise<acme.Client> {
|
||||
|
||||
const directoryUrl = acme.getDirectoryUrl({ sslProvider: this.sslProvider, pkType: this.options.privateKeyType });
|
||||
let targetUrl = directoryUrl.replace("https://", "");
|
||||
targetUrl = targetUrl.substring(0, targetUrl.indexOf("/"));
|
||||
|
||||
|
||||
const mappings = {
|
||||
"acme-v02.api.letsencrypt.org": "le.px.certd.handfree.work",
|
||||
"dv.acme-v02.api.pki.goog": "gg.px.certd.handfree.work",
|
||||
@@ -171,7 +215,23 @@ export class AcmeService {
|
||||
contact: [`mailto:${email}`],
|
||||
externalAccountBinding: this.eab,
|
||||
};
|
||||
await client.createAccount(accountPayload);
|
||||
if (this.getEabAccountPrivateKey()) {
|
||||
try {
|
||||
// RFC 8555 的 newAccount 支持 onlyReturnExisting。
|
||||
// 使用同一个账号私钥时,CA 会返回已存在账号的 URL,不会再次消费 EAB。
|
||||
await client.createAccount({ onlyReturnExisting: true });
|
||||
conf.accountUrl = client.getAccountUrl();
|
||||
await this.saveAccountConfig(email, conf);
|
||||
return client;
|
||||
} catch (e: any) {
|
||||
this.logger.info(`未找到已存在的ACME账号,准备创建新账号:${e.message}`);
|
||||
}
|
||||
}
|
||||
try {
|
||||
await client.createAccount(accountPayload);
|
||||
} catch (e: any) {
|
||||
throw this.formatCreateAccountError(e);
|
||||
}
|
||||
conf.accountUrl = client.getAccountUrl();
|
||||
await this.saveAccountConfig(email, conf);
|
||||
}
|
||||
@@ -264,8 +324,8 @@ export class AcmeService {
|
||||
const cname: CnameVerifyPlan = domainVerifyPlan.cnameVerifyPlan;
|
||||
if (cname) {
|
||||
dnsProvider = cname.dnsProvider;
|
||||
domain = await this.options.domainParser.parse(cname.domain);
|
||||
fullRecord = cname.fullRecord;
|
||||
domain = await this.options.domainParser.parse(fullRecord);
|
||||
} else {
|
||||
this.logger.error(`未找到域名${fullDomain}的CNAME校验计划,请修改证书申请配置`);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import { omit } from "lodash-es";
|
||||
//插件分组
|
||||
group: pluginGroups.tencent.key,
|
||||
needPlus: false,
|
||||
deprecated: "腾讯更新证书(Id不变)接口已失效,本插件已下架,请使用其他接口",
|
||||
default: {
|
||||
//默认值配置照抄即可
|
||||
strategy: {
|
||||
|
||||
@@ -23,3 +23,4 @@
|
||||
},
|
||||
"exclude": ["*.js", "*.ts", "dist", "node_modules", "src/**/*.test.ts", "test"]
|
||||
}
|
||||
|
||||
|
||||
Generated
+145
-98
@@ -36,6 +36,9 @@ importers:
|
||||
axios:
|
||||
specifier: ^1.9.0
|
||||
version: 1.9.0(debug@4.4.3)
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
medium-zoom:
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0
|
||||
@@ -49,7 +52,7 @@ importers:
|
||||
packages/core/acme-client:
|
||||
dependencies:
|
||||
'@certd/basic':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../basic
|
||||
'@peculiar/x509':
|
||||
specifier: ^1.11.0
|
||||
@@ -94,6 +97,9 @@ importers:
|
||||
chai-as-promised:
|
||||
specifier: ^7.1.2
|
||||
version: 7.1.2(chai@4.5.0)
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: ^8.57.0
|
||||
version: 8.57.0
|
||||
@@ -106,6 +112,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8)
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
jsdoc-to-markdown:
|
||||
specifier: ^8.0.1
|
||||
version: 8.0.3
|
||||
@@ -188,6 +197,9 @@ importers:
|
||||
chai:
|
||||
specifier: 4.3.10
|
||||
version: 4.3.10
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: ^8.41.0
|
||||
version: 8.57.0
|
||||
@@ -197,6 +209,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8)
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
mocha:
|
||||
specifier: ^10.2.0
|
||||
version: 10.8.2
|
||||
@@ -219,11 +234,11 @@ importers:
|
||||
packages/core/pipeline:
|
||||
dependencies:
|
||||
'@certd/basic':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../basic
|
||||
'@certd/plus-core':
|
||||
specifier: ^1.39.12
|
||||
version: 1.39.12
|
||||
version: link:../../pro/plus-core
|
||||
dayjs:
|
||||
specifier: ^1.11.7
|
||||
version: 1.11.13
|
||||
@@ -267,6 +282,9 @@ importers:
|
||||
chai:
|
||||
specifier: 4.3.10
|
||||
version: 4.3.10
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: ^8.41.0
|
||||
version: 8.57.0
|
||||
@@ -276,6 +294,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8)
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
mocha:
|
||||
specifier: ^10.2.0
|
||||
version: 10.8.2
|
||||
@@ -313,6 +334,12 @@ importers:
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^8.26.1
|
||||
version: 8.32.1(eslint@8.57.0)(typescript@5.9.3)
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
prettier:
|
||||
specifier: ^2.8.8
|
||||
version: 2.8.8
|
||||
@@ -335,6 +362,9 @@ importers:
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^8.26.1
|
||||
version: 8.32.1(eslint@8.57.0)(typescript@5.9.3)
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: ^8.24.0
|
||||
version: 8.57.0
|
||||
@@ -344,6 +374,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8)
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
prettier:
|
||||
specifier: ^2.8.8
|
||||
version: 2.8.8
|
||||
@@ -403,8 +436,11 @@ importers:
|
||||
specifier: ^1.30.0
|
||||
version: 1.31.0
|
||||
cross-env:
|
||||
specifier: ^5.1.4
|
||||
version: 5.2.1
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
js-yaml:
|
||||
specifier: ^3.11.0
|
||||
version: 3.14.1
|
||||
@@ -421,7 +457,7 @@ importers:
|
||||
packages/libs/lib-k8s:
|
||||
dependencies:
|
||||
'@certd/basic':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/basic
|
||||
'@kubernetes/client-node':
|
||||
specifier: 0.21.0
|
||||
@@ -436,6 +472,9 @@ importers:
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^8.26.1
|
||||
version: 8.32.1(eslint@8.57.0)(typescript@5.9.3)
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: ^8.24.0
|
||||
version: 8.57.0
|
||||
@@ -445,6 +484,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8)
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
prettier:
|
||||
specifier: ^2.8.8
|
||||
version: 2.8.8
|
||||
@@ -461,20 +503,20 @@ importers:
|
||||
packages/libs/lib-server:
|
||||
dependencies:
|
||||
'@certd/acme-client':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/acme-client
|
||||
'@certd/basic':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/basic
|
||||
'@certd/pipeline':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/pipeline
|
||||
'@certd/plugin-lib':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../plugins/plugin-lib
|
||||
'@certd/plus-core':
|
||||
specifier: ^1.39.12
|
||||
version: 1.39.12
|
||||
version: link:../../pro/plus-core
|
||||
'@midwayjs/cache':
|
||||
specifier: 3.14.0
|
||||
version: 3.14.0
|
||||
@@ -505,9 +547,6 @@ importers:
|
||||
better-sqlite3:
|
||||
specifier: ^11.1.2
|
||||
version: 11.10.0
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
dayjs:
|
||||
specifier: ^1.11.7
|
||||
version: 1.11.13
|
||||
@@ -539,6 +578,9 @@ importers:
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^8.26.1
|
||||
version: 8.32.1(eslint@8.57.0)(typescript@5.9.3)
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: ^8.24.0
|
||||
version: 8.57.0
|
||||
@@ -548,6 +590,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8)
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
mocha:
|
||||
specifier: ^10.2.0
|
||||
version: 10.8.2
|
||||
@@ -594,6 +639,9 @@ importers:
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^8.26.1
|
||||
version: 8.32.1(eslint@8.57.0)(typescript@5.9.3)
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: ^8.24.0
|
||||
version: 8.57.0
|
||||
@@ -609,6 +657,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8)
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
prettier:
|
||||
specifier: ^2.8.8
|
||||
version: 2.8.8
|
||||
@@ -628,16 +679,16 @@ importers:
|
||||
packages/plugins/plugin-cert:
|
||||
dependencies:
|
||||
'@certd/acme-client':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/acme-client
|
||||
'@certd/basic':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/basic
|
||||
'@certd/pipeline':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/pipeline
|
||||
'@certd/plugin-lib':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../plugin-lib
|
||||
psl:
|
||||
specifier: ^1.9.0
|
||||
@@ -661,6 +712,9 @@ importers:
|
||||
chai:
|
||||
specifier: ^4.3.6
|
||||
version: 4.5.0
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: ^8.24.0
|
||||
version: 8.57.0
|
||||
@@ -670,6 +724,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8)
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
mocha:
|
||||
specifier: ^10.1.0
|
||||
version: 10.8.2
|
||||
@@ -701,17 +758,17 @@ importers:
|
||||
specifier: ^3.964.0
|
||||
version: 3.964.0(aws-crt@1.26.2)
|
||||
'@certd/acme-client':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/acme-client
|
||||
'@certd/basic':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/basic
|
||||
'@certd/pipeline':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/pipeline
|
||||
'@certd/plus-core':
|
||||
specifier: ^1.39.12
|
||||
version: 1.39.12
|
||||
version: link:../../pro/plus-core
|
||||
'@kubernetes/client-node':
|
||||
specifier: 0.21.0
|
||||
version: 0.21.0
|
||||
@@ -776,6 +833,9 @@ importers:
|
||||
chai:
|
||||
specifier: ^4.3.6
|
||||
version: 4.5.0
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: ^8.24.0
|
||||
version: 8.57.0
|
||||
@@ -785,6 +845,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8)
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
mocha:
|
||||
specifier: ^10.1.0
|
||||
version: 10.8.2
|
||||
@@ -804,16 +867,16 @@ importers:
|
||||
packages/pro/commercial-core:
|
||||
dependencies:
|
||||
'@certd/basic':
|
||||
specifier: ^1.39.7
|
||||
specifier: ^1.39.12
|
||||
version: link:../../core/basic
|
||||
'@certd/lib-server':
|
||||
specifier: ^1.39.7
|
||||
specifier: ^1.39.12
|
||||
version: link:../../libs/lib-server
|
||||
'@certd/pipeline':
|
||||
specifier: ^1.39.7
|
||||
specifier: ^1.39.12
|
||||
version: link:../../core/pipeline
|
||||
'@certd/plus-core':
|
||||
specifier: ^1.39.7
|
||||
specifier: ^1.39.12
|
||||
version: link:../plus-core
|
||||
'@midwayjs/core':
|
||||
specifier: 3.20.11
|
||||
@@ -858,6 +921,9 @@ importers:
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^8.26.1
|
||||
version: 8.32.1(eslint@8.57.0)(typescript@5.9.3)
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: ^8.24.0
|
||||
version: 8.57.0
|
||||
@@ -867,6 +933,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8)
|
||||
mocha:
|
||||
specifier: ^10.2.0
|
||||
version: 10.8.2
|
||||
prettier:
|
||||
specifier: ^2.8.8
|
||||
version: 2.8.8
|
||||
@@ -889,16 +958,16 @@ importers:
|
||||
packages/pro/plugin-plus:
|
||||
dependencies:
|
||||
'@certd/basic':
|
||||
specifier: ^1.39.7
|
||||
specifier: ^1.39.12
|
||||
version: link:../../core/basic
|
||||
'@certd/pipeline':
|
||||
specifier: ^1.39.7
|
||||
specifier: ^1.39.12
|
||||
version: link:../../core/pipeline
|
||||
'@certd/plugin-lib':
|
||||
specifier: ^1.39.7
|
||||
specifier: ^1.39.12
|
||||
version: link:../../plugins/plugin-lib
|
||||
'@certd/plus-core':
|
||||
specifier: ^1.39.7
|
||||
specifier: ^1.39.12
|
||||
version: link:../plus-core
|
||||
crypto-js:
|
||||
specifier: ^4.2.0
|
||||
@@ -943,6 +1012,9 @@ importers:
|
||||
chai:
|
||||
specifier: 4.3.10
|
||||
version: 4.3.10
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: ^8.41.0
|
||||
version: 8.57.0
|
||||
@@ -952,6 +1024,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8)
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
mocha:
|
||||
specifier: ^10.2.0
|
||||
version: 10.8.2
|
||||
@@ -974,7 +1049,7 @@ importers:
|
||||
packages/pro/plus-core:
|
||||
dependencies:
|
||||
'@certd/basic':
|
||||
specifier: ^1.39.7
|
||||
specifier: ^1.39.12
|
||||
version: link:../../core/basic
|
||||
dayjs:
|
||||
specifier: ^1.11.7
|
||||
@@ -1007,6 +1082,9 @@ importers:
|
||||
chai:
|
||||
specifier: 4.3.10
|
||||
version: 4.3.10
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
eslint:
|
||||
specifier: ^8.41.0
|
||||
version: 8.57.0
|
||||
@@ -1016,6 +1094,9 @@ importers:
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^4.2.1
|
||||
version: 4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8)
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
mocha:
|
||||
specifier: ^10.2.0
|
||||
version: 10.8.2
|
||||
@@ -1139,9 +1220,6 @@ importers:
|
||||
cropperjs:
|
||||
specifier: ^1.6.1
|
||||
version: 1.6.2
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
cssnano:
|
||||
specifier: ^7.0.6
|
||||
version: 7.0.7(postcss@8.5.6)
|
||||
@@ -1270,10 +1348,10 @@ importers:
|
||||
version: 0.1.3(zod@3.24.4)
|
||||
devDependencies:
|
||||
'@certd/lib-iframe':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../libs/lib-iframe
|
||||
'@certd/pipeline':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/pipeline
|
||||
'@rollup/plugin-commonjs':
|
||||
specifier: ^25.0.7
|
||||
@@ -1329,6 +1407,9 @@ importers:
|
||||
chai:
|
||||
specifier: ^5.1.0
|
||||
version: 5.2.0
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
dependency-cruiser:
|
||||
specifier: ^16.2.3
|
||||
version: 16.10.2
|
||||
@@ -1356,6 +1437,9 @@ importers:
|
||||
eslint-plugin-vue:
|
||||
specifier: ^9.23.0
|
||||
version: 9.33.0(eslint@8.57.0)
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
less:
|
||||
specifier: ^4.2.0
|
||||
version: 4.3.0
|
||||
@@ -1474,47 +1558,47 @@ importers:
|
||||
specifier: ^4.13.1
|
||||
version: 4.13.1
|
||||
'@certd/acme-client':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/acme-client
|
||||
'@certd/basic':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/basic
|
||||
'@certd/commercial-core':
|
||||
specifier: ^1.39.12
|
||||
version: 1.39.12(better-sqlite3@11.10.0)(mysql2@3.14.1)(pg@8.16.0)(reflect-metadata@0.2.2)(ts-node@10.9.2(@types/node@18.19.100)(typescript@5.9.3))
|
||||
version: link:../../pro/commercial-core
|
||||
'@certd/cv4pve-api-javascript':
|
||||
specifier: ^8.4.2
|
||||
version: 8.4.2
|
||||
'@certd/jdcloud':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../libs/lib-jdcloud
|
||||
'@certd/lib-huawei':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../libs/lib-huawei
|
||||
'@certd/lib-k8s':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../libs/lib-k8s
|
||||
'@certd/lib-server':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../libs/lib-server
|
||||
'@certd/midway-flyway-js':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../libs/midway-flyway-js
|
||||
'@certd/pipeline':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../core/pipeline
|
||||
'@certd/plugin-cert':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../plugins/plugin-cert
|
||||
'@certd/plugin-lib':
|
||||
specifier: ^1.39.12
|
||||
specifier: ^1.39.13
|
||||
version: link:../../plugins/plugin-lib
|
||||
'@certd/plugin-plus':
|
||||
specifier: ^1.39.12
|
||||
version: 1.39.12
|
||||
version: link:../../pro/plugin-plus
|
||||
'@certd/plus-core':
|
||||
specifier: ^1.39.12
|
||||
version: 1.39.12
|
||||
version: link:../../pro/plus-core
|
||||
'@google-cloud/dns':
|
||||
specifier: ^5.3.1
|
||||
version: 5.3.1
|
||||
@@ -1617,9 +1701,6 @@ importers:
|
||||
cron-parser:
|
||||
specifier: ^4.9.0
|
||||
version: 4.9.0
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
crypto-js:
|
||||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
@@ -1786,6 +1867,12 @@ importers:
|
||||
c8:
|
||||
specifier: ^10.1.2
|
||||
version: 10.1.3
|
||||
cross-env:
|
||||
specifier: ^7.0.3
|
||||
version: 7.0.3
|
||||
esmock:
|
||||
specifier: ^2.7.5
|
||||
version: 2.7.5
|
||||
mocha:
|
||||
specifier: ^10.2.0
|
||||
version: 10.8.2
|
||||
@@ -6801,20 +6888,11 @@ packages:
|
||||
cropperjs@1.6.2:
|
||||
resolution: {integrity: sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==}
|
||||
|
||||
cross-env@5.2.1:
|
||||
resolution: {integrity: sha512-1yHhtcfAd1r4nwQgknowuUNfIT9E8dOMMspC36g45dN+iD1blloi7xp8X/xAIDnjHWyt1uQ8PHk2fkNaym7soQ==}
|
||||
engines: {node: '>=4.0'}
|
||||
hasBin: true
|
||||
|
||||
cross-env@7.0.3:
|
||||
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
|
||||
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
|
||||
hasBin: true
|
||||
|
||||
cross-spawn@6.0.6:
|
||||
resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==}
|
||||
engines: {node: '>=4.8'}
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -7594,6 +7672,10 @@ packages:
|
||||
deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options.
|
||||
hasBin: true
|
||||
|
||||
esmock@2.7.5:
|
||||
resolution: {integrity: sha512-jKwL7yYpVOERalCllSnPur59s9M0gV5BKijtmOKclqDMuhqdS7DT/a7cODjz6w1XusE0wAaHBTrK+zgab/ENgw==}
|
||||
engines: {node: '>=14.16.0'}
|
||||
|
||||
esniff@2.0.1:
|
||||
resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==}
|
||||
engines: {node: '>=0.10'}
|
||||
@@ -9816,9 +9898,6 @@ packages:
|
||||
next-tick@1.1.0:
|
||||
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
|
||||
|
||||
nice-try@1.0.5:
|
||||
resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==}
|
||||
|
||||
no-case@3.0.4:
|
||||
resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
|
||||
|
||||
@@ -10228,10 +10307,6 @@ packages:
|
||||
resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
path-key@2.0.1:
|
||||
resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
path-key@3.1.1:
|
||||
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -11469,18 +11544,10 @@ packages:
|
||||
shallow-equal@1.2.1:
|
||||
resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==}
|
||||
|
||||
shebang-command@1.2.0:
|
||||
resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
shebang-regex@1.0.0:
|
||||
resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
shebang-regex@3.0.0:
|
||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -20464,22 +20531,10 @@ snapshots:
|
||||
|
||||
cropperjs@1.6.2: {}
|
||||
|
||||
cross-env@5.2.1:
|
||||
dependencies:
|
||||
cross-spawn: 6.0.6
|
||||
|
||||
cross-env@7.0.3:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.6
|
||||
|
||||
cross-spawn@6.0.6:
|
||||
dependencies:
|
||||
nice-try: 1.0.5
|
||||
path-key: 2.0.1
|
||||
semver: 5.7.2
|
||||
shebang-command: 1.2.0
|
||||
which: 1.3.1
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
@@ -21485,6 +21540,8 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
esmock@2.7.5: {}
|
||||
|
||||
esniff@2.0.1:
|
||||
dependencies:
|
||||
d: 1.0.2
|
||||
@@ -23908,8 +23965,6 @@ snapshots:
|
||||
|
||||
next-tick@1.1.0: {}
|
||||
|
||||
nice-try@1.0.5: {}
|
||||
|
||||
no-case@3.0.4:
|
||||
dependencies:
|
||||
lower-case: 2.0.2
|
||||
@@ -24377,8 +24432,6 @@ snapshots:
|
||||
|
||||
path-is-absolute@1.0.1: {}
|
||||
|
||||
path-key@2.0.1: {}
|
||||
|
||||
path-key@3.1.1: {}
|
||||
|
||||
path-key@4.0.0: {}
|
||||
@@ -25728,16 +25781,10 @@ snapshots:
|
||||
|
||||
shallow-equal@1.2.1: {}
|
||||
|
||||
shebang-command@1.2.0:
|
||||
dependencies:
|
||||
shebang-regex: 1.0.0
|
||||
|
||||
shebang-command@2.0.0:
|
||||
dependencies:
|
||||
shebang-regex: 3.0.0
|
||||
|
||||
shebang-regex@1.0.0: {}
|
||||
|
||||
shebang-regex@3.0.0: {}
|
||||
|
||||
shiki@3.4.1:
|
||||
|
||||
@@ -1 +1 @@
|
||||
23:11
|
||||
00:27
|
||||
|
||||
Reference in New Issue
Block a user