Compare commits

..

23 Commits

Author SHA1 Message Date
xiaojunnuo 56350b54ee Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-03-12 18:11:09 +08:00
xiaojunnuo 10b7644bb7 perf: 支持passkey登录 2026-03-12 18:11:02 +08:00
xiaojunnuo d79db3bd3f perf: 获取阿里证书订单id组件增加翻页功能,突破50的上限 2026-03-12 00:46:05 +08:00
xiaojunnuo 1588461633 perf: 优化阿里云连接超时时长为10秒,支持配置环境变量 2026-03-11 23:10:37 +08:00
xiaojunnuo dd999b60a4 fix: 修复当证书更新后第一次站点检查会报与主站证书过期时间不一致错误的bug 2026-03-11 22:38:48 +08:00
xiaojunnuo 3abee72fee fix: 修复修改项目名称后,没有同步刷新的bug
https://github.com/certd/certd/issues/680
2026-03-11 22:36:05 +08:00
xiaojunnuo b5577b1d37 build: release 2026-03-10 00:21:08 +08:00
xiaojunnuo e15ffb5820 build: publish 2026-03-10 00:03:15 +08:00
xiaojunnuo 4d9a5ed4a1 build: trigger build image 2026-03-10 00:03:03 +08:00
xiaojunnuo b2bc1debe0 chore: release 2026-03-10 00:02:46 +08:00
xiaojunnuo 590ff67fcb v1.39.1 2026-03-09 23:47:08 +08:00
xiaojunnuo 209e1adf53 build: prepare to build 2026-03-09 23:44:19 +08:00
xiaojunnuo 53c08484a3 chore: project transfer 2026-03-09 23:43:23 +08:00
xiaojunnuo c6ca832737 perf: 支持迁移个人数据到企业项目中 2026-03-09 23:34:11 +08:00
xiaojunnuo 2c399a078e Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-03-09 13:35:18 +08:00
xiaojunnuo 8c519f13da chore: 1 2026-03-09 13:34:26 +08:00
xiaojunnuo 853fdc70a2 perf: install tip 2026-03-08 11:15:25 +08:00
xiaojunnuo dc4f811eaa build: release 2026-03-08 01:57:31 +08:00
xiaojunnuo d23c8b4a2a fix: 修复企业管理模式下,切换用户登录后,丢失项目列表的bug 2026-03-08 01:53:46 +08:00
xiaojunnuo 00c0dcc81d build: release 2026-03-08 01:47:46 +08:00
xiaojunnuo f77feefdb8 chore: github action update 2026-03-08 01:33:34 +08:00
xiaojunnuo 2e346e5369 build: publish 2026-03-08 01:19:01 +08:00
xiaojunnuo 17023f6b55 build: trigger build image 2026-03-08 01:18:50 +08:00
83 changed files with 1864 additions and 409 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
with:
time: '10' # for 60 seconds
- name: deploy-certd-demo
uses: tyrrrz/action-http-request@master
uses: tyrrrz/action-http-request@prime
with:
# 通过webhook 触发 certd-demo来部署
url: ${{ secrets.WEBHOOK_CERTD_DEMO }}
+1 -1
View File
@@ -118,7 +118,7 @@ jobs:
# greper/certd-agent:latest
# greper/certd-agent:${{steps.get_certd_version.outputs.result}}
- name: deploy-certd-doc
uses: tyrrrz/action-http-request@master
uses: tyrrrz/action-http-request@prime
with:
url: ${{ secrets.WEBHOOK_CERTD_DOC }}
method: POST
+11
View File
@@ -3,6 +3,17 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
### Bug Fixes
* 修复企业管理模式下,切换用户登录后,丢失项目列表的bug ([d23c8b4](https://github.com/certd/certd/commit/d23c8b4a2a5f5ab17822c6ee1d4108ac7280b9d1))
### Performance Improvements
* 支持迁移个人数据到企业项目中 ([c6ca832](https://github.com/certd/certd/commit/c6ca83273779ed84de1b23b5e477063af043d015))
* install tip ([853fdc7](https://github.com/certd/certd/commit/853fdc70a263b62d75c9ff3970607e6bf1c1593b))
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
### Bug Fixes
+39
View File
@@ -3,6 +3,45 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
### Bug Fixes
* 修复企业管理模式下,切换用户登录后,丢失项目列表的bug ([d23c8b4](https://github.com/certd/certd/commit/d23c8b4a2a5f5ab17822c6ee1d4108ac7280b9d1))
### Performance Improvements
* 支持迁移个人数据到企业项目中 ([c6ca832](https://github.com/certd/certd/commit/c6ca83273779ed84de1b23b5e477063af043d015))
* install tip ([853fdc7](https://github.com/certd/certd/commit/853fdc70a263b62d75c9ff3970607e6bf1c1593b))
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
### Bug Fixes
* 修复部署到openwrt错误的bug ([2e3d0cc](https://github.com/certd/certd/commit/2e3d0cc57c16c48ad435bc8fde729bacaedde9f5))
* 修复发件邮箱无法输入的bug ([27b0348](https://github.com/certd/certd/commit/27b0348e1d3d752f418f851965d6afbc26c0160c))
* 修复复制流水线保存后丢失分组和排序号的问题 ([bc32648](https://github.com/certd/certd/commit/bc326489abc1d50a0930b4f47aa2d62d3a486798))
* 修复获取群辉deviceid报错的bug ([79be392](https://github.com/certd/certd/commit/79be392775a2c91848dd5a66a2618adc4e4b48f6))
* 修复京东云域名申请证书报错的bug ([d9c0130](https://github.com/certd/certd/commit/d9c0130b59997144a3c274d456635b800135e43f))
* 修复偶尔下载证书报未授权的错误 ([316537e](https://github.com/certd/certd/commit/316537eb4dcbe5ec57784e8bf95ee3cdfd21dce7))
* 修复dcdn多个域名同时部署时 可能会出现证书名称重复的bug ([78c2ced](https://github.com/certd/certd/commit/78c2ced43b1a73d142b0ed783b162b97f545ab06))
* 优化dcdn部署上传多次证书 偶尔报 The CertName already exists的问题 ([72f850f](https://github.com/certd/certd/commit/72f850f675b500d12ebff2338d1b99d6fab476e1))
* **cert-plugin:** 优化又拍云客户端错误处理逻辑,当域名已绑定证书时不再抛出异常。 ([92c9ac3](https://github.com/certd/certd/commit/92c9ac382692e6c84140ff787759ab6d39ccbe96))
* esxi部署失败的bug ([1e44115](https://github.com/certd/certd/commit/1e441154617e6516a9a3610412bf597128c62696))
### Features
* 支持企业级管理模式,项目管理,细分权限 ([3734083](https://github.com/certd/certd/commit/37340838b6a61a94b86bfa13cf5da88b26f1315a))
### Performance Improvements
* 【破坏性更新】错误返回信息msg字段名统一改成message,与成功的返回结构一致 ([51ab6d6](https://github.com/certd/certd/commit/51ab6d6da1bb551b55b3a6a4a9a945c8d6ace806))
* 当域名管理中没有域名时,创建流水线时不展开域名选择框 ([bb0afe1](https://github.com/certd/certd/commit/bb0afe1fa7b0fc52fde051d24fbe6be69d52f4cc))
* 任务步骤页面增加串行执行提示说明 ([787f6ef](https://github.com/certd/certd/commit/787f6ef52893d8dc912ee2a7a5b8ce2b73c108c9))
* 站点监控支持指定ip地址检查 ([83d81b6](https://github.com/certd/certd/commit/83d81b64b3adb375366039e07c87d1ad79121c13))
* AI开发插件 skills 定义初步 ([1f68fad](https://github.com/certd/certd/commit/1f68faddb97a978c5a5e731a8895b4bb0587ad83))
* http请求增加建立连接超时配置 ([3c85602](https://github.com/certd/certd/commit/3c85602ab1fc1953cdc06a6cd75a971d14119179))
## [1.38.12](https://github.com/certd/certd/compare/v1.38.11...v1.38.12) (2026-02-18)
### Bug Fixes
+7 -2
View File
@@ -16,7 +16,6 @@ https://1panel.cn/docs/installation/online_installation/
![](./images/store-1.png)
![](./images/store-2.png)
#### 1.2 访问测试:
@@ -40,6 +39,9 @@ admin/123456
1. 打开`docker-compose.yaml`,整个内容复制下来
https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml
::: tip
默认使用SQLite数据库,如果需要使用MySQL、PostgreSQL数据库,请参考[多数据库支持](./install/database.md)
:::
2. 然后到 `1Panel->容器->编排->新建编排`
输入名称,粘贴`docker-compose.yaml`原文内容
@@ -49,7 +51,10 @@ admin/123456
![](./images/2.png)
> 默认使用sqlite数据库,数据保存在`/data/certd`目录下,您可以手动备份该目录
> certd还支持`mysql`和`postgresql`数据库,[点我了解如何切换其他数据库](../database)
#### 2.2 访问测试
+3 -1
View File
@@ -30,7 +30,9 @@
点击确定,等待启动完成
![](./images/2.png)
> certd默认使用sqlite数据库,另外支持`mysql`和`postgresql`数据库,[点我了解如何切换其他数据库](../database)
::: tip
默认安装使用SQLite数据库,如果需要使用MySQL、PostgreSQL数据库,请参考[多数据库支持](./install/database.md)
:::
## 二、访问应用
+3 -2
View File
@@ -42,8 +42,9 @@ docker compose up -d
> 如果提示 没有docker compose命令,请安装docker-compose
> https://docs.docker.com/compose/install/linux/
> certd默认使用sqlite数据库,另外还支持`mysql`和`postgresql`数据库,[点我了解如何切换其他数据库](../database)
::: tip
默认安装使用SQLite数据库,如果需要使用MySQL、PostgreSQL数据库,请参考[多数据库支持](./install/database.md)
:::
### 3. 访问测试
+5
View File
@@ -28,6 +28,11 @@ https://certd.handsfree.work/
2. [Docker方式部署](./install/docker/)
3. [源码方式部署](./install/source/)
::: tip
默认安装使用SQLite数据库,如果需要使用MySQL、PostgreSQL数据库,请参考[多数据库支持](./install/database.md)
:::
### 2. 访问测试
+1 -1
View File
@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.39.0"
"version": "1.39.1"
}
+1
View File
@@ -37,6 +37,7 @@
"docs:preview": "vitepress preview docs",
"pub": "echo 1",
"dev": "pnpm run -r --parallel compile ",
"pub_all":"pnpm run -r --parallel pub ",
"release": "time /t >trigger/release.trigger && git add trigger/release.trigger && git commit -m \"build: release\" && git push",
"publish_to_atomgit": "node --experimental-json-modules ./scripts/publish-atomgit.js",
"publish_to_gitee": "node --experimental-json-modules ./scripts/publish-gitee.js",
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/publishlab/node-acme-client/compare/v1.39.0...v1.39.1) (2026-03-09)
**Note:** Version bump only for package @certd/acme-client
# [1.39.0](https://github.com/publishlab/node-acme-client/compare/v1.38.12...v1.39.0) (2026-03-07)
**Note:** Version bump only for package @certd/acme-client
+3 -3
View File
@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.39.0",
"version": "1.39.1",
"type": "module",
"module": "scr/index.js",
"main": "src/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.39.0",
"@certd/basic": "^1.39.1",
"@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5",
"axios": "^1.9.0",
@@ -70,5 +70,5 @@
"bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues"
},
"gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2"
"gitHead": "590ff67fcb40ff8ba0f7b2a9592b51d9fb54a2ef"
}
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
**Note:** Version bump only for package @certd/basic
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
### Bug Fixes
+1 -1
View File
@@ -1 +1 @@
01:15
23:44
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.39.0",
"version": "1.39.1",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -47,5 +47,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2"
"gitHead": "590ff67fcb40ff8ba0f7b2a9592b51d9fb54a2ef"
}
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
**Note:** Version bump only for package @certd/pipeline
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
**Note:** Version bump only for package @certd/pipeline
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.39.0",
"version": "1.39.1",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -18,8 +18,8 @@
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/basic": "^1.39.0",
"@certd/plus-core": "^1.39.0",
"@certd/basic": "^1.39.1",
"@certd/plus-core": "^1.39.1",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"reflect-metadata": "^0.1.13"
@@ -45,5 +45,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2"
"gitHead": "590ff67fcb40ff8ba0f7b2a9592b51d9fb54a2ef"
}
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
**Note:** Version bump only for package @certd/lib-huawei
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
**Note:** Version bump only for package @certd/lib-huawei
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.39.0",
"version": "1.39.1",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -24,5 +24,5 @@
"prettier": "^2.8.8",
"tslib": "^2.8.1"
},
"gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2"
"gitHead": "590ff67fcb40ff8ba0f7b2a9592b51d9fb54a2ef"
}
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
**Note:** Version bump only for package @certd/lib-iframe
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
**Note:** Version bump only for package @certd/lib-iframe
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.39.0",
"version": "1.39.1",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -31,5 +31,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2"
"gitHead": "590ff67fcb40ff8ba0f7b2a9592b51d9fb54a2ef"
}
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
**Note:** Version bump only for package @certd/jdcloud
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
**Note:** Version bump only for package @certd/jdcloud
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/jdcloud",
"version": "1.39.0",
"version": "1.39.1",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
@@ -56,5 +56,5 @@
"fetch"
]
},
"gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2"
"gitHead": "590ff67fcb40ff8ba0f7b2a9592b51d9fb54a2ef"
}
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
**Note:** Version bump only for package @certd/lib-k8s
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
**Note:** Version bump only for package @certd/lib-k8s
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-k8s",
"private": false,
"version": "1.39.0",
"version": "1.39.1",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -17,7 +17,7 @@
"pub": "npm publish"
},
"dependencies": {
"@certd/basic": "^1.39.0",
"@certd/basic": "^1.39.1",
"@kubernetes/client-node": "0.21.0"
},
"devDependencies": {
@@ -32,5 +32,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2"
"gitHead": "590ff67fcb40ff8ba0f7b2a9592b51d9fb54a2ef"
}
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
**Note:** Version bump only for package @certd/lib-server
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
### Performance Improvements
+7 -7
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.39.0",
"version": "1.39.1",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -28,11 +28,11 @@
],
"license": "AGPL",
"dependencies": {
"@certd/acme-client": "^1.39.0",
"@certd/basic": "^1.39.0",
"@certd/pipeline": "^1.39.0",
"@certd/plugin-lib": "^1.39.0",
"@certd/plus-core": "^1.39.0",
"@certd/acme-client": "^1.39.1",
"@certd/basic": "^1.39.1",
"@certd/pipeline": "^1.39.1",
"@certd/plugin-lib": "^1.39.1",
"@certd/plus-core": "^1.39.1",
"@midwayjs/cache": "3.14.0",
"@midwayjs/core": "3.20.11",
"@midwayjs/i18n": "3.20.13",
@@ -64,5 +64,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2"
"gitHead": "590ff67fcb40ff8ba0f7b2a9592b51d9fb54a2ef"
}
@@ -88,6 +88,10 @@ export class SysPrivateSettings extends BaseSettings {
pipelineMaxRunningCount?: number;
environmentVars?: string = '';
sms?: {
type?: string;
config?: any;
@@ -11,6 +11,8 @@ import { BaseService, setAdminMode } from '../../../basic/index.js';
import { executorQueue } from '../../basic/service/executor-queue.js';
import { isComm } from '@certd/plus-core';
const { merge } = mergeUtils;
let lastSaveEnvVars = {};
/**
* 设置
*/
@@ -117,12 +119,12 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
}
async savePublicSettings(bean: SysPublicSettings) {
if(isComm()){
if(bean.adminMode === 'enterprise'){
if (isComm()) {
if (bean.adminMode === 'enterprise') {
throw new Error("商业版不支持使用企业管理模式")
}
}
await this.saveSetting(bean);
//让设置生效
await this.reloadPublicSettings();
@@ -173,6 +175,44 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
}
setSslProviderReverseProxies(privateSetting.reverseProxies);
//加载环境变量
this.setEnvironmentVars(privateSetting.environmentVars);
}
setEnvironmentVars(vars: string) {
const envVars = {}
if (typeof vars !== 'string') {
vars = ""
}
vars.split('\n').forEach(line => {
line = line.trim();
if (!line || line.startsWith('#')) {
return
}
const arr = line.split("#")
if (arr.length > 0) {
line = arr[0].trim();
}
if (!line.includes("=")) {
return
}
const [key, value] = line.split('=');
if (key && value) {
envVars[key.trim()] = value.trim();
}
});
//先删除旧环境变量
if (lastSaveEnvVars) {
for (const key in lastSaveEnvVars) {
delete process.env[key];
}
}
merge(process.env, envVars);
lastSaveEnvVars = envVars;
}
async updateByKey(key: string, setting: any) {
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
**Note:** Version bump only for package @certd/midway-flyway-js
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
**Note:** Version bump only for package @certd/midway-flyway-js
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
"version": "1.39.0",
"version": "1.39.1",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -46,5 +46,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2"
"gitHead": "590ff67fcb40ff8ba0f7b2a9592b51d9fb54a2ef"
}
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
**Note:** Version bump only for package @certd/plugin-cert
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
**Note:** Version bump only for package @certd/plugin-cert
+6 -6
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.39.0",
"version": "1.39.1",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -17,10 +17,10 @@
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/acme-client": "^1.39.0",
"@certd/basic": "^1.39.0",
"@certd/pipeline": "^1.39.0",
"@certd/plugin-lib": "^1.39.0",
"@certd/acme-client": "^1.39.1",
"@certd/basic": "^1.39.1",
"@certd/pipeline": "^1.39.1",
"@certd/plugin-lib": "^1.39.1",
"psl": "^1.9.0",
"punycode.js": "^2.3.1"
},
@@ -38,5 +38,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2"
"gitHead": "590ff67fcb40ff8ba0f7b2a9592b51d9fb54a2ef"
}
+4
View File
@@ -3,6 +3,10 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
**Note:** Version bump only for package @certd/plugin-lib
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
**Note:** Version bump only for package @certd/plugin-lib
+6 -6
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
"version": "1.39.0",
"version": "1.39.1",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -22,10 +22,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.0",
"@certd/basic": "^1.39.0",
"@certd/pipeline": "^1.39.0",
"@certd/plus-core": "^1.39.0",
"@certd/acme-client": "^1.39.1",
"@certd/basic": "^1.39.1",
"@certd/pipeline": "^1.39.1",
"@certd/plus-core": "^1.39.1",
"@kubernetes/client-node": "0.21.0",
"ali-oss": "^6.22.0",
"basic-ftp": "^5.0.5",
@@ -57,5 +57,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "49457505cdf8156fd9d936b8e9ace0b48e43a6b2"
"gitHead": "590ff67fcb40ff8ba0f7b2a9592b51d9fb54a2ef"
}
+4
View File
@@ -38,6 +38,10 @@ COPY ./patch/ssh2/*.js /app/node_modules/.pnpm/node_modules/ssh2/lib/protocol/
ENV LEGO_VERSION=4.30.1
ENV LEGO_DOWNLOAD_DIR=/app/tools/lego
ENV ALIYUN_CLIENT_CONNECT_TIMEOUT=10000
ENV ALIYUN_CLIENT_READ_TIMEOUT=20000
RUN mkdir -p $LEGO_DOWNLOAD_DIR
# 根据架构下载不同的文件
+10
View File
@@ -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.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
### Bug Fixes
* 修复企业管理模式下,切换用户登录后,丢失项目列表的bug ([d23c8b4](https://github.com/certd/certd/commit/d23c8b4a2a5f5ab17822c6ee1d4108ac7280b9d1))
### Performance Improvements
* 支持迁移个人数据到企业项目中 ([c6ca832](https://github.com/certd/certd/commit/c6ca83273779ed84de1b23b5e477063af043d015))
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
### Bug Fixes
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/ui-client",
"version": "1.39.0",
"version": "1.39.1",
"private": true,
"scripts": {
"dev": "vite --open",
@@ -106,8 +106,8 @@
"zod-defaults": "^0.1.3"
},
"devDependencies": {
"@certd/lib-iframe": "^1.39.0",
"@certd/pipeline": "^1.39.0",
"@certd/lib-iframe": "^1.39.1",
"@certd/pipeline": "^1.39.1",
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12",
@@ -1,7 +1,7 @@
<template>
<div class="remote-select">
<div class="flex flex-row">
<a-select class="remote-select-input" show-search :filter-option="filterOption" :options="optionsRef" :value="value" v-bind="attrs" @click="onClick" @update:value="emit('update:value', $event)">
<a-select class="remote-select-input" show-search mode="tags" :filter-option="filterOption" :options="optionsRef" :value="value" v-bind="attrs" @click="onClick" @update:value="updateValue($event)">
<template #dropdownRender="{ menuNode: menu }">
<template v-if="search">
<div class="flex w-full" style="padding: 4px 8px">
@@ -61,6 +61,7 @@ const props = defineProps<
watches?: string[];
search?: boolean;
pager?: boolean;
multi?: boolean;
} & ComponentPropsType
>();
@@ -68,6 +69,15 @@ const emit = defineEmits<{
"update:value": any;
}>();
function updateValue(value: any) {
if (props.multi) {
emit("update:value", value);
} else {
const last = value?.[value.length - 1];
emit("update:value", last);
}
}
const attrs = useAttrs();
const getCurrentPluginDefine: any = inject("getCurrentPluginDefine", () => {
@@ -80,6 +90,7 @@ const getPluginType: any = inject("get:plugin:type", () => {
return "plugin";
});
debugger;
const searchKeyRef = ref("");
const optionsRef = ref([]);
const message = ref("");
@@ -68,6 +68,14 @@ export default {
smsTab: "Login via SMS code",
passwordTab: "Password login",
passkeyTab: "Passkey Login",
passkeyLogin: "Passkey Login",
passkeyHelper: "Login with your biometric or security key",
passkeyNotSupported: "Your browser does not support Passkey",
passkeyRegister: "Register Passkey",
passkeyRegistered: "Passkey Registered",
passkeyRegisterSuccess: "Passkey registered successfully",
passkeyRegisterFailed: "Passkey registration failed",
title: "Change Password",
weakPasswordWarning: "For your account security, please change your password immediately",
changeNow: "Change Now",
@@ -90,4 +98,7 @@ export default {
updateProfile: "Update Profile",
oauthLoginTitle: "Other ways of login",
oauthOnlyLoginTitle: "Login",
registerPasskey: "Register Passkey",
deviceName: "Device Name",
deviceNameHelper: "Please enter the device name used to identify the device",
};
@@ -789,6 +789,8 @@ export default {
reverseProxyHelper: "Reverse proxy for ACME address, used when applying for certificate",
reverseProxyPlaceholder: "http://le.px.handfree.work",
reverseProxyEmpty: "No reverse proxy list configured",
environmentVars: "Environment Variables",
environmentVarsHelper: "configure the runtime environment variables, one per line, format: KEY=VALUE",
},
},
modal: {
@@ -68,6 +68,14 @@ export default {
smsTab: "手机号登录/注册",
passwordTab: "密码登录",
passkeyTab: "Passkey登录",
passkeyLogin: "Passkey登录",
passkeyHelper: "使用您的生物识别或安全密钥登录",
passkeyNotSupported: "您的浏览器不支持Passkey",
passkeyRegister: "注册Passkey",
passkeyRegistered: "Passkey已注册",
passkeyRegisterSuccess: "Passkey注册成功",
passkeyRegisterFailed: "Passkey注册失败",
title: "修改密码",
weakPasswordWarning: "为了您的账户安全,请立即修改密码",
@@ -92,4 +100,7 @@ export default {
oauthLoginTitle: "其他登录方式",
oauthOnlyLoginTitle: "登录",
registerPasskey: "注册Passkey",
deviceName: "设备名称",
deviceNameHelper: "请输入当前设备名称,绑定多个时好做区分",
};
@@ -800,6 +800,8 @@ export default {
reverseProxyHelper: "证书颁发机构ACME地址的反向代理,在申请证书时自动使用",
reverseProxyPlaceholder: "http://le.px.handfree.work",
reverseProxyEmpty: "未配置反向代理",
environmentVars: "环境变量",
environmentVarsHelper: "配置运行时环境变量,每行一个,格式:KEY=VALUE",
},
},
modal: {
@@ -127,6 +127,21 @@ export const certdResources = [
keepAlive: true,
},
},
{
title: "certd.sysResources.currentProject",
name: "ProjectMemberManager",
path: "/certd/project/detail",
component: "/certd/project/detail/index.vue",
meta: {
show: () => {
const projectStore = useProjectStore();
return projectStore.isEnterprise;
},
isMenu: true,
icon: "ion:apps",
auth: true,
},
},
{
title: "certd.settings",
name: "MineSetting",
@@ -260,22 +275,7 @@ export const certdResources = [
meta: {
icon: "ion:person-outline",
auth: true,
isMenu: false,
},
},
{
title: "certd.sysResources.projectMemberManager",
name: "ProjectMemberManager",
path: "/certd/project/detail",
component: "/certd/project/detail/index.vue",
meta: {
show: () => {
const projectStore = useProjectStore();
return projectStore.isEnterprise;
},
isMenu: true,
icon: "ion:apps",
auth: true,
},
},
],
@@ -15,11 +15,18 @@ export type ProjectItem = {
export const useProjectStore = defineStore("app.project", () => {
const myProjects = ref([]);
const inited = ref(false);
const currentProjectId = ref(); // 直接调用
function $reset() {
myProjects.value = [];
currentProjectId.value = "";
inited.value = false;
}
const userStore = useUserStore();
const userId = userStore.getUserInfo?.id;
const lastProjectIdCacheKey = "currentProjectId:" + userId;
const lastProjectId = LocalStorage.get(lastProjectIdCacheKey);
const currentProjectId = ref(lastProjectId); // 直接调用
currentProjectId.value = lastProjectId;
const projects = computed(() => {
return myProjects.value;
@@ -118,11 +125,6 @@ export const useProjectStore = defineStore("app.project", () => {
return false;
}
function $reset() {
myProjects.value = [];
currentProjectId.value = "";
}
return {
projects,
myProjects,
@@ -101,6 +101,8 @@ export type SysPrivateSetting = {
commonCnameEnabled?: boolean;
// 同一个用户同时最大运行流水线数量
pipelineMaxRunningCount?: number;
// 环境变量
environmentVars?: string;
sms?: {
type?: string;
@@ -107,3 +107,42 @@ export async function OauthProviders() {
method: "post",
});
}
export async function generatePasskeyRegistrationOptions() {
return await request({
url: "/passkey/generateRegistration",
method: "post",
});
}
export async function verifyPasskeyRegistration(userId: number, response: any, challenge: string) {
return await request({
url: "/passkey/verifyRegistration",
method: "post",
data: { userId, response, challenge },
});
}
export async function generatePasskeyAuthenticationOptions(userId: number) {
return await request({
url: "/passkey/generateAuthentication",
method: "post",
data: { userId },
});
}
export async function loginByPasskey(form: { userId: number; credential: any; challenge: string }) {
return await request({
url: "/loginByPasskey",
method: "post",
data: form,
});
}
export async function registerPasskey(form: { userId: number; response: any; challenge: string }) {
return await request({
url: "/passkey/register",
method: "post",
data: form,
});
}
@@ -92,6 +92,16 @@ export const useUserStore = defineStore({
const loginRes = await UserApi.loginByTwoFactor(form);
return await this.onLoginSuccess(loginRes);
},
async loginByPasskey(form: any) {
const loginRes = await UserApi.loginByPasskey(form);
return await this.onLoginSuccess(loginRes);
},
async registerPasskey(form: any) {
return await UserApi.registerPasskey(form);
},
async getUserInfoAction(): Promise<UserInfoRes> {
const userInfo = await UserApi.mine();
this.setUserInfo(userInfo);
+14 -8
View File
@@ -1,4 +1,5 @@
import { useFormWrapper } from "@fast-crud/fast-crud";
import { merge } from "lodash-es";
export type FormOptionReq = {
title: string;
@@ -7,6 +8,7 @@ export type FormOptionReq = {
body?: any;
initialForm?: any;
zIndex?: number;
wrapper?: any;
};
export function useFormDialog() {
@@ -14,19 +16,23 @@ export function useFormDialog() {
async function openFormDialog(req: FormOptionReq) {
function createCrudOptions() {
const warpper = merge(
{
zIndex: req.zIndex,
title: req.title,
saveRemind: false,
slots: {
"form-body-top": req.body,
},
},
req.wrapper
);
return {
crudOptions: {
columns: req.columns,
form: {
initialForm: req.initialForm,
wrapper: {
zIndex: req.zIndex,
title: req.title,
saveRemind: false,
slots: {
"form-body-top": req.body,
},
},
wrapper: warpper,
async afterSubmit() {},
async doSubmit({ form }: any) {
if (req.onSubmit) {
@@ -1,5 +1,5 @@
import * as _ from "lodash-es";
import { asyncCompute, compute } from "@fast-crud/fast-crud";
import { merge } from "lodash-es";
import { computed } from "vue";
export type MergeScriptContext = {
@@ -18,7 +18,7 @@ export function useReference(formItem: any) {
const script = formItem.mergeScript;
const func = new Function("ctx", script);
const merged = func(ctx);
_.merge(formItem, merged);
merge(formItem, merged);
delete formItem.mergeScript;
}
@@ -55,3 +55,94 @@ export async function OauthBoundUrl(type: string) {
},
});
}
export async function GetPasskeys() {
return await request({
url: "/mine/passkeys",
method: "POST",
});
}
export async function UnbindPasskey(id: number) {
return await request({
url: "/mine/unbindPasskey",
method: "POST",
data: { id },
});
}
export interface PasskeyRegistrationOptions {
rp: {
name: string;
id: string;
};
user: {
id: Uint8Array;
name: string;
displayName: string;
};
challenge: string;
pubKeyCredParams: {
type: string;
alg: number;
}[];
timeout: number;
attestation: string;
excludeCredentials: any[];
}
export interface PasskeyAuthenticationOptions {
rpId: string;
challenge: string;
timeout: number;
allowCredentials: any[];
}
export interface PasskeyCredential {
id: string;
type: string;
rawId: string;
response: {
attestationObject: string;
clientDataJSON: string;
};
}
export async function generatePasskeyRegistrationOptions() {
return await request({
url: "/passkey/generateRegistration",
method: "post",
});
}
export async function verifyPasskeyRegistration(userId: number, response: any, challenge: string, deviceName: string) {
return await request({
url: "/passkey/verifyRegistration",
method: "post",
data: { userId, response, challenge, deviceName },
});
}
export async function generatePasskeyAuthenticationOptions(userId: number) {
return await request({
url: "/passkey/generateAuthentication",
method: "post",
data: { userId },
});
}
export async function loginByPasskey(userId: number, credential: any, challenge: string) {
return await request({
url: "/passkey/login",
method: "post",
data: { userId, credential, challenge },
});
}
export async function registerPasskey(userId: number, response: any, challenge: string) {
return await request({
url: "/passkey/register",
method: "post",
data: { userId, response, challenge },
});
}
@@ -108,3 +108,54 @@ export function useUserProfile() {
openEditProfileDialog,
};
}
export function usePasskeyRegister() {
const { openCrudFormDialog } = useFormWrapper();
const wrapperRef = ref();
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,
},
columns: {
deviceName: {
title: t("authentication.deviceName"),
type: "text",
form: {
component: {
class: "w-full",
},
col: {
span: 24,
},
helper: t("authentication.deviceNameHelper"),
rules: [{ required: true, message: t("authentication.deviceName") }],
},
},
},
};
const wrapper = await openCrudFormDialog({ crudOptions });
wrapperRef.value = wrapper;
return wrapper;
}
return {
openRegisterDialog,
};
}
@@ -15,6 +15,9 @@
</a-descriptions-item>
<a-descriptions-item :label="t('authentication.email')">{{ userInfo.email }}</a-descriptions-item>
<a-descriptions-item :label="t('authentication.phoneNumber')">{{ userInfo.phoneCode }}{{ userInfo.mobile }}</a-descriptions-item>
<a-descriptions-item label="角色">
<fs-values-format :model-value="userInfo.roleIds" :dict="roleDict" />
</a-descriptions-item>
<a-descriptions-item v-if="settingStore.sysPublic.oauthEnabled && settingStore.isPlus" label="第三方账号绑定">
<template v-for="item in computedOauthBounds" :key="item.name">
<div v-if="item.addonId" class="flex items-center gap-2 mb-2">
@@ -25,6 +28,21 @@
</div>
</template>
</a-descriptions-item>
<a-descriptions-item label="Passkey">
<div v-if="passkeys.length > 0" class="flex flex-col gap-2">
<div v-for="passkey in passkeys" :key="passkey.id" class="flex items-center gap-4 p-2 border-b">
<fs-icon icon="ion:finger-print" class="text-blue-500 fs-24" />
<span class="w-40 truncate" :title="passkey.passkeyId">{{ passkey.deviceName }}</span>
<span class="text-sm text-gray-500">{{ formatDate(passkey.registeredAt) }}</span>
<a-button type="primary" danger @click="unbindPasskey(passkey.id)">解绑</a-button>
</div>
</div>
<div v-else class="text-gray-500">暂无Passkey</div>
<a-button v-if="passkeySupported" type="primary" class="mt-2" @click="registerPasskey">注册Passkey</a-button>
<div v-if="!passkeySupported" class="text-red-500 text-sm mt-2">
{{ t("authentication.passkeyNotSupported") }}
</div>
</a-descriptions-item>
<a-descriptions-item :label="t('common.handle')">
<a-button type="primary" @click="doUpdate">{{ t("authentication.updateProfile") }}</a-button>
<change-password-button class="ml-10" :show-button="true"> </change-password-button>
@@ -40,9 +58,11 @@ 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 { Modal } from "ant-design-vue";
import { usePasskeyRegister } from "./use";
import { message, Modal } from "ant-design-vue";
import { useSettingStore } from "/@/store/settings";
import { isEmpty } from "lodash-es";
import { dict } from "@fast-crud/fast-crud";
const { t } = useI18n();
@@ -53,11 +73,20 @@ defineOptions({
const settingStore = useSettingStore();
const userInfo: Ref = ref({});
const passkeys = ref([]);
const passkeySupported = ref(false);
const getUserInfo = async () => {
userInfo.value = await api.getMineInfo();
};
const roleDict = dict({
url: "/basic/user/getSimpleRoles",
value: "id",
label: "name",
});
const { openEditProfileDialog } = useUserProfile();
const { openRegisterDialog } = usePasskeyRegister();
function doUpdate() {
openEditProfileDialog({
@@ -69,10 +98,12 @@ function doUpdate() {
const oauthBounds = ref([]);
const oauthProviders = ref([]);
async function loadOauthBounds() {
const res = await api.GetOauthBounds();
oauthBounds.value = res;
}
async function loadOauthProviders() {
const res = await api.GetOauthProviders();
oauthProviders.value = res;
@@ -102,12 +133,116 @@ async function unbind(type: string) {
}
async function bind(type: string) {
//URL
const res = await api.OauthBoundUrl(type);
const loginUrl = res.loginUrl;
window.location.href = loginUrl;
}
async function loadPasskeys() {
try {
const res = await api.GetPasskeys();
passkeys.value = res;
} catch (e: any) {
console.error("加载Passkey失败:", e);
}
}
async function unbindPasskey(id: number) {
Modal.confirm({
title: "确认解绑吗?",
okText: "确认",
okType: "danger",
onOk: async () => {
await api.UnbindPasskey(id);
await loadPasskeys();
},
});
}
const toBase64Url = (buffer: ArrayBuffer) => {
const bytes = new Uint8Array(buffer);
let binary = "";
for (let i = 0; i < bytes.length; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
};
async function registerPasskey() {
if (!passkeySupported.value) {
Modal.error({ title: "错误", content: "浏览器不支持Passkey" });
return;
}
await openRegisterDialog({
onSubmit: async (ctx: any) => {
const deviceName = ctx.form.deviceName;
if (!deviceName) {
return;
}
await doRegisterPasskey(deviceName);
message.success("Passkey注册成功");
},
});
}
async function doRegisterPasskey(deviceName: string) {
try {
const res: any = await api.generatePasskeyRegistrationOptions();
const options = res;
navigator.credentials.query({
publicKey: options,
});
const credential = await (navigator.credentials as any).create({
publicKey: {
challenge: Uint8Array.from(atob(options.challenge.replace(/-/g, "+").replace(/_/g, "/")), c => c.charCodeAt(0)),
rp: options.rp,
pubKeyCredParams: options.pubKeyCredParams,
timeout: options.timeout || 60000,
attestation: options.attestation,
excludeCredentials: options.excludeCredentials || [],
user: {
id: Uint8Array.from([res.userId]),
name: userInfo.value.username,
displayName: deviceName,
},
},
});
if (!credential) {
throw new Error("Passkey注册失败");
}
const response = {
id: credential.id,
type: credential.type,
rawId: toBase64Url(credential.rawId),
response: {
attestationObject: toBase64Url(credential.response.attestationObject),
clientDataJSON: toBase64Url(credential.response.clientDataJSON),
},
};
const verifyRes: any = await api.verifyPasskeyRegistration(userInfo.value.id, response, options.challenge, deviceName);
await loadPasskeys();
} catch (e: any) {
console.error("Passkey注册失败:", e);
Modal.error({ title: "错误", content: e.message || "Passkey注册失败" });
}
}
const formatDate = (dateString: string) => {
if (!dateString) return "";
return new Date(dateString).toLocaleString("zh-CN");
};
const checkPasskeySupport = () => {
passkeySupported.value = false;
if (typeof window !== "undefined" && "credentials" in navigator && "PublicKeyCredential" in window) {
passkeySupported.value = true;
}
};
const userAvatar = computed(() => {
if (isEmpty(userInfo.value.avatar)) {
return "";
@@ -123,5 +258,7 @@ onMounted(async () => {
await getUserInfo();
await loadOauthBounds();
await loadOauthProviders();
await loadPasskeys();
checkPasskeySupport();
});
</script>
@@ -6,7 +6,7 @@ import { useRouter } from "vue-router";
import { compute, CreateCrudOptionsRet, dict, useFormWrapper } from "@fast-crud/fast-crud";
import NotificationSelector from "/@/views/certd/notification/notification-selector/index.vue";
import { useReference } from "/@/use/use-refrence";
import { computed, ref } from "vue";
import { computed, provide, Ref, ref } from "vue";
import * as api from "../api";
import { PluginGroup, usePluginStore } from "/@/store/plugin";
import { createNotificationApi } from "/@/views/certd/notification/api";
@@ -89,9 +89,10 @@ export function useCertPipelineCreator() {
const inputs: any = {};
const moreParams = [];
const doSubmit = req.doSubmit;
for (const inputKey in req.certPlugin.input) {
const certPlugin = req.certPlugin;
for (const inputKey in certPlugin.input) {
// inputs[inputKey].form.show = true;
const inputDefine = cloneDeep(req.certPlugin.input[inputKey]);
const inputDefine = cloneDeep(certPlugin.input[inputKey]);
if (inputDefine.maybeNeed) {
moreParams.push(inputKey);
}
@@ -103,7 +104,6 @@ export function useCertPipelineCreator() {
},
};
}
const pluginStore = usePluginStore();
const randomHour = Math.floor(Math.random() * 6);
const randomMin = Math.floor(Math.random() * 60);
@@ -322,7 +322,7 @@ export function useCertPipelineCreator() {
return certPlugins;
}
async function openAddCertdPipelineDialog(req: { pluginName: string; defaultGroupId?: number; title?: string }) {
async function openAddCertdPipelineDialog(req: { pluginName: string; defaultGroupId?: number; title?: string; currentPluginRef: Ref<any> }) {
//检查是否流水线数量超出限制
await checkPipelineLimit();
@@ -393,6 +393,8 @@ export function useCertPipelineCreator() {
message.error("该证书申请插件不存在");
return;
}
req.currentPluginRef.value = certPlugin;
const { crudOptions } = createCrudOptions({
certPlugin,
doSubmit,
@@ -52,7 +52,7 @@
</template>
<script lang="ts" setup>
import { computed, onActivated, onMounted, ref } from "vue";
import { computed, onActivated, onMounted, provide, ref } from "vue";
import { dict, useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import ChangeGroup from "./components/change-group.vue";
@@ -69,6 +69,7 @@ import { groupDictRef } from "./group/dicts";
import { useCertPipelineCreator } from "./certd-form/use";
import { useRouter } from "vue-router";
import { useCrudPermission } from "/@/plugin/permission";
import CertdForm from "./certd-form/certd-form.vue";
defineOptions({
name: "PipelineManager",
@@ -79,11 +80,17 @@ const context: any = {
selectedRowKeys,
};
const router = useRouter();
const { openAddCertdPipelineDialog } = useCertPipelineCreator();
function onActionbarMoreItemClick(req: { key: string; item: any }) {
openCertApplyDialog({ key: req.key, title: req.item?.title });
}
const certdFormRef = ref<typeof CertdForm>();
const currentPluginRef = ref();
provide("getCurrentPluginDefine", () => {
return currentPluginRef.value;
});
const addMorePipelineBtns = computed(() => {
return [
{ key: "CertApplyGetFormAliyun", title: t("certd.pipelinePage.aliyunSubscriptionPipeline"), icon: "svg:icon-aliyun" },
@@ -92,6 +99,7 @@ const addMorePipelineBtns = computed(() => {
{ key: "BatchAddPipeline", title: t("certd.pipelinePage.batchAddPipeline"), icon: "ion:duplicate" },
];
});
const { openAddCertdPipelineDialog } = useCertPipelineCreator();
function openCertApplyDialog(req: { key: string; title: string }) {
if (req.key === "AddPipeline") {
crudExpose.openAdd({});
@@ -104,7 +112,7 @@ function openCertApplyDialog(req: { key: string; title: string }) {
const searchForm = crudExpose.getSearchValidatedFormData();
const defaultGroupId = searchForm.groupId;
openAddCertdPipelineDialog({ pluginName: req.key, defaultGroupId, title: req.title });
openAddCertdPipelineDialog({ pluginName: req.key, defaultGroupId, title: req.title, currentPluginRef });
}
context.openCertApplyDialog = openCertApplyDialog;
context.permission = { isProjectPermission: true };
@@ -294,13 +294,9 @@ function useStepForm() {
currentStep.value.input[key] = pluginSysConfig.sysSetting?.input[key];
}
}
console.log("currentStepTypeChanged:", currentStep.value);
console.log("currentStepPlugin:", currentPlugin.value);
};
const stepSave = async (e: any) => {
console.log("currentStepSave", currentStep.value);
try {
await stepFormRef.value.validate();
} catch (e) {
@@ -73,3 +73,17 @@ export async function ApproveJoin(form: any) {
data: form,
});
}
export async function GetSelfResources() {
return await request({
url: "/enterprise/transfer/selfResources",
method: "post",
});
}
export async function TransferResources() {
return await request({
url: "/enterprise/transfer/doTransfer",
method: "post",
});
}
@@ -9,8 +9,12 @@
<fs-values-format :model-value="project.permission" :dict="projectPermissionDict"></fs-values-format>
<a-divider type="vertical"></a-divider>
<fs-values-format :model-value="project.status" :dict="projectMemberStatusDict"></fs-values-format> -->
<a-divider type="vertical"></a-divider>
<a-button class="mr-5" type="primary" @click="openTransferDialog">个人数据迁移</a-button>
<a-button v-if="userStore.isAdmin" type="primary" @click="goProjectManager">{{ t("certd.project.projectManage") }}</a-button>
</span>
</div>
<div class="more"></div>
</template>
<fs-crud ref="crudRef" v-bind="crudBinding">
<template #pagination-left>
@@ -29,11 +33,13 @@ import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales";
import { useRoute } from "vue-router";
import { useRoute, useRouter } from "vue-router";
import { useProjectStore } from "/@/store/project";
import { request } from "/@/api/service";
import { useDicts } from "../../dicts";
import { useCrudPermission } from "/@/plugin/permission";
import { useUserStore } from "/@/store/user";
import { useTransfer } from "./use";
const { t } = useI18n();
@@ -49,6 +55,14 @@ if (!projectId) {
projectId = projectStore.currentProject?.id;
}
const router = useRouter();
const userStore = useUserStore();
function goProjectManager() {
router.push(`/sys/enterprise/project`);
}
const { openTransferDialog } = useTransfer();
const { projectPermissionDict, projectMemberStatusDict, userDict } = useDicts();
const project: Ref<any> = ref({});
@@ -1,46 +0,0 @@
import { dict } from "@fast-crud/fast-crud";
import { useDicts } from "../../dicts";
import { useFormDialog } from "/@/use/use-dialog";
export function useApprove() {
const { openFormDialog } = useFormDialog();
const { projectPermissionDict, projectMemberStatusDict, userDict } = useDicts();
function openApproveDialog({ id, permission, onSubmit }: { id: any; permission: any; onSubmit: any }) {
openFormDialog({
title: "审批加入申请",
columns: {
permission: {
title: "成员权限",
type: "dict-select",
dict: projectPermissionDict,
},
status: {
title: "审批结果",
type: "dict-radio",
dict: dict({
data: [
{
label: "通过",
value: "approved",
},
{
label: "拒绝",
value: "rejected",
},
],
}),
},
},
onSubmit: onSubmit,
initialForm: {
id: id,
permission: permission,
status: "approved",
},
});
}
return {
openApproveDialog,
};
}
@@ -0,0 +1,134 @@
import { dict } from "@fast-crud/fast-crud";
import { useDicts } from "../../dicts";
import { useFormDialog } from "/@/use/use-dialog";
import * as api from "./api";
import { useProjectStore } from "/@/store/project";
import { message, Modal } from "ant-design-vue";
import { Ref, ref } from "vue";
export function useApprove() {
const { openFormDialog } = useFormDialog();
const { projectPermissionDict, projectMemberStatusDict, userDict } = useDicts();
function openApproveDialog({ id, permission, onSubmit }: { id: any; permission: any; onSubmit: any }) {
openFormDialog({
title: "审批加入申请",
columns: {
permission: {
title: "成员权限",
type: "dict-select",
dict: projectPermissionDict,
},
status: {
title: "审批结果",
type: "dict-radio",
dict: dict({
data: [
{
label: "通过",
value: "approved",
},
{
label: "拒绝",
value: "rejected",
},
],
}),
},
},
onSubmit: onSubmit,
initialForm: {
id: id,
permission: permission,
status: "approved",
},
});
}
return {
openApproveDialog,
};
}
export function useTransfer() {
const { openFormDialog } = useFormDialog();
async function doTransfer() {
Modal.confirm({
title: "请确认",
content: () => (
<div>
<p></p>
<p class="text-red-500"></p>
</div>
),
okText: "确认",
okType: "primary",
onOk: async () => {
await api.TransferResources();
message.success("迁移成功");
await loadMyResources();
},
});
}
const selfResources: Ref = ref({});
const projectStore = useProjectStore();
async function loadMyResources() {
selfResources.value = await api.GetSelfResources();
}
async function openTransferDialog() {
await loadMyResources();
openFormDialog({
title: "迁移我的个人资源到当前企业项目",
wrapper: {
buttons: {
ok: {
show: false,
},
reset: {
show: false,
},
},
},
body() {
return (
<div class="p-8">
<div class="flex flex-row items-center justify-evenly w-full">
<div>
<h3 class="text-lg font-bold"></h3>
<div class="mt-4">
<p>线{selfResources.value.pipeline}</p>
<p>线{selfResources.value.history}</p>
<p>线{selfResources.value.historyLog}</p>
<p>线{selfResources.value.pipelineGroup}</p>
<p>{selfResources.value.storage}</p>
<p>{selfResources.value.certInfo}</p>
<p>{selfResources.value.access}</p>
<p>{selfResources.value.siteMonitor}</p>
<p>{selfResources.value.notification}</p>
<p>{selfResources.value.group}</p>
<p>线{selfResources.value.template}</p>
<p>{selfResources.value.domain}</p>
<p>{selfResources.value.subdomain}</p>
<p>cname记录{selfResources.value.cnameRecord}</p>
</div>
</div>
<div class="text-2xl font-bold"> </div>
<div>"{projectStore.currentProject?.name}"</div>
</div>
<div class="flex flex-row items-center justify-center w-full">
<a-button type="primary" onClick={doTransfer}>
</a-button>
</div>
</div>
);
},
});
}
return {
openTransferDialog,
};
}
@@ -46,11 +46,21 @@
</a-form-item>
</template>
</a-tab-pane>
<a-tab-pane key="passkey" :tab="t('authentication.passkeyTab')">
<template v-if="formState.loginType === 'passkey'">
<div v-if="!passkeySupported" class="text-red-500 text-sm mt-2">
{{ t("authentication.passkeyNotSupported") }}
</div>
</template>
</a-tab-pane>
</a-tabs>
<a-form-item>
<a-button type="primary" size="large" html-type="button" :loading="loading" class="login-button" @click="handleFinish">
<a-button v-if="formState.loginType !== 'passkey'" type="primary" size="large" html-type="button" :loading="loading" class="login-button" @click="handleFinish">
{{ queryBindCode ? t("authentication.bindButton") : t("authentication.loginButton") }}
</a-button>
<a-button v-else type="primary" size="large" html-type="button" :loading="loading" class="login-button" :disabled="!passkeySupported" @click="handlePasskeyLogin">
{{ t("authentication.passkeyLogin") }}
</a-button>
</a-form-item>
<a-form-item>
<div class="mt-2 flex justify-between items-center">
@@ -94,8 +104,8 @@
</a-form>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, nextTick, reactive, ref, toRaw } from "vue";
<script lang="ts" setup>
import { computed, nextTick, reactive, ref, toRaw, onMounted } from "vue";
import { useUserStore } from "/src/store/user";
import { useSettingStore } from "/@/store/settings";
import { utils } from "@fast-crud/fast-crud";
@@ -103,193 +113,199 @@ import SmsCode from "/@/views/framework/login/sms-code.vue";
import { useI18n } from "/@/locales";
import { LanguageToggle } from "/@/vben/layouts";
import CaptchaInput from "/@/components/captcha/captcha-input.vue";
import { useRoute, useRouter } from "vue-router";
import { useRoute } from "vue-router";
import OauthFooter from "/@/views/framework/oauth/oauth-footer.vue";
import * as oauthApi from "../oauth/api";
import { notification } from "ant-design-vue";
export default defineComponent({
name: "LoginPage",
components: { LanguageToggle, SmsCode, CaptchaInput, OauthFooter },
setup() {
const { t } = useI18n();
const route = useRoute();
const userStore = useUserStore();
import { request } from "/src/api/service";
import * as UserApi from "/src/store/user/api.user";
const queryBindCode = ref(route.query.bindCode as string | undefined);
const { t } = useI18n();
const route = useRoute();
const userStore = useUserStore();
const queryOauthOnly = route.query.oauthOnly as string;
const urlLoginType = route.query.loginType as string | undefined;
const verifyCodeInputRef = ref();
const loading = ref(false);
const queryBindCode = ref(route.query.bindCode as string | undefined);
const queryOauthOnly = route.query.oauthOnly as string;
const urlLoginType = route.query.loginType as string | undefined;
const verifyCodeInputRef = ref();
const loading = ref(false);
const settingStore = useSettingStore();
const formRef = ref();
let defaultLoginType = settingStore.sysPublic.defaultLoginType || "password";
if (defaultLoginType === "sms") {
if (!settingStore.sysPublic.smsLoginEnabled || !settingStore.isComm) {
defaultLoginType = "password";
}
}
const formState = reactive({
username: "",
phoneCode: "86",
mobile: "",
password: "",
loginType: urlLoginType || defaultLoginType, //password
smsCode: "",
captcha: null,
smsCaptcha: null,
});
const settingStore = useSettingStore();
const formRef = ref();
let defaultLoginType = settingStore.sysPublic.defaultLoginType || "password";
if (defaultLoginType === "sms") {
if (!settingStore.sysPublic.smsLoginEnabled || !settingStore.isComm) {
defaultLoginType = "password";
}
}
const formState = reactive({
username: "",
phoneCode: "86",
mobile: "",
password: "",
loginType: urlLoginType || defaultLoginType,
smsCode: "",
captcha: null,
smsCaptcha: null,
});
const rules = {
mobile: [
{
required: true,
message: "请输入手机号",
},
],
username: [
{
required: true,
message: "请输入用户名",
},
],
password: [
{
required: true,
message: "请输入登录密码",
},
],
smsCode: [
{
required: true,
message: "请输入短信验证码",
},
],
captcha: [
{
required: true,
message: "请进行验证码验证",
},
],
};
const layout = {
labelCol: {
span: 0,
},
wrapperCol: {
span: 24,
},
};
async function afterLoginSuccess() {
if (queryBindCode.value) {
await oauthApi.BindUser(queryBindCode.value);
notification.success({ message: "绑定第三方账号成功" });
}
}
const twoFactor = reactive({
loginId: "",
verifyCode: "",
});
const handleTwoFactorSubmit = async () => {
await userStore.loginByTwoFactor(twoFactor);
afterLoginSuccess();
};
const handleFinish = async () => {
loading.value = true;
try {
// formState.captcha = await doCaptchaValidate();
// if (!formState.captcha) {
// return;
// }
const loginType = formState.loginType;
await userStore.login(loginType, toRaw(formState));
afterLoginSuccess();
} catch (e: any) {
//@ts-ignore
if (e.code === 10020) {
//
//@ts-ignore
twoFactor.loginId = e.data;
await nextTick();
verifyCodeInputRef.value.focus();
} else {
throw e;
}
} finally {
loading.value = false;
formState.captcha = null;
}
};
const handleFinishFailed = (errors: any) => {
utils.logger.log(errors);
};
const resetForm = () => {
formRef.value.resetFields();
};
const isLoginError = ref();
const sysPublicSettings = settingStore.getSysPublic;
function hasRegisterTypeEnabled() {
return sysPublicSettings.registerEnabled && (sysPublicSettings.usernameRegisterEnabled || sysPublicSettings.emailRegisterEnabled || sysPublicSettings.mobileRegisterEnabled || sysPublicSettings.smsLoginEnabled);
}
const captchaInputRef = ref();
const captchaInputForSmsCode = ref();
const isOauthOnly = computed(() => {
if (queryOauthOnly === "false" || queryOauthOnly === "0") {
return false;
}
return sysPublicSettings.oauthOnly && settingStore.isPlus && sysPublicSettings.oauthEnabled;
});
return {
t,
loading,
formState,
formRef,
rules,
layout,
isOauthOnly,
handleFinishFailed,
handleFinish,
resetForm,
isLoginError,
sysPublicSettings,
hasRegisterTypeEnabled,
twoFactor,
handleTwoFactorSubmit,
verifyCodeInputRef,
settingStore,
captchaInputRef,
captchaInputForSmsCode,
queryBindCode,
};
const rules = {
mobile: [
{
required: true,
message: "请输入手机号",
},
],
username: [
{
required: true,
message: "请输入用户名",
},
],
password: [
{
required: true,
message: "请输入登录密码",
},
],
smsCode: [
{
required: true,
message: "请输入短信验证码",
},
],
captcha: [
{
required: true,
message: "请进行验证码验证",
},
],
};
const layout = {
labelCol: {
span: 0,
},
wrapperCol: {
span: 24,
},
};
const twoFactor = reactive({
loginId: "",
verifyCode: "",
});
const passkeySupported = ref(false);
const passkeyEnabled = ref(false);
const checkPasskeySupport = () => {
passkeySupported.value = false;
if (typeof window !== "undefined" && "credentials" in navigator && "PublicKeyCredential" in window) {
passkeySupported.value = true;
}
};
const handlePasskeyLogin = async () => {
if (!passkeySupported.value) {
notification.error({ message: t("authentication.passkeyNotSupported") });
return;
}
loading.value = true;
try {
const optionsResponse: any = await request({
url: "/passkey/generateAuthentication",
method: "post",
});
const options = optionsResponse;
const credential = await (navigator.credentials as any).get({
publicKey: {
challenge: Uint8Array.from(atob(options.challenge.replace(/-/g, "+").replace(/_/g, "/")), c => c.charCodeAt(0)),
rpId: options.rpId,
allowCredentials: options.allowCredentials || [],
timeout: options.timeout || 60000,
},
});
if (!credential) {
throw new Error("Passkey认证失败");
}
const loginRes: any = await UserApi.loginByPasskey({
userId: optionsResponse.userId,
credential,
challenge: options.challenge,
});
await userStore.onLoginSuccess(loginRes);
} catch (e: any) {
console.error("Passkey登录失败:", e);
notification.error({ message: e.message || "Passkey登录失败" });
} finally {
loading.value = false;
}
};
const handleFinish = async () => {
loading.value = true;
try {
const loginType = formState.loginType;
await userStore.login(loginType, toRaw(formState));
if (queryBindCode.value) {
await oauthApi.BindUser(queryBindCode.value);
notification.success({ message: "绑定第三方账号成功" });
}
} catch (e: any) {
if (e.code === 10020) {
twoFactor.loginId = e.data;
await nextTick();
verifyCodeInputRef.value.focus();
} else {
throw e;
}
} finally {
loading.value = false;
formState.captcha = null;
}
};
const handleFinishFailed = (errors: any) => {
utils.logger.log(errors);
};
const handleTwoFactorSubmit = async () => {
await userStore.loginByTwoFactor(twoFactor);
if (queryBindCode.value) {
await oauthApi.BindUser(queryBindCode.value);
notification.success({ message: "绑定第三方账号成功" });
}
};
const sysPublicSettings = settingStore.getSysPublic;
const hasRegisterTypeEnabled = () => {
return sysPublicSettings.registerEnabled && (sysPublicSettings.usernameRegisterEnabled || sysPublicSettings.emailRegisterEnabled || sysPublicSettings.mobileRegisterEnabled || sysPublicSettings.smsLoginEnabled);
};
const isOauthOnly = computed(() => {
if (queryOauthOnly === "false" || queryOauthOnly === "0") {
return false;
}
return sysPublicSettings.oauthOnly && settingStore.isPlus && sysPublicSettings.oauthEnabled;
});
onMounted(() => {
checkPasskeySupport();
});
</script>
<style lang="less">
.login-page.main {
//margin: 20px !important;
margin-bottom: 100px;
.user-layout-login {
//label {
// font-size: 14px;
//}
.fs-icon {
// color: rgba(0, 0, 0, 0.45);
margin-right: 4px;
}
@@ -328,7 +344,6 @@ export default defineComponent({
text-align: left;
margin-top: 30px;
margin-bottom: 30px;
//line-height: 22px;
.item-icon {
font-size: 24px;
@@ -269,7 +269,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
title: t("certd.roles"),
type: "dict-select",
dict: dict({
url: "/sys/authority/role/list",
url: "/basic/user/getSimpleRoles",
value: "id",
label: "name",
}), // 数据字典
@@ -7,6 +7,7 @@ import * as api from "./api";
import { useSettingStore } from "/@/store/settings";
import { useUserStore } from "/@/store/user";
import { useI18n } from "/src/locales";
import { useProjectStore } from "/@/store/project";
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
const router = useRouter();
@@ -29,6 +30,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
};
const userStore = useUserStore();
const projectStore = useProjectStore();
const settingStore = useSettingStore();
const selectedRowKeys: Ref<any[]> = ref([]);
context.selectedRowKeys = selectedRowKeys;
@@ -61,6 +63,12 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
minWidth: 200,
fixed: "right",
},
form: {
onSuccess: async () => {
await projectStore.reload();
crudExpose?.doRefresh();
},
},
columns: {
id: {
title: "ID",
@@ -14,6 +14,11 @@
<div class="helper">{{ t("certd.httpsProxyHelper") }}</div>
</a-form-item>
<a-form-item :label="t('certd.sys.setting.environmentVars')" :name="['private', 'environmentVars']">
<a-textarea v-model:value="formState.private.environmentVars" :placeholder="environmentVarsExample" rows="4" />
<div class="helper">{{ t("certd.sys.setting.environmentVarsHelper") }}</div>
</a-form-item>
<a-form-item :label="t('certd.dualStackNetwork')" :name="['private', 'dnsResultOrder']">
<a-select v-model:value="formState.private.dnsResultOrder">
<a-select-option value="verbatim">{{ t("certd.default") }}</a-select-option>
@@ -55,6 +60,11 @@ defineOptions({
name: "SettingNetwork",
});
const environmentVarsExample = ref(
`ALIYUN_CLIENT_CONNECT_TIMEOUT=16000 #连接超时,单位毫秒
ALIYUN_CLIENT_READ_TIMEOUT=16000 #读取数据超时单位毫秒`
);
const formState = reactive<Partial<SysSettings>>({
public: {},
private: {},
+4 -4
View File
@@ -93,16 +93,16 @@ export default (req: any) => {
// with options
"/api": {
//配套后端 https://github.com/fast-crud/fs-server-js
target: "https://127.0.0.1:7002",
target: "http://127.0.0.1:7001",
//忽略证书
agent: new https.Agent({ rejectUnauthorized: false }),
// agent: new https.Agent({ rejectUnauthorized: false }),
},
"/certd/api": {
//配套后端 https://github.com/fast-crud/fs-server-js
target: "https://127.0.0.1:7002/api",
target: "http://127.0.0.1:7001/api",
rewrite: path => path.replace(/^\/certd\/api/, ""),
//忽略证书
agent: new https.Agent({ rejectUnauthorized: false }),
// agent: new https.Agent({ rejectUnauthorized: false }),
},
},
},
+6
View File
@@ -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.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
### Performance Improvements
* 支持迁移个人数据到企业项目中 ([c6ca832](https://github.com/certd/certd/commit/c6ca83273779ed84de1b23b5e477063af043d015))
# [1.39.0](https://github.com/certd/certd/compare/v1.38.12...v1.39.0) (2026-03-07)
### Bug Fixes
@@ -0,0 +1,18 @@
CREATE TABLE "sys_passkey"
(
"id" integer PRIMARY KEY AUTOINCREMENT NOT NULL,
"user_id" integer NOT NULL,
"device_name" varchar(512) NOT NULL,
"passkey_id" varchar(512) NOT NULL,
"public_key" varchar(1024) NOT NULL,
"counter" integer NOT NULL,
"transports" varchar(512) NULL,
"registered_at" integer NOT NULL,
"create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP),
"update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP)
);
CREATE INDEX "index_passkey_user_id" ON "sys_passkey" ("user_id");
CREATE INDEX "index_passkey_passkey_id" ON "sys_passkey" ("passkey_id");
+16 -14
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/ui-server",
"version": "1.39.0",
"version": "1.39.1",
"description": "fast-server base midway",
"private": true,
"type": "module",
@@ -50,20 +50,20 @@
"@aws-sdk/client-route-53": "^3.964.0",
"@aws-sdk/client-s3": "^3.964.0",
"@aws-sdk/client-sts": "^3.990.0",
"@certd/acme-client": "^1.39.0",
"@certd/basic": "^1.39.0",
"@certd/commercial-core": "^1.39.0",
"@certd/acme-client": "^1.39.1",
"@certd/basic": "^1.39.1",
"@certd/commercial-core": "^1.39.1",
"@certd/cv4pve-api-javascript": "^8.4.2",
"@certd/jdcloud": "^1.39.0",
"@certd/lib-huawei": "^1.39.0",
"@certd/lib-k8s": "^1.39.0",
"@certd/lib-server": "^1.39.0",
"@certd/midway-flyway-js": "^1.39.0",
"@certd/pipeline": "^1.39.0",
"@certd/plugin-cert": "^1.39.0",
"@certd/plugin-lib": "^1.39.0",
"@certd/plugin-plus": "^1.39.0",
"@certd/plus-core": "^1.39.0",
"@certd/jdcloud": "^1.39.1",
"@certd/lib-huawei": "^1.39.1",
"@certd/lib-k8s": "^1.39.1",
"@certd/lib-server": "^1.39.1",
"@certd/midway-flyway-js": "^1.39.1",
"@certd/pipeline": "^1.39.1",
"@certd/plugin-cert": "^1.39.1",
"@certd/plugin-lib": "^1.39.1",
"@certd/plugin-plus": "^1.39.1",
"@certd/plus-core": "^1.39.1",
"@google-cloud/publicca": "^1.3.0",
"@huaweicloud/huaweicloud-sdk-cdn": "^3.1.185",
"@huaweicloud/huaweicloud-sdk-core": "^3.1.185",
@@ -82,6 +82,8 @@
"@midwayjs/upload": "3.20.13",
"@midwayjs/validate": "3.20.13",
"@peculiar/x509": "^1.11.0",
"@simplewebauthn/browser": "^13.2.2",
"@simplewebauthn/server": "^13.2.3",
"@ucloud-sdks/ucloud-sdk-js": "^0.2.4",
"@volcengine/openapi": "^1.28.1",
"ali-oss": "^6.21.0",
@@ -81,6 +81,23 @@ export class LoginController extends BaseController {
return this.ok(token);
}
@Post('/loginByPasskey', { summary: Constants.per.guest })
public async loginByPasskey(
@Body(ALL)
body: any
) {
const credential = body.credential;
const challenge = body.challenge;
const token = await this.loginService.loginByPasskey({
credential,
challenge,
}, this.ctx);
// this.writeTokenCookie(token);
return this.ok(token);
}
@Post('/logout', { summary: Constants.per.authOnly })
public logout() {
this.ctx.cookies.set("certd_token", "", {
@@ -0,0 +1,99 @@
import { ALL, Body, Controller, Inject, Post, Provide, RequestIP } from "@midwayjs/core";
import { PasskeyService } from "../../../modules/login/service/passkey-service.js";
import { BaseController, Constants } from "@certd/lib-server";
import { UserService } from "../../../modules/sys/authority/service/user-service.js";
@Provide()
@Controller('/api/passkey')
export class PasskeyController extends BaseController {
@Inject()
passkeyService: PasskeyService;
@Inject()
userService: UserService;
@Post('/generateRegistration', { summary: Constants.per.authOnly })
public async generateRegistration(
@Body(ALL)
body: any,
@RequestIP()
remoteIp: string
) {
const userId = this.getUserId()
const user = await this.userService.info(userId);
if (!user) {
throw new Error('用户不存在');
}
const options = await this.passkeyService.generateRegistrationOptions(
userId,
user.username,
remoteIp,
this.ctx
);
return this.ok({
...options,
userId
});
}
@Post('/verifyRegistration', { summary: Constants.per.guest })
public async verifyRegistration(
@Body(ALL)
body: any
) {
const userId = body.userId;
const response = body.response;
const challenge = body.challenge;
const deviceName = body.deviceName;
const result = await this.passkeyService.registerPasskey(
userId,
response,
challenge,
deviceName,
this.ctx
);
return this.ok(result);
}
@Post('/generateAuthentication', { summary: Constants.per.guest })
public async generateAuthentication(
@Body(ALL)
body: any
) {
const options = await this.passkeyService.generateAuthenticationOptions(
this.ctx
);
return this.ok({
...options,
});
}
@Post('/register', { summary: Constants.per.guest })
public async registerPasskey(
@Body(ALL)
body: any
) {
const userId = body.userId;
const response = body.response;
const deviceName = body.deviceName;
const challenge = body.challenge;
const result = await this.passkeyService.registerPasskey(
userId,
response,
challenge,
deviceName,
this.ctx
);
return this.ok(result);
}
}
@@ -4,6 +4,7 @@ import { In } from 'typeorm';
import { AuthService } from '../../../modules/sys/authority/service/auth-service.js';
import { UserService } from '../../../modules/sys/authority/service/user-service.js';
import { BasicController } from '../../basic/code-controller.js';
import { RoleService } from '../../../modules/sys/authority/service/role-service.js';
/**
*
@@ -15,6 +16,8 @@ export class BasicUserController extends BasicController {
service: UserService;
@Inject()
authService: AuthService;
@Inject()
roleService: RoleService;
getService(): UserService {
return this.service;
@@ -57,4 +60,15 @@ export class BasicUserController extends BasicController {
return this.ok(users);
}
@Post('/getSimpleRoles', {summary: Constants.per.authOnly})
async getSimpleRoles() {
const roles = await this.roleService.find({
select: {
id: true,
name: true,
},
});
return this.ok(roles);
}
}
@@ -0,0 +1,42 @@
import { BaseController, Constants } from '@certd/lib-server';
import { Controller, Inject, Post, Provide } from '@midwayjs/core';
import { TransferService } from '../../../modules/sys/enterprise/service/transfer-service.js';
/**
*/
@Provide()
@Controller('/api/enterprise/transfer')
export class TransferController extends BaseController {
@Inject()
service: TransferService;
getService(): TransferService {
return this.service;
}
/**
*
* @param body
* @returns
*/
@Post('/selfResources', { summary: Constants.per.authOnly })
async selfResources() {
const userId = this.getUserId();
const res = await this.service.getUserResources(userId);
return this.ok(res);
}
/**
*
* @param body
* @returns
*/
@Post('/doTransfer', { summary: Constants.per.authOnly })
async doTransfer() {
const {projectId} = await this.getProjectUserIdRead();
const userId = this.getUserId();
await this.service.transferAll(userId,projectId);
return this.ok();
}
}
@@ -1,7 +1,8 @@
import { ALL, Body, Controller, Inject, Post, Provide } from '@midwayjs/core';
import { BaseController, Constants } from '@certd/lib-server';
import { UserService } from '../../../modules/sys/authority/service/user-service.js';
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';
/**
*/
@@ -10,8 +11,14 @@ import { RoleService } from '../../../modules/sys/authority/service/role-service
export class MineController extends BaseController {
@Inject()
userService: UserService;
@Inject()
roleService: RoleService;
@Inject()
passkeyService: PasskeyService;
@Post('/info', { summary: Constants.per.authOnly })
public async info() {
const userId = this.getUserId();
@@ -43,4 +50,30 @@ export class MineController extends BaseController {
});
return this.ok({});
}
@Post('/passkeys', { summary: Constants.per.authOnly })
public async getPasskeys() {
const userId = this.getUserId();
const passkeys = await this.passkeyService.find({
select: ['id', 'deviceName', 'registeredAt'],
where: { userId }});
return this.ok(passkeys);
}
@Post('/unbindPasskey', { summary: Constants.per.authOnly })
public async unbindPasskey(@Body(ALL) body: any) {
const userId = this.getUserId();
const passkeyId = body.id;
const passkey = await this.passkeyService.findOne({
where: { id: passkeyId, userId },
});
if (!passkey) {
throw new Error('Passkey不存在');
}
await this.passkeyService.delete([passkey.id]);
return this.ok({});
}
}
@@ -0,0 +1,35 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity('sys_passkey')
export class PasskeyEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: 'user_id', comment: '用户id' })
userId: number;
@Column({ name: 'device_name', comment: '设备名称' })
deviceName: string;
@Column({ name: 'passkey_id', comment: 'passkey_id' })
passkeyId: string;
@Column({ name: 'public_key', comment: '公钥', type: 'text' })
publicKey: string;
@Column({ name: 'counter', comment: '计数器' })
counter: number;
@Column({ name: 'transports', comment: '传输方式', type: 'text', nullable: true })
transports: string;
@Column({ name: 'registered_at', comment: '注册时间' })
registeredAt: number;
@Column({ name: 'create_time', comment: '创建时间', default: () => 'CURRENT_TIMESTAMP' })
createTime: Date;
@Column({ name: 'update_time', comment: '修改时间', default: () => 'CURRENT_TIMESTAMP' })
updateTime: Date;
}
@@ -18,6 +18,7 @@ import { UserSettingsService } from "../../mine/service/user-settings-service.js
import { isPlus } from "@certd/plus-core";
import { AddonService } from "@certd/lib-server";
import { OauthBoundService } from "./oauth-bound-service.js";
import { PasskeyService } from "./passkey-service.js";
/**
*/
@@ -45,6 +46,9 @@ export class LoginService {
@Inject()
oauthBoundService: OauthBoundService;
@Inject()
passkeyService: PasskeyService;
checkIsBlocked(username: string) {
const blockDurationKey = `login_block_duration:${username}`;
const value = cache.get(blockDurationKey);
@@ -254,4 +258,10 @@ export class LoginService {
}
return this.generateToken(info);
}
}
async loginByPasskey(req: { credential: any; challenge: string }, ctx: any) {
const {credential, challenge} = req;
const user = await this.passkeyService.loginByPasskey(credential, challenge, ctx);
return this.generateToken(user);
}}
@@ -0,0 +1,208 @@
import { cache } from "@certd/basic";
import { AuthException, BaseService } from "@certd/lib-server";
import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { UserService } from "../../sys/authority/service/user-service.js";
import { PasskeyEntity } from "../entity/passkey.js";
import { Repository } from "typeorm";
import { InjectEntityModel } from "@midwayjs/typeorm";
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class PasskeyService extends BaseService<PasskeyEntity> {
@Inject()
userService: UserService;
@InjectEntityModel(PasskeyEntity)
repository: Repository<PasskeyEntity>;
getRepository(): Repository<PasskeyEntity> {
return this.repository;
}
async generateRegistrationOptions(userId: number, username: string, remoteIp: string, ctx: any) {
const { generateRegistrationOptions } = await import("@simplewebauthn/server");
const user = await this.userService.info(userId);
const options = await generateRegistrationOptions({
rpName: "Certd",
rpID: this.getRpId(ctx),
userID: new Uint8Array([userId]),
userName: username,
userDisplayName: user.nickName || username,
timeout: 60000,
attestationType: "none",
excludeCredentials: [],
});
cache.set(`passkey:registration:${options.challenge}`, userId, {
ttl: 5 * 60 * 1000,
});
return {
...options,
};
}
async verifyRegistrationResponse(
userId: number,
response: any,
challenge: string,
ctx: any
) {
const { verifyRegistrationResponse } = await import("@simplewebauthn/server");
const storedUserId = cache.get(`passkey:registration:${challenge}`);
if (!storedUserId || storedUserId !== userId) {
throw new AuthException("注册验证失败");
}
const verification = await verifyRegistrationResponse({
response,
expectedChallenge: challenge,
expectedOrigin: this.getOrigin(ctx),
expectedRPID: this.getRpId(ctx),
});
if (!verification.verified) {
throw new AuthException("注册验证失败");
}
cache.delete(`passkey:registration:${challenge}`);
return {
credentialId: verification.registrationInfo.credential.id,
credentialPublicKey: verification.registrationInfo.credential.publicKey,
counter: verification.registrationInfo.credential.counter,
};
}
async generateAuthenticationOptions(ctx: any) {
const { generateAuthenticationOptions } = await import("@simplewebauthn/server");
const options = await generateAuthenticationOptions({
rpID: this.getRpId(ctx),
timeout: 60000,
allowCredentials: [],
});
// cache.set(`passkey:authentication:${options.challenge}`, userId, {
// ttl: 5 * 60 * 1000,
// });
return {
...options,
};
}
async verifyAuthenticationResponse(
credential: any,
challenge: string,
ctx: any
) {
const { verifyAuthenticationResponse } = await import("@simplewebauthn/server");
const passkey = await this.repository.findOne({
where: {
passkeyId: credential.id,
},
});
if (!passkey) {
throw new AuthException("Passkey不存在");
}
const verification = await verifyAuthenticationResponse({
response:credential,
expectedChallenge: challenge,
expectedOrigin: this.getOrigin(ctx),
expectedRPID: this.getRpId(ctx),
credential: {
id: passkey.passkeyId,
publicKey: new Uint8Array(Buffer.from(passkey.publicKey, 'base64')),
counter: passkey.counter,
transports: passkey.transports as any,
},
});
if (!verification.verified) {
throw new AuthException("认证验证失败");
}
cache.delete(`passkey:authentication:${challenge}`);
return {
credentialId: verification.authenticationInfo.credentialID,
counter: verification.authenticationInfo.newCounter,
userId: passkey.userId,
};
}
async registerPasskey(
userId: number,
response: any,
challenge: string,
deviceName: string,
ctx: any
) {
const verification = await this.verifyRegistrationResponse(
userId,
response,
challenge,
ctx
);
await this.add({
userId,
passkeyId: verification.credentialId,
publicKey: Buffer.from(verification.credentialPublicKey).toString('base64'),
counter: verification.counter,
deviceName,
registeredAt: Date.now(),
});
return { success: true };
}
async loginByPasskey( credential: any, challenge: string, ctx: any) {
const verification = await this.verifyAuthenticationResponse(
credential,
challenge,
ctx
);
const passkey = await this.repository.findOne({
where: {
passkeyId: verification.credentialId,
},
});
if (!passkey) {
throw new AuthException("Passkey不存在");
}
if (verification.counter <= passkey.counter) {
throw new AuthException("认证失败:计数器异常");
}
passkey.counter = verification.counter;
await this.repository.save(passkey);
const user = await this.userService.info(passkey.userId);
return user;
}
private getRpId(ctx: any): string {
if (ctx && ctx.request && ctx.request.host) {
return ctx.request.host.split(':')[0];
}
return 'localhost';
}
private getOrigin(ctx: any): string {
if (ctx && ctx.request) {
const protocol = ctx.request.protocol;
const host = ctx.request.host;
return `${protocol}://${host}`;
}
return 'https://localhost';
}
}
@@ -16,6 +16,7 @@ import {SiteIpService} from "./site-ip-service.js";
import {SiteIpEntity} from "../entity/site-ip.js";
import {Cron} from "../../cron/cron.js";
import { dnsContainer } from "./dns-custom.js";
import { merge } from "lodash-es";
@Provide()
@Scope(ScopeEnum.Request, {allowDowngrade: true})
@@ -164,6 +165,8 @@ export class SiteInfoService extends BaseService<SiteInfoEntity> {
await this.update(updateData);
const setting = await this.userSettingsService.getSetting<UserSiteMonitorSetting>(site.userId,site.projectId, UserSiteMonitorSetting)
merge(site,updateData)
//检查ip
await this.checkAllIp(site,retryTimes,setting);
@@ -25,6 +25,7 @@ export const AdminRoleId = 1
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class UserService extends BaseService<UserEntity> {
@InjectEntityModel(UserEntity)
repository: Repository<UserEntity>;
@@ -278,6 +279,10 @@ export class UserService extends BaseService<UserEntity> {
return user.username;
}
async getByUsername(username: any) {
return await this.findOne({ username });
}
async changePassword(userId: any, form: any) {
const user = await this.info(userId);
const passwordChecked = await this.checkPassword(form.password, user.password, user.passwordVersion);
@@ -0,0 +1,93 @@
import { isEnterprise } from "@certd/lib-server";
import { ApplicationContext, IMidwayContainer, Provide, Scope, ScopeEnum } from "@midwayjs/core";
@Provide()
@Scope(ScopeEnum.Request, { allowDowngrade: true })
export class TransferService {
@ApplicationContext()
appCtx: IMidwayContainer;
async getServices() {
const getService = async (key: string) => {
return await this.appCtx.getAsync(key);
}
const serviceNames = [
"pipeline",
"certInfo",
"siteInfo",
"domain",
"cnameRecord",
"group",
"pipelineGroup",
"notification",
"subDomain",
"template",
"openKey",
"siteIp",
"access",
"history",
"historyLog",
"storage",
]
const services: any = {}
for (const key of serviceNames) {
services[key] = await getService(`${key}Service`);
}
return services;
}
/**
*
* @param userId
* @returns
*/
async getUserResources(userId: number) {
const query = {
userId,
}
const services = await this.getServices();
const counts: any = {}
let totalCount = 0;
for (const key of Object.keys(services)) {
const count = await services[key].repository.count({ where: query });
counts[key] = count;
totalCount += count;
}
return {
...counts,
totalCount,
}
}
async transferAll(userId: number, projectId: number) {
if (!isEnterprise()) {
throw new Error('当前非企业模式,不支持资源迁移到项目');
}
if (projectId === 0) {
throw new Error('项目ID不能为0');
}
if (userId == null) {
throw new Error('用户ID不能为空');
}
const query = {
userId,
}
const services = await this.getServices();
for (const key of Object.keys(services)) {
await services[key].repository.update(query, {
userId: -1,
projectId,
});
}
}
}
@@ -1,4 +1,4 @@
import { IsTaskPlugin, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { IsTaskPlugin, Pager, PageSearch, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { AliyunAccess } from "../../../../plugin-lib/aliyun/access/index.js";
import { CertApplyBasePlugin } from "../base.js";
import { CertReader, createRemoteSelectInputDefine } from "@certd/plugin-lib";
@@ -34,8 +34,9 @@ export class CertApplyGetFormAliyunPlugin extends CertApplyBasePlugin {
helper: "订阅模式的证书订单Id",
typeName: "CertApplyGetFormAliyun",
component: {
name: "RemoteAutoComplete",
name: "RemoteSelect",
vModel: "value",
pager: true,
},
action: CertApplyGetFormAliyunPlugin.prototype.onGetOrderList.name,
})
@@ -126,6 +127,7 @@ export class CertApplyGetFormAliyunPlugin extends CertApplyBasePlugin {
const client = await access.getClient("cas.aliyuncs.com");
const pager = new Pager(req)
const res = await client.doRequest({
// 接口名称
action: "ListUserCertificateOrder",
@@ -139,12 +141,14 @@ export class CertApplyGetFormAliyunPlugin extends CertApplyBasePlugin {
data: {
query: {
Status: "ISSUED",
CurrentPage: pager.pageNo,
ShowSize : pager.pageSize,
},
},
});
const list = res?.CertificateOrderList || [];
if (!list || list.length === 0) {
throw new Error("没有找到已签发的证书订单");
return []
}
return list.map((item: any) => {
+231 -61
View File
@@ -49,7 +49,7 @@ importers:
packages/core/acme-client:
dependencies:
'@certd/basic':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../basic
'@peculiar/x509':
specifier: ^1.11.0
@@ -213,11 +213,11 @@ importers:
packages/core/pipeline:
dependencies:
'@certd/basic':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../basic
'@certd/plus-core':
specifier: ^1.38.12
version: 1.38.12
specifier: ^1.39.0
version: 1.39.0
dayjs:
specifier: ^1.11.7
version: 1.11.13
@@ -412,7 +412,7 @@ importers:
packages/libs/lib-k8s:
dependencies:
'@certd/basic':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/basic
'@kubernetes/client-node':
specifier: 0.21.0
@@ -452,20 +452,20 @@ importers:
packages/libs/lib-server:
dependencies:
'@certd/acme-client':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/acme-client
'@certd/basic':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/basic
'@certd/pipeline':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/pipeline
'@certd/plugin-lib':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../plugins/plugin-lib
'@certd/plus-core':
specifier: ^1.38.12
version: 1.38.12
specifier: ^1.39.0
version: 1.39.0
'@midwayjs/cache':
specifier: 3.14.0
version: 3.14.0
@@ -610,16 +610,16 @@ importers:
packages/plugins/plugin-cert:
dependencies:
'@certd/acme-client':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/acme-client
'@certd/basic':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/basic
'@certd/pipeline':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/pipeline
'@certd/plugin-lib':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../plugin-lib
psl:
specifier: ^1.9.0
@@ -683,17 +683,17 @@ importers:
specifier: ^3.964.0
version: 3.964.0(aws-crt@1.26.2)
'@certd/acme-client':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/acme-client
'@certd/basic':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/basic
'@certd/pipeline':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/pipeline
'@certd/plus-core':
specifier: ^1.38.12
version: 1.38.12
specifier: ^1.39.0
version: 1.39.0
'@kubernetes/client-node':
specifier: 0.21.0
version: 0.21.0
@@ -783,16 +783,16 @@ importers:
packages/pro/commercial-core:
dependencies:
'@certd/basic':
specifier: ^1.38.8
specifier: ^1.38.12
version: link:../../core/basic
'@certd/lib-server':
specifier: ^1.38.8
specifier: ^1.38.12
version: link:../../libs/lib-server
'@certd/pipeline':
specifier: ^1.38.8
specifier: ^1.38.12
version: link:../../core/pipeline
'@certd/plus-core':
specifier: ^1.38.8
specifier: ^1.38.12
version: link:../plus-core
'@midwayjs/core':
specifier: 3.20.11
@@ -865,16 +865,16 @@ importers:
packages/pro/plugin-plus:
dependencies:
'@certd/basic':
specifier: ^1.38.8
specifier: ^1.38.12
version: link:../../core/basic
'@certd/pipeline':
specifier: ^1.38.8
specifier: ^1.38.12
version: link:../../core/pipeline
'@certd/plugin-lib':
specifier: ^1.38.8
specifier: ^1.38.12
version: link:../../plugins/plugin-lib
'@certd/plus-core':
specifier: ^1.38.8
specifier: ^1.38.12
version: link:../plus-core
crypto-js:
specifier: ^4.2.0
@@ -950,7 +950,7 @@ importers:
packages/pro/plus-core:
dependencies:
'@certd/basic':
specifier: ^1.38.8
specifier: ^1.38.12
version: link:../../core/basic
dayjs:
specifier: ^1.11.7
@@ -1246,10 +1246,10 @@ importers:
version: 0.1.3(zod@3.24.4)
devDependencies:
'@certd/lib-iframe':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../libs/lib-iframe
'@certd/pipeline':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/pipeline
'@rollup/plugin-commonjs':
specifier: ^25.0.7
@@ -1444,47 +1444,47 @@ importers:
specifier: ^3.990.0
version: 3.990.0(aws-crt@1.26.2)
'@certd/acme-client':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/acme-client
'@certd/basic':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/basic
'@certd/commercial-core':
specifier: ^1.38.12
version: 1.38.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))
specifier: ^1.39.0
version: 1.39.0(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))
'@certd/cv4pve-api-javascript':
specifier: ^8.4.2
version: 8.4.2
'@certd/jdcloud':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../libs/lib-jdcloud
'@certd/lib-huawei':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../libs/lib-huawei
'@certd/lib-k8s':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../libs/lib-k8s
'@certd/lib-server':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../libs/lib-server
'@certd/midway-flyway-js':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../libs/midway-flyway-js
'@certd/pipeline':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../core/pipeline
'@certd/plugin-cert':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../plugins/plugin-cert
'@certd/plugin-lib':
specifier: ^1.38.12
specifier: ^1.39.0
version: link:../../plugins/plugin-lib
'@certd/plugin-plus':
specifier: ^1.38.12
version: 1.38.12
specifier: ^1.39.0
version: 1.39.0
'@certd/plus-core':
specifier: ^1.38.12
version: 1.38.12
specifier: ^1.39.0
version: 1.39.0
'@google-cloud/publicca':
specifier: ^1.3.0
version: 1.3.0(encoding@0.1.13)
@@ -1539,6 +1539,12 @@ importers:
'@peculiar/x509':
specifier: ^1.11.0
version: 1.12.3
'@simplewebauthn/browser':
specifier: ^13.2.2
version: 13.2.2
'@simplewebauthn/server':
specifier: ^13.2.3
version: 13.2.3
'@ucloud-sdks/ucloud-sdk-js':
specifier: ^0.2.4
version: 0.2.4
@@ -2820,17 +2826,17 @@ packages:
'@better-scroll/zoom@2.5.1':
resolution: {integrity: sha512-aGvFY5ooeZWS4RcxQLD+pGLpQHQxpPy0sMZV3yadcd2QK53PK9gS4Dp+BYfRv8lZ4/P2LoNEhr6Wq1DN6+uPlA==}
'@certd/commercial-core@1.38.12':
resolution: {integrity: sha512-kP4vM3F+D6TME9NYSM7Q1YqTx6Ig1dseWQUFVf7GnfG9E3A2odaRwmeYwy5QomChF0vbPHPkJqOtfYv9WItikQ==}
'@certd/commercial-core@1.39.0':
resolution: {integrity: sha512-amnJsyLdNMTUU+v67exGMHe6igU5m0vaCJaWjq6c+CQN7PKkM9Bt7H7f7FlbHRon8q6AngtpIgQNhIx7473Dmg==}
'@certd/cv4pve-api-javascript@8.4.2':
resolution: {integrity: sha512-udGce7ewrVl4DmZvX+17PjsnqsdDIHEDatr8QP0AVrY2p+8JkaSPW4mXCKiLGf82C9K2+GXgT+qNIqgW7tfF9Q==}
'@certd/plugin-plus@1.38.12':
resolution: {integrity: sha512-I0aQAzIRaDFSwM2vb/ycLqaNjVcb/fWUOgmX/BpHEnN446oNk5+pl8d4KFE+OMzEDQcwOwZhdXhjrOsskqY3PA==}
'@certd/plugin-plus@1.39.0':
resolution: {integrity: sha512-/cS9XYwjgP6JNgysHKz5tN93D8+CIOviBtLdnkkaZ6nXluLueRSBqNNvCqQM8dE1GndS7sEYj3RnkX7ver/Q3A==}
'@certd/plus-core@1.38.12':
resolution: {integrity: sha512-2BKhInDmMrH4l/WRKcSq7E6PA4ANcE1PVMIVoWlhSnnW+sghYUioymRaSoGTxJYfSvGHlLrqZXAassQHAzm98g==}
'@certd/plus-core@1.39.0':
resolution: {integrity: sha512-wLIMH38oBCtPBu3xxG2JGBiFchT0ko9Q/iPIoF1YJnKooGYS69qjtqEwGoGo5nHFqrRJB4RGmd880wj7wCb5cg==}
'@certd/vue-js-cron-core@6.0.3':
resolution: {integrity: sha512-kqzoAMhYz9j6FGNWEODRYtt4NpUEUwjpkU89z5WVg2tCtOcI5VhwyUGOd8AxiBCRfd6PtXvzuqw85PaOps9wrQ==}
@@ -3528,6 +3534,9 @@ packages:
'@hapi/topo@5.1.0':
resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==}
'@hexagon/base64@1.1.28':
resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==}
'@httptoolkit/websocket-stream@6.0.1':
resolution: {integrity: sha512-A0NOZI+Glp3Xgcz6Na7i7o09+/+xm2m0UCU8gdtM2nIv6/cjLmhMZMqehSpTlgbx9omtLmV8LVqOskPEyWnmZQ==}
@@ -3776,6 +3785,9 @@ packages:
resolution: {integrity: sha512-VxP+PLlw62B14hP5g19RA6svqZHNN4J1P2eIzDpwQb2RNeBMuZMUveJMgiZVKF5nNjCh2mm4mKk11wcTr+v+vw==}
engines: {node: ^18.0.0 || >=20.0.0}
'@levischuck/tiny-cbor@0.2.11':
resolution: {integrity: sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==}
'@manypkg/find-root@2.2.3':
resolution: {integrity: sha512-jtEZKczWTueJYHjGpxU3KJQ08Gsrf4r6Q2GjmPp/RGk5leeYAA1eyDADSAF+KVCsQ6EwZd/FMcOFCoMhtqdCtQ==}
engines: {node: '>=14.18.0'}
@@ -4016,39 +4028,76 @@ packages:
'@paralleldrive/cuid2@2.2.2':
resolution: {integrity: sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==}
'@peculiar/asn1-android@2.6.0':
resolution: {integrity: sha512-cBRCKtYPF7vJGN76/yG8VbxRcHLPF3HnkoHhKOZeHpoVtbMYfY9ROKtH3DtYUY9m8uI1Mh47PRhHf2hSK3xcSQ==}
'@peculiar/asn1-cms@2.3.15':
resolution: {integrity: sha512-B+DoudF+TCrxoJSTjjcY8Mmu+lbv8e7pXGWrhNp2/EGJp9EEcpzjBCar7puU57sGifyzaRVM03oD5L7t7PghQg==}
'@peculiar/asn1-cms@2.6.1':
resolution: {integrity: sha512-vdG4fBF6Lkirkcl53q6eOdn3XYKt+kJTG59edgRZORlg/3atWWEReRCx5rYE1ZzTTX6vLK5zDMjHh7vbrcXGtw==}
'@peculiar/asn1-csr@2.3.15':
resolution: {integrity: sha512-caxAOrvw2hUZpxzhz8Kp8iBYKsHbGXZPl2KYRMIPvAfFateRebS3136+orUpcVwHRmpXWX2kzpb6COlIrqCumA==}
'@peculiar/asn1-csr@2.6.1':
resolution: {integrity: sha512-WRWnKfIocHyzFYQTka8O/tXCiBquAPSrRjXbOkHbO4qdmS6loffCEGs+rby6WxxGdJCuunnhS2duHURhjyio6w==}
'@peculiar/asn1-ecc@2.3.15':
resolution: {integrity: sha512-/HtR91dvgog7z/WhCVdxZJ/jitJuIu8iTqiyWVgRE9Ac5imt2sT/E4obqIVGKQw7PIy+X6i8lVBoT6wC73XUgA==}
'@peculiar/asn1-ecc@2.6.1':
resolution: {integrity: sha512-+Vqw8WFxrtDIN5ehUdvlN2m73exS2JVG0UAyfVB31gIfor3zWEAQPD+K9ydCxaj3MLen9k0JhKpu9LqviuCE1g==}
'@peculiar/asn1-pfx@2.3.15':
resolution: {integrity: sha512-E3kzQe3J2xV9DP6SJS4X6/N1e4cYa2xOAK46VtvpaRk8jlheNri8v0rBezKFVPB1rz/jW8npO+u1xOvpATFMWg==}
'@peculiar/asn1-pfx@2.6.1':
resolution: {integrity: sha512-nB5jVQy3MAAWvq0KY0R2JUZG8bO/bTLpnwyOzXyEh/e54ynGTatAR+csOnXkkVD9AFZ2uL8Z7EV918+qB1qDvw==}
'@peculiar/asn1-pkcs8@2.3.15':
resolution: {integrity: sha512-/PuQj2BIAw1/v76DV1LUOA6YOqh/UvptKLJHtec/DQwruXOCFlUo7k6llegn8N5BTeZTWMwz5EXruBw0Q10TMg==}
'@peculiar/asn1-pkcs8@2.6.1':
resolution: {integrity: sha512-JB5iQ9Izn5yGMw3ZG4Nw3Xn/hb/G38GYF3lf7WmJb8JZUydhVGEjK/ZlFSWhnlB7K/4oqEs8HnfFIKklhR58Tw==}
'@peculiar/asn1-pkcs9@2.3.15':
resolution: {integrity: sha512-yiZo/1EGvU1KiQUrbcnaPGWc0C7ElMMskWn7+kHsCFm+/9fU0+V1D/3a5oG0Jpy96iaXggQpA9tzdhnYDgjyFg==}
'@peculiar/asn1-pkcs9@2.6.1':
resolution: {integrity: sha512-5EV8nZoMSxeWmcxWmmcolg22ojZRgJg+Y9MX2fnE2bGRo5KQLqV5IL9kdSQDZxlHz95tHvIq9F//bvL1OeNILw==}
'@peculiar/asn1-rsa@2.3.15':
resolution: {integrity: sha512-p6hsanvPhexRtYSOHihLvUUgrJ8y0FtOM97N5UEpC+VifFYyZa0iZ5cXjTkZoDwxJ/TTJ1IJo3HVTB2JJTpXvg==}
'@peculiar/asn1-rsa@2.6.1':
resolution: {integrity: sha512-1nVMEh46SElUt5CB3RUTV4EG/z7iYc7EoaDY5ECwganibQPkZ/Y2eMsTKB/LeyrUJ+W/tKoD9WUqIy8vB+CEdA==}
'@peculiar/asn1-schema@2.3.15':
resolution: {integrity: sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w==}
'@peculiar/asn1-schema@2.6.0':
resolution: {integrity: sha512-xNLYLBFTBKkCzEZIw842BxytQQATQv+lDTCEMZ8C196iJcJJMBUZxrhSTxLaohMyKK8QlzRNTRkUmanucnDSqg==}
'@peculiar/asn1-x509-attr@2.3.15':
resolution: {integrity: sha512-TWJVJhqc+IS4MTEML3l6W1b0sMowVqdsnI4dnojg96LvTuP8dga9f76fjP07MUuss60uSyT2ckoti/2qHXA10A==}
'@peculiar/asn1-x509-attr@2.6.1':
resolution: {integrity: sha512-tlW6cxoHwgcQghnJwv3YS+9OO1737zgPogZ+CgWRUK4roEwIPzRH4JEiG770xe5HX2ATfCpmX60gurfWIF9dcQ==}
'@peculiar/asn1-x509@2.3.15':
resolution: {integrity: sha512-0dK5xqTqSLaxv1FHXIcd4Q/BZNuopg+u1l23hT9rOmQ1g4dNtw0g/RnEi+TboB0gOwGtrWn269v27cMgchFIIg==}
'@peculiar/asn1-x509@2.6.1':
resolution: {integrity: sha512-O9jT5F1A2+t3r7C4VT7LYGXqkGLK7Kj1xFpz7U0isPrubwU5PbDoyYtx6MiGst29yq7pXN5vZbQFKRCP+lLZlA==}
'@peculiar/x509@1.12.3':
resolution: {integrity: sha512-+Mzq+W7cNEKfkNZzyLl6A6ffqc3r21HGZUezgfKxpZrkORfOqgRXnS80Zu0IV6a9Ue9QBJeKD7kN0iWfc3bhRQ==}
'@peculiar/x509@1.14.3':
resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==}
engines: {node: '>=20.0.0'}
'@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@@ -4329,6 +4378,13 @@ packages:
'@simonwep/pickr@1.8.2':
resolution: {integrity: sha512-/l5w8BIkrpP6n1xsetx9MWPWlU6OblN5YgZZphxan0Tq4BByTCETL6lyIeY8lagalS2Nbt4F2W034KHLIiunKA==}
'@simplewebauthn/browser@13.2.2':
resolution: {integrity: sha512-FNW1oLQpTJyqG5kkDg5ZsotvWgmBaC6jCHR7Ej0qUNep36Wl9tj2eZu7J5rP+uhXgHaLk+QQ3lqcw2vS5MX1IA==}
'@simplewebauthn/server@13.2.3':
resolution: {integrity: sha512-ZhcVBOw63birYx9jVfbhK6rTehckVes8PeWV324zpmdxr0BUfylospwMzcrxrdMcOi48MHWj2LCA+S528LnGvg==}
engines: {node: '>=20.0.0'}
'@sinclair/typebox@0.27.8':
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
@@ -5874,6 +5930,7 @@ packages:
basic-ftp@5.0.5:
resolution: {integrity: sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==}
engines: {node: '>=10.0.0'}
deprecated: Security vulnerability fixed in 5.2.0, please upgrade
bcrypt-pbkdf@1.0.2:
resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
@@ -15037,12 +15094,12 @@ snapshots:
dependencies:
'@better-scroll/core': 2.5.1
'@certd/commercial-core@1.38.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))':
'@certd/commercial-core@1.39.0(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))':
dependencies:
'@certd/basic': link:packages/core/basic
'@certd/lib-server': link:packages/libs/lib-server
'@certd/pipeline': link:packages/core/pipeline
'@certd/plus-core': 1.38.12
'@certd/plus-core': 1.39.0
'@midwayjs/core': 3.20.11
'@midwayjs/koa': 3.20.13
'@midwayjs/logger': 3.4.2
@@ -15077,19 +15134,19 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@certd/plugin-plus@1.38.12':
'@certd/plugin-plus@1.39.0':
dependencies:
'@certd/basic': link:packages/core/basic
'@certd/pipeline': link:packages/core/pipeline
'@certd/plugin-lib': link:packages/plugins/plugin-lib
'@certd/plus-core': 1.38.12
'@certd/plus-core': 1.39.0
crypto-js: 4.2.0
dayjs: 1.11.13
form-data: 4.0.2
jsrsasign: 11.1.0
querystring: 0.2.1
'@certd/plus-core@1.38.12':
'@certd/plus-core@1.39.0':
dependencies:
'@certd/basic': link:packages/core/basic
dayjs: 1.11.13
@@ -15712,6 +15769,8 @@ snapshots:
dependencies:
'@hapi/hoek': 9.3.0
'@hexagon/base64@1.1.28': {}
'@httptoolkit/websocket-stream@6.0.1':
dependencies:
'@types/ws': 8.18.1
@@ -16176,6 +16235,8 @@ snapshots:
- supports-color
- typescript
'@levischuck/tiny-cbor@0.2.11': {}
'@manypkg/find-root@2.2.3':
dependencies:
'@manypkg/tools': 1.1.2
@@ -16552,6 +16613,12 @@ snapshots:
dependencies:
'@noble/hashes': 1.8.0
'@peculiar/asn1-android@2.6.0':
dependencies:
'@peculiar/asn1-schema': 2.6.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-cms@2.3.15':
dependencies:
'@peculiar/asn1-schema': 2.3.15
@@ -16560,6 +16627,14 @@ snapshots:
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-cms@2.6.1':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.1
'@peculiar/asn1-x509-attr': 2.6.1
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-csr@2.3.15':
dependencies:
'@peculiar/asn1-schema': 2.3.15
@@ -16567,6 +16642,13 @@ snapshots:
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-csr@2.6.1':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.1
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-ecc@2.3.15':
dependencies:
'@peculiar/asn1-schema': 2.3.15
@@ -16574,6 +16656,13 @@ snapshots:
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-ecc@2.6.1':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.1
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-pfx@2.3.15':
dependencies:
'@peculiar/asn1-cms': 2.3.15
@@ -16583,6 +16672,15 @@ snapshots:
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-pfx@2.6.1':
dependencies:
'@peculiar/asn1-cms': 2.6.1
'@peculiar/asn1-pkcs8': 2.6.1
'@peculiar/asn1-rsa': 2.6.1
'@peculiar/asn1-schema': 2.6.0
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-pkcs8@2.3.15':
dependencies:
'@peculiar/asn1-schema': 2.3.15
@@ -16590,6 +16688,13 @@ snapshots:
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-pkcs8@2.6.1':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.1
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-pkcs9@2.3.15':
dependencies:
'@peculiar/asn1-cms': 2.3.15
@@ -16601,6 +16706,17 @@ snapshots:
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-pkcs9@2.6.1':
dependencies:
'@peculiar/asn1-cms': 2.6.1
'@peculiar/asn1-pfx': 2.6.1
'@peculiar/asn1-pkcs8': 2.6.1
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.1
'@peculiar/asn1-x509-attr': 2.6.1
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-rsa@2.3.15':
dependencies:
'@peculiar/asn1-schema': 2.3.15
@@ -16608,12 +16724,25 @@ snapshots:
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-rsa@2.6.1':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.1
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-schema@2.3.15':
dependencies:
asn1js: 3.0.6
pvtsutils: 1.3.6
tslib: 2.8.1
'@peculiar/asn1-schema@2.6.0':
dependencies:
asn1js: 3.0.6
pvtsutils: 1.3.6
tslib: 2.8.1
'@peculiar/asn1-x509-attr@2.3.15':
dependencies:
'@peculiar/asn1-schema': 2.3.15
@@ -16621,6 +16750,13 @@ snapshots:
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-x509-attr@2.6.1':
dependencies:
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.1
asn1js: 3.0.6
tslib: 2.8.1
'@peculiar/asn1-x509@2.3.15':
dependencies:
'@peculiar/asn1-schema': 2.3.15
@@ -16628,6 +16764,13 @@ snapshots:
pvtsutils: 1.3.6
tslib: 2.8.1
'@peculiar/asn1-x509@2.6.1':
dependencies:
'@peculiar/asn1-schema': 2.6.0
asn1js: 3.0.6
pvtsutils: 1.3.6
tslib: 2.8.1
'@peculiar/x509@1.12.3':
dependencies:
'@peculiar/asn1-cms': 2.3.15
@@ -16642,6 +16785,20 @@ snapshots:
tslib: 2.8.1
tsyringe: 4.10.0
'@peculiar/x509@1.14.3':
dependencies:
'@peculiar/asn1-cms': 2.6.1
'@peculiar/asn1-csr': 2.6.1
'@peculiar/asn1-ecc': 2.6.1
'@peculiar/asn1-pkcs9': 2.6.1
'@peculiar/asn1-rsa': 2.6.1
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.1
pvtsutils: 1.3.6
reflect-metadata: 0.2.2
tslib: 2.8.1
tsyringe: 4.10.0
'@pkgjs/parseargs@0.11.0':
optional: true
@@ -16906,6 +17063,19 @@ snapshots:
core-js: 3.42.0
nanopop: 2.4.2
'@simplewebauthn/browser@13.2.2': {}
'@simplewebauthn/server@13.2.3':
dependencies:
'@hexagon/base64': 1.1.28
'@levischuck/tiny-cbor': 0.2.11
'@peculiar/asn1-android': 2.6.0
'@peculiar/asn1-ecc': 2.6.1
'@peculiar/asn1-rsa': 2.6.1
'@peculiar/asn1-schema': 2.6.0
'@peculiar/asn1-x509': 2.6.1
'@peculiar/x509': 1.14.3
'@sinclair/typebox@0.27.8': {}
'@sindresorhus/is@0.14.0': {}
@@ -20659,13 +20829,13 @@ snapshots:
resolve: 1.22.10
semver: 6.3.1
eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8):
eslint-plugin-prettier@3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8):
dependencies:
eslint: 7.32.0
prettier: 2.8.8
prettier-linter-helpers: 1.0.0
optionalDependencies:
eslint-config-prettier: 8.10.0(eslint@8.57.0)
eslint-config-prettier: 8.10.0(eslint@7.32.0)
eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@8.57.0)(prettier@2.8.8):
dependencies:
@@ -23069,7 +23239,7 @@ snapshots:
eslint: 7.32.0
eslint-config-prettier: 8.10.0(eslint@7.32.0)
eslint-plugin-node: 11.1.0(eslint@7.32.0)
eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@8.57.0))(eslint@7.32.0)(prettier@2.8.8)
eslint-plugin-prettier: 3.4.1(eslint-config-prettier@8.10.0(eslint@7.32.0))(eslint@7.32.0)(prettier@2.8.8)
execa: 5.1.1
inquirer: 7.3.3
json5: 2.2.3
+1 -1
View File
@@ -1 +1 @@
23:21
00:03
+1 -1
View File
@@ -1 +1 @@
00:12
00:21