Compare commits

...

100 Commits

Author SHA1 Message Date
xiaojunnuo
adc3e6118b v1.39.7 2026-03-26 01:06:27 +08:00
xiaojunnuo
d933493c31 build: prepare to build 2026-03-26 01:02:42 +08:00
xiaojunnuo
f91d591b03 perf: 支持部署证书到火山引擎vod 2026-03-26 01:01:52 +08:00
xiaojunnuo
af6deb99cd perf: 支持部署到火山云tos自定义域名证书
https://github.com/certd/certd/issues/693
2026-03-26 00:05:30 +08:00
xiaojunnuo
c5d285f755 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-03-25 12:48:56 +08:00
xiaojunnuo
b1eb706925 fix: 修复cname校验报该授权无权限的bug 2026-03-25 12:48:47 +08:00
xiaojunnuo
5a01634ca3 build: release 2026-03-23 00:23:32 +08:00
xiaojunnuo
487676ce13 build: publish 2026-03-23 00:07:52 +08:00
xiaojunnuo
0280ca7b1a build: trigger build image 2026-03-23 00:07:40 +08:00
xiaojunnuo
b0ccab41e1 v1.39.6 2026-03-23 00:06:18 +08:00
xiaojunnuo
ccda3a3325 build: prepare to build 2026-03-23 00:03:15 +08:00
xiaojunnuo
4b7eeaa6e0 perf: 新增阿里云证书清理插件 2026-03-23 00:02:46 +08:00
xiaojunnuo
2951f0030d build: prepare to build 2026-03-22 23:33:45 +08:00
xiaojunnuo
acc2df29de perf: 支持复制粘贴任务步骤 2026-03-22 23:31:24 +08:00
xiaojunnuo
431afd618f perf: 企业模式下面增加个人数据迁移的引导 2026-03-22 00:49:54 +08:00
xiaojunnuo
6d5e5bd692 chore: 优化access edit 请求参数,删除多余的参数 2026-03-21 23:59:11 +08:00
xiaojunnuo
ffd2e8149e perf: 火山引擎部署alb证书插件支持部署扩展证书以及删除已过期扩展证书 2026-03-21 23:51:30 +08:00
xiaojunnuo
2ab92dc13e chore: cnb_sync 2026-03-20 18:14:31 +08:00
xiaojunnuo
7f6a8bc87e perf: 优化远程数据选择框,选择数据时不刷新闪烁 2026-03-20 18:04:13 +08:00
xiaojunnuo
b1ff163a28 fix: 修复模版id不正确导致修改到错误的模版流水线bug 2026-03-20 17:48:47 +08:00
xiaojunnuo
440d55ccb8 chore: sync push 2026-03-20 13:53:12 +08:00
xiaojunnuo
285532d431 fix: remote-select默认pageSize设置为50,阿里云WAF不支持pageSize100 2026-03-20 13:44:49 +08:00
xiaojunnuo
f2c47459f8 Merge branch 'v2' of https://github.com/certd/certd into v2 2026-03-20 12:09:01 +08:00
xiaojunnuo
49703f08e5 fix: 修复批量执行按钮无效的bug 2026-03-20 12:08:55 +08:00
xiaojunnuo
1d0aa9573b Merge branch 'v2' of https://github.com/certd/certd into v2 2026-03-19 01:12:34 +08:00
xiaojunnuo
b2014cf88b build: release 2026-03-19 01:11:46 +08:00
xiaojunnuo
a0e0078bad build: prepare to build 2026-03-19 01:09:00 +08:00
xiaojunnuo
5ebca21c32 build: publish 2026-03-19 00:52:27 +08:00
xiaojunnuo
970aea90c9 build: trigger build image 2026-03-19 00:52:15 +08:00
xiaojunnuo
5f9341ad8e v1.39.5 2026-03-19 00:50:53 +08:00
xiaojunnuo
574c0983f5 build: prepare to build 2026-03-19 00:48:08 +08:00
xiaojunnuo
be6c7c7ac8 build: prepare to build 2026-03-19 00:41:29 +08:00
xiaojunnuo
4fd31f276b build: prepare to build 2026-03-19 00:20:00 +08:00
xiaojunnuo
224db7da57 fix: 修复修改分组报错的bug 2026-03-19 00:15:15 +08:00
xiaojunnuo
1413e1aff4 perf: passkey登录放到下方其他登录位置 2026-03-19 00:14:05 +08:00
xiaojunnuo
68b669d3ff perf: 移除passkey的counter递增校验 2026-03-18 23:56:50 +08:00
xiaojunnuo
29f44c67c8 perf: passkey 支持Bitwarden 2026-03-18 23:13:37 +08:00
xiaojunnuo
3332d2288f chore: 支持推送到cnb 2026-03-18 16:16:22 +08:00
xiaojunnuo
34702196e1 chore: 优化workflow 2026-03-18 09:22:09 +08:00
xiaojunnuo
d45c8d1e9b build: release 2026-03-18 01:30:28 +08:00
xiaojunnuo
bc19825ada build: publish 2026-03-18 01:14:10 +08:00
xiaojunnuo
72bb640239 build: trigger build image 2026-03-18 01:13:58 +08:00
xiaojunnuo
aacee4a94c v1.39.4 2026-03-18 01:12:39 +08:00
xiaojunnuo
3ab37d5c5d build: prepare to build 2026-03-18 01:09:27 +08:00
xiaojunnuo
3dd3ecf8f1 build: prepare to build 2026-03-18 01:05:23 +08:00
xiaojunnuo
a3831827d0 chore: 1 2026-03-18 01:04:43 +08:00
xiaojunnuo
6aa6c957ee build: prepare to build 2026-03-18 00:43:45 +08:00
xiaojunnuo
9e12412f5f perf: 优化passkey 2026-03-18 00:43:01 +08:00
xiaojunnuo
0f9eb31740 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-03-17 23:33:18 +08:00
xiaojunnuo
6be8ab581d chore: 1 2026-03-17 23:33:13 +08:00
xiaojunnuo
d8425bc9c5 fix: 修复选择插件页面无法滚动的bug 2026-03-17 23:25:45 +08:00
xiaojunnuo
0ddcb9c00a chore: passkey perf 2026-03-17 19:16:11 +08:00
xiaojunnuo
6d43623f45 fix: 修复阿里云证书订单翻页问题 2026-03-17 18:56:52 +08:00
xiaojunnuo
196cd88010 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-03-17 14:53:51 +08:00
xiaojunnuo
7f37df4227 fix: 修复查看证书详情页面错位的bug 2026-03-17 14:53:46 +08:00
xiaojunnuo
985a12a63b build: release 2026-03-17 14:19:59 +08:00
xiaojunnuo
9058c0e9fc build: publish 2026-03-17 14:02:46 +08:00
xiaojunnuo
4b0cd32d12 build: trigger build image 2026-03-17 14:02:34 +08:00
xiaojunnuo
6cb51bc55d v1.39.3 2026-03-17 14:01:18 +08:00
xiaojunnuo
119e3c31c9 build: prepare to build 2026-03-17 13:58:14 +08:00
xiaojunnuo
56164c25d0 build: prepare to build 2026-03-17 13:56:03 +08:00
xiaojunnuo
c66e5f9fcd chore: remote-select mode还原回原来的模式 2026-03-17 13:54:02 +08:00
xiaojunnuo
12700e1754 fix: 修复多选框只能单选的bug 2026-03-17 13:33:12 +08:00
xiaojunnuo
50db6f0765 fix: 修复旧版1panel插件 报sslIds is not iterable的错误 2026-03-17 09:13:29 +08:00
xiaojunnuo
64e8adddfd build: release 2026-03-17 01:15:04 +08:00
xiaojunnuo
729a4d64e9 chore: project mode 2026-03-17 01:14:52 +08:00
xiaojunnuo
6f12504588 chore: docs 2026-03-17 01:12:03 +08:00
xiaojunnuo
271459f820 build: publish 2026-03-17 00:57:40 +08:00
xiaojunnuo
5000c95d01 build: trigger build image 2026-03-17 00:57:27 +08:00
xiaojunnuo
f477733483 v1.39.2 2026-03-17 00:56:06 +08:00
xiaojunnuo
54e1681c5e build: prepare to build 2026-03-17 00:53:10 +08:00
xiaojunnuo
2f6d9a156a chore: user profile 2026-03-17 00:34:20 +08:00
xiaojunnuo
10dd89ae62 fix: 修复京东云报错不准确的bug 2026-03-17 00:05:53 +08:00
xiaojunnuo
d01bfbec96 fix: cname provider授权修改为sys级别 2026-03-16 23:27:24 +08:00
xiaojunnuo
5eb4aa3a0e fix: 修复群晖测试时报addSecret undefine错误 2026-03-16 22:51:09 +08:00
xiaojunnuo
0b9933df1e perf: 查看证书增加证书详情显示,包括域名,过期时间,颁发机构,指纹等 2026-03-16 00:52:33 +08:00
xiaojunnuo
76d12d6062 perf: dns-provider 支持bind9 ,support bind9
https://github.com/certd/certd/issues/683
https://github.com/certd/certd/discussions/668
2026-03-15 23:55:49 +08:00
xiaojunnuo
cf10faf61c style: 调整复制按钮的显示样式为行内弹性布局 2026-03-15 18:35:13 +08:00
xiaojunnuo
1cbf9c1cd9 chore: 增加流水线,授权等文档 2026-03-15 18:26:49 +08:00
xiaojunnuo
25e361b9f9 chore: 修改权限判断字段从summary改成description 2026-03-15 16:20:20 +08:00
xiaojunnuo
b88ee33ae4 chore: tencent cos doc tip 2026-03-15 16:05:17 +08:00
xiaojunnuo
684964da4f chore: swagger support 2026-03-15 14:01:34 +08:00
xiaojunnuo
8a3841f638 perf: 支持批量转移流水线到其他项目 2026-03-15 04:17:40 +08:00
xiaojunnuo
f642e42eea chore: 优化passkey 2026-03-15 02:20:39 +08:00
xiaojunnuo
bbef854c02 chore: user profile 夜间模式 2026-03-13 19:44:55 +08:00
xiaojunnuo
e50611666e perf: 优化个人账户页面 2026-03-13 19:39:27 +08:00
xiaojunnuo
eae4f721e8 chore: passkey登录优化 2026-03-13 15:31:03 +08:00
xiaojunnuo
12fed34e10 fix: 修复提示支付失败的bug 2026-03-13 12:03:28 +08:00
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
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
311 changed files with 5989 additions and 1245 deletions

View File

@@ -4,7 +4,7 @@ on:
branches: ['v2-dev']
paths:
- "trigger/build.trigger"
workflow_dispatch: # 添加手动触发
# schedule:
# - # 国际时间 19:17 执行北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
# - cron: '17 19 * * *'
@@ -21,7 +21,7 @@ jobs:
with:
fetch-depth: 0
lfs: true
ref: 'v2-dev'
- name: get_certd_version
id: get_certd_version
uses: actions/github-script@v6

View File

@@ -8,6 +8,8 @@ on:
workflows: [ "build-image" ]
types:
- completed
workflow_dispatch: # 添加手动触发
# schedule:
@@ -26,6 +28,7 @@ jobs:
with:
fetch-depth: 0
ref: v2-dev
- name: get_certd_version
id: get_certd_version
uses: actions/github-script@v6

View File

@@ -8,7 +8,7 @@ on:
workflows: [ "build-image-for-release" ]
types:
- completed
workflow_dispatch: # 添加手动触发
# schedule:
# - # 国际时间 19:17 执行北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
# - cron: '17 19 * * *'
@@ -19,13 +19,17 @@ permissions:
jobs:
publish-atomgit:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
if: |
github.event_name == 'workflow_dispatch' ||
(github.event.workflow_run.conclusion == 'success')
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
lfs: true
ref: 'v2-dev'
- name: get_certd_version
id: get_certd_version
uses: actions/github-script@v6

View File

@@ -8,7 +8,7 @@ on:
workflows: [ "build-image-for-release" ]
types:
- completed
workflow_dispatch: # 添加手动触发
# schedule:
# - # 国际时间 19:17 执行北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
# - cron: '17 19 * * *'
@@ -19,13 +19,16 @@ permissions:
jobs:
publish-gitee:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
if: |
github.event_name == 'workflow_dispatch' ||
(github.event.workflow_run.conclusion == 'success')
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
lfs: true
ref: 'v2-dev'
- name: publish_to_gitee
id: publish_to_gitee

View File

@@ -8,7 +8,7 @@ on:
workflows: [ "build-image-for-release" ]
types:
- completed
workflow_dispatch: # 添加手动触发
# schedule:
# - # 国际时间 19:17 执行北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
# - cron: '17 19 * * *'
@@ -19,13 +19,16 @@ permissions:
jobs:
publish-github:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
if: |
github.event_name == 'workflow_dispatch' ||
(github.event.workflow_run.conclusion == 'success')
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
lfs: true
ref: 'v2-dev'
- name: publish_to_github
id: publish_to_github

View File

@@ -4,6 +4,7 @@ on:
branches: ['v2-dev']
paths:
- "trigger/release.trigger"
workflow_dispatch: # 添加手动触发
# workflow_run:
# workflows: [ "deploy-demo" ]
# types:
@@ -25,6 +26,7 @@ jobs:
with:
fetch-depth: 0
lfs: true
ref: 'v2-dev'
- name: get_certd_version
id: get_certd_version

View File

@@ -17,6 +17,7 @@ jobs:
with:
fetch-depth: 0
lfs: true
ref: v2-dev
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
run: |
git config --global user.name "xiaojunnuo"

View File

@@ -17,6 +17,7 @@ jobs:
with:
fetch-depth: 0
lfs: true
ref: v2
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
run: |
git config --global user.name "xiaojunnuo"

35
.github/workflows/sync-to-cnb-dev.yml vendored Normal file
View File

@@ -0,0 +1,35 @@
name: sync-to-cnb-dev
on:
push:
branches: ['v2-dev']
# schedule:
# - # 国际时间 19:17 执行北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
# - cron: '17 19 * * *'
permissions:
contents: read
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
uses: actions/checkout@v4
with:
fetch-depth: 0
lfs: true
ref: v2-dev
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
run: |
git config --global user.name "xiaojunnuo"
git config --global user.email "xiaojunnuo@qq.com"
- name: Set git token # 3. 给git命令设置token用于push到目标仓库
uses: de-vri-es/setup-git-credentials@v2
with: # token 格式为: username:password
credentials: https://cnb:${{secrets.CNB_TOKEN}}@cnb.cool
- name: push to cnb # 4. 执行同步
run: |
git remote add upstream https://cnb.cool/certd/certd.git
git push --set-upstream upstream v2-dev

34
.github/workflows/sync-to-cnb.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: sync-to-cnb
on:
push:
branches: ['v2']
# schedule:
# - # 国际时间 19:17 执行北京时间3:17 ↙↙↙ 改成你想要每天自动执行的时间
# - cron: '17 19 * * *'
permissions:
contents: read
jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Checkout work repo # 1. 检出当前仓库(certd-sync-work)
uses: actions/checkout@v4
with:
fetch-depth: 0
lfs: true
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
run: |
git config --global user.name "xiaojunnuo"
git config --global user.email "xiaojunnuo@qq.com"
- name: Set git token # 3. 给git命令设置token用于push到目标仓库
uses: de-vri-es/setup-git-credentials@v2
with: # token 格式为: username:password
credentials: https://cnb:${{secrets.CNB_TOKEN}}@cnb.cool
- name: push to cnb # 4. 执行同步
run: |
git remote add upstream https://cnb.cool/certd/certd.git
git push --set-upstream upstream v2

View File

@@ -17,6 +17,7 @@ jobs:
with:
fetch-depth: 0
lfs: true
ref: v2-dev
- name: Set git user # 2. 给git命令设置用户名和邮箱,↙↙↙ 改成你的name和email
run: |
git config --global user.name "xiaojunnuo"

View File

@@ -65,6 +65,20 @@ demoKeyId = '';
encrypt: true, //该属性是否需要加密
})
demoKeySecret = '';
@AccessInput({
title: '另外一个授权Id',//标题
component: {
name:"access-selector", //access选择组件
vModel:"modelValue",
type: "ssh", // access类型让用户固定选择这种类型的access
},
required: true, //text组件可以省略
})
otherAccessId;
```
### 4. 实现测试方法
@@ -93,7 +107,7 @@ async onTestRequest() {
```typescript
/**
* api接口示例 取域名列表,
* api接口示例 取域名列表,
*/
async GetDomainList(req: PageSearch): Promise<PageRes<DomainRecord>> {
//输出日志必须使用ctx.logger

14
.trae/skills/agent.md Normal file
View File

@@ -0,0 +1,14 @@
你是一名资深nodejs工程师擅长开发Certd开源系统的任务插件。
certd是一款全自动证书申请部署管理工具基于流水线的方式通过里面申请证书插件申请证书然后将证书传递给下一个部署任务插件不同的部署任务插件将证书部署到用户的各个应用系统当中。
certd插件分成以下几种类型
Access存储用户的第三放应用的授权数据比如用户名密码accessSecret 或 accessToken等。同时它里面的方法还负责对接第三方的api接口
Task 部署任务插件它继承AbstractTaskPlugin类被流水线调用execute方法将证书部署到对应的应用上
DnsProvider: DNS提供商插件它用于在ACME申请证书时给域名添加txt解析记录。
在开始工作前,请阅读并加载.trae/skills下面的技能根据skills进行相应的插件开发
当开发过程中遇到问题需要参考plugins目录下的其他插件或者用户提醒你更好的做法时你需要总结经验更新相应的skills让skills越来越完善能够在以后得新插件开发中具备指导意义。
一般调用的api接口文档会比较复杂你不知道接口是什么时请务必询问用户让用户提供API接口文档
完成开发后无需测试,通知用户自己去测试

View File

@@ -2,7 +2,7 @@
## 什么是插件转换工具
插件转换工具是一个用于将 Certd 插件转换为 YAML 配置文件的命令行工具。它可以分析单个插件文件,识别插件类型,并生成对应的 YAML 配置,方便插件的注册和管理
插件转换工具是一个用于将 Certd 插件转换为 YAML 配置文件的命令行工具。它可以分析单个插件文件,识别插件类型,并生成对应的 YAML 配置,可以让插件分发和在线注册
## 工具位置

View File

@@ -80,7 +80,8 @@ certDomains!: string[];
helper: 'demoAccess授权',
component: {
name: 'access-selector',
type: 'demo', // 固定授权类型
vModel:"modelValue",
type: "demo", // access类型让用户固定选择这种类型的access
},
// rules: [{ required: true, message: '此项必填' }],
// required: true, // 必填

10
.vscode/launch.json vendored
View File

@@ -79,5 +79,15 @@
"PLUS_SERVER_BASE_URL": "http://127.0.0.1:11007"
}
}
],
"compounds": [
{
"name": "all",
"configurations": [
"server",
"client",
],
"stopAll": false
},
]
}

View File

@@ -3,6 +3,85 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
### Bug Fixes
* 修复cname校验报该授权无权限的bug ([b1eb706](https://github.com/certd/certd/commit/b1eb7069258d6ff2b128091911fa448eaffc5f33))
### Performance Improvements
* 支持部署到火山云tos自定义域名证书 ([af6deb9](https://github.com/certd/certd/commit/af6deb99cd24a69a189b1fdd1df51c8f7816dcda))
* 支持部署证书到火山引擎vod ([f91d591](https://github.com/certd/certd/commit/f91d591b03c50166d9fa352ba11c62d963869aa5))
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
### Bug Fixes
* 修复模版id不正确导致修改到错误的模版流水线bug ([b1ff163](https://github.com/certd/certd/commit/b1ff163a2828b205297408d5aed21cf1eff335e8))
* 修复批量执行按钮无效的bug ([49703f0](https://github.com/certd/certd/commit/49703f08e55b303851086d9f36aca562d7999be6))
* remote-select默认pageSize设置为50阿里云WAF不支持pageSize100 ([285532d](https://github.com/certd/certd/commit/285532d4318b90d0d7f8154f070274c0a0ec0269))
### Performance Improvements
* 火山引擎部署alb证书插件支持部署扩展证书以及删除已过期扩展证书 ([ffd2e81](https://github.com/certd/certd/commit/ffd2e8149e3a06bf3eec456ff85dbed793af9e90))
* 企业模式下面增加个人数据迁移的引导 ([431afd6](https://github.com/certd/certd/commit/431afd618f547cecf9a29433f46d4367619e2ecf))
* 新增阿里云证书清理插件 ([4b7eeaa](https://github.com/certd/certd/commit/4b7eeaa6e0a14d2e461c7c473a920a0966b1fe8e))
* 优化远程数据选择框,选择数据时不刷新闪烁 ([7f6a8bc](https://github.com/certd/certd/commit/7f6a8bc87e364685defe7f039264b2de064806c5))
* 支持复制粘贴任务步骤 ([acc2df2](https://github.com/certd/certd/commit/acc2df29def017fb8165f931b41ef95414966afc))
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
### Bug Fixes
* 修复修改分组报错的bug ([224db7d](https://github.com/certd/certd/commit/224db7da57dbdddf25bcac7faa0a29eb228c5a33))
### Performance Improvements
* 移除passkey的counter递增校验 ([68b669d](https://github.com/certd/certd/commit/68b669d3ff3e13b931939093320ce7237bb02b1b))
* passkey 支持Bitwarden ([29f44c6](https://github.com/certd/certd/commit/29f44c67c808bed9ff1c9d4884d39a1a62d043a7))
* passkey登录放到下方其他登录位置 ([1413e1a](https://github.com/certd/certd/commit/1413e1aff4aabcfd471716338c210fbcfd76c8f9))
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
### Bug Fixes
* 修复阿里云证书订单翻页问题 ([6d43623](https://github.com/certd/certd/commit/6d43623f459a7594599e50a7ed89d67fcc775518))
* 修复查看证书详情页面错位的bug ([7f37df4](https://github.com/certd/certd/commit/7f37df42274e657892d92e868ceac67e139f3bf2))
* 修复选择插件页面无法滚动的bug ([d8425bc](https://github.com/certd/certd/commit/d8425bc9c5ee81bb669706c6de6bad69d7c38d8e))
### Performance Improvements
* 优化passkey ([9e12412](https://github.com/certd/certd/commit/9e12412f5fa7800df1d7efaf62cd8fd5d79bb569))
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
### Bug Fixes
* 修复多选框只能单选的bug ([12700e1](https://github.com/certd/certd/commit/12700e1754319513ac02822ff1588d63420b964e))
* 修复旧版1panel插件 报sslIds is not iterable的错误 ([50db6f0](https://github.com/certd/certd/commit/50db6f0765e7ec9a5698cd99540d90e188634fb1))
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
### Bug Fixes
* 修复当证书更新后第一次站点检查会报与主站证书过期时间不一致错误的bug ([dd999b6](https://github.com/certd/certd/commit/dd999b60a4fe3507ff5e0109d637b4e891b28bdd))
* 修复京东云报错不准确的bug ([10dd89a](https://github.com/certd/certd/commit/10dd89ae62e438a211a15e729559af823a096583))
* 修复群晖测试时报addSecret undefine错误 ([5eb4aa3](https://github.com/certd/certd/commit/5eb4aa3a0eab9ffa729c8e813cbf973d9683cc13))
* 修复提示支付失败的bug ([12fed34](https://github.com/certd/certd/commit/12fed34e109f3254de664813954081a52513bd38))
* 修复修改项目名称后没有同步刷新的bug ([3abee72](https://github.com/certd/certd/commit/3abee72fee286864b665033b23b172ef0ea92d83))
* cname provider授权修改为sys级别 ([d01bfbe](https://github.com/certd/certd/commit/d01bfbec96a3a2109ec864953b0c9e8c1f95b97b))
### Performance Improvements
* 查看证书增加证书详情显示,包括域名,过期时间,颁发机构,指纹等 ([0b9933d](https://github.com/certd/certd/commit/0b9933df1e8d1685d14271435a8a7488974cc47b))
* 获取阿里证书订单id组件增加翻页功能突破50的上限 ([d79db3b](https://github.com/certd/certd/commit/d79db3bd3f0d5ad39664bb47ec3814d43ad93304))
* 优化阿里云连接超时时长为10秒支持配置环境变量 ([1588461](https://github.com/certd/certd/commit/1588461633bd275765daa96fc68320abb58d616d))
* 优化个人账户页面 ([e506116](https://github.com/certd/certd/commit/e50611666ef731a903d7bdd8eb62333b97e2cc5b))
* 支持批量转移流水线到其他项目 ([8a3841f](https://github.com/certd/certd/commit/8a3841f6382b53ce2343307fb035e74fa5383fef))
* 支持passkey登录 ([10b7644](https://github.com/certd/certd/commit/10b7644bb7ba5f82776537bc0c4f5eb95d5f8e4e))
* dns-provider 支持bind9 support bind9 ([76d12d6](https://github.com/certd/certd/commit/76d12d60624c0672fd3717a80a2cfef6845b14b8))
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
### Bug Fixes

View File

@@ -3,6 +3,85 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
### Bug Fixes
* 修复模版id不正确导致修改到错误的模版流水线bug ([b1ff163](https://github.com/certd/certd/commit/b1ff163a2828b205297408d5aed21cf1eff335e8))
* 修复批量执行按钮无效的bug ([49703f0](https://github.com/certd/certd/commit/49703f08e55b303851086d9f36aca562d7999be6))
* remote-select默认pageSize设置为50阿里云WAF不支持pageSize100 ([285532d](https://github.com/certd/certd/commit/285532d4318b90d0d7f8154f070274c0a0ec0269))
### Performance Improvements
* 火山引擎部署alb证书插件支持部署扩展证书以及删除已过期扩展证书 ([ffd2e81](https://github.com/certd/certd/commit/ffd2e8149e3a06bf3eec456ff85dbed793af9e90))
* 企业模式下面增加个人数据迁移的引导 ([431afd6](https://github.com/certd/certd/commit/431afd618f547cecf9a29433f46d4367619e2ecf))
* 新增阿里云证书清理插件 ([4b7eeaa](https://github.com/certd/certd/commit/4b7eeaa6e0a14d2e461c7c473a920a0966b1fe8e))
* 优化远程数据选择框,选择数据时不刷新闪烁 ([7f6a8bc](https://github.com/certd/certd/commit/7f6a8bc87e364685defe7f039264b2de064806c5))
* 支持复制粘贴任务步骤 ([acc2df2](https://github.com/certd/certd/commit/acc2df29def017fb8165f931b41ef95414966afc))
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
### Bug Fixes
* 修复修改分组报错的bug ([224db7d](https://github.com/certd/certd/commit/224db7da57dbdddf25bcac7faa0a29eb228c5a33))
### Performance Improvements
* 移除passkey的counter递增校验 ([68b669d](https://github.com/certd/certd/commit/68b669d3ff3e13b931939093320ce7237bb02b1b))
* passkey 支持Bitwarden ([29f44c6](https://github.com/certd/certd/commit/29f44c67c808bed9ff1c9d4884d39a1a62d043a7))
* passkey登录放到下方其他登录位置 ([1413e1a](https://github.com/certd/certd/commit/1413e1aff4aabcfd471716338c210fbcfd76c8f9))
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
### Bug Fixes
* 修复阿里云证书订单翻页问题 ([6d43623](https://github.com/certd/certd/commit/6d43623f459a7594599e50a7ed89d67fcc775518))
* 修复查看证书详情页面错位的bug ([7f37df4](https://github.com/certd/certd/commit/7f37df42274e657892d92e868ceac67e139f3bf2))
* 修复选择插件页面无法滚动的bug ([d8425bc](https://github.com/certd/certd/commit/d8425bc9c5ee81bb669706c6de6bad69d7c38d8e))
### Performance Improvements
* 优化passkey ([9e12412](https://github.com/certd/certd/commit/9e12412f5fa7800df1d7efaf62cd8fd5d79bb569))
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
### Bug Fixes
* 修复多选框只能单选的bug ([12700e1](https://github.com/certd/certd/commit/12700e1754319513ac02822ff1588d63420b964e))
* 修复旧版1panel插件 报sslIds is not iterable的错误 ([50db6f0](https://github.com/certd/certd/commit/50db6f0765e7ec9a5698cd99540d90e188634fb1))
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
### Bug Fixes
* 修复当证书更新后第一次站点检查会报与主站证书过期时间不一致错误的bug ([dd999b6](https://github.com/certd/certd/commit/dd999b60a4fe3507ff5e0109d637b4e891b28bdd))
* 修复京东云报错不准确的bug ([10dd89a](https://github.com/certd/certd/commit/10dd89ae62e438a211a15e729559af823a096583))
* 修复群晖测试时报addSecret undefine错误 ([5eb4aa3](https://github.com/certd/certd/commit/5eb4aa3a0eab9ffa729c8e813cbf973d9683cc13))
* 修复提示支付失败的bug ([12fed34](https://github.com/certd/certd/commit/12fed34e109f3254de664813954081a52513bd38))
* 修复修改项目名称后没有同步刷新的bug ([3abee72](https://github.com/certd/certd/commit/3abee72fee286864b665033b23b172ef0ea92d83))
* cname provider授权修改为sys级别 ([d01bfbe](https://github.com/certd/certd/commit/d01bfbec96a3a2109ec864953b0c9e8c1f95b97b))
### Performance Improvements
* 查看证书增加证书详情显示,包括域名,过期时间,颁发机构,指纹等 ([0b9933d](https://github.com/certd/certd/commit/0b9933df1e8d1685d14271435a8a7488974cc47b))
* 获取阿里证书订单id组件增加翻页功能突破50的上限 ([d79db3b](https://github.com/certd/certd/commit/d79db3bd3f0d5ad39664bb47ec3814d43ad93304))
* 优化阿里云连接超时时长为10秒支持配置环境变量 ([1588461](https://github.com/certd/certd/commit/1588461633bd275765daa96fc68320abb58d616d))
* 优化个人账户页面 ([e506116](https://github.com/certd/certd/commit/e50611666ef731a903d7bdd8eb62333b97e2cc5b))
* 支持批量转移流水线到其他项目 ([8a3841f](https://github.com/certd/certd/commit/8a3841f6382b53ce2343307fb035e74fa5383fef))
* 支持passkey登录 ([10b7644](https://github.com/certd/certd/commit/10b7644bb7ba5f82776537bc0c4f5eb95d5f8e4e))
* dns-provider 支持bind9 support bind9 ([76d12d6](https://github.com/certd/certd/commit/76d12d60624c0672fd3717a80a2cfef6845b14b8))
## [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

View File

@@ -40,7 +40,7 @@ admin/123456
https://gitee.com/certd/certd/raw/v2/docker/run/docker-compose.yaml
::: tip
默认使用SQLite数据库如果需要使用MySQL、PostgreSQL数据库请参考[多数据库支持](./install/database.md)
默认使用SQLite数据库如果需要使用MySQL、PostgreSQL数据库请参考[多数据库支持](../database.md)
:::
2. 然后到 `1Panel->容器->编排->新建编排`

View File

@@ -31,7 +31,7 @@
![](./images/2.png)
::: tip
默认安装使用SQLite数据库如果需要使用MySQL、PostgreSQL数据库请参考[多数据库支持](./install/database.md)
默认安装使用SQLite数据库如果需要使用MySQL、PostgreSQL数据库请参考[多数据库支持](../database.md)
:::

View File

@@ -43,7 +43,7 @@ docker compose up -d
> https://docs.docker.com/compose/install/linux/
::: tip
默认安装使用SQLite数据库如果需要使用MySQL、PostgreSQL数据库请参考[多数据库支持](./install/database.md)
默认安装使用SQLite数据库如果需要使用MySQL、PostgreSQL数据库请参考[多数据库支持](../database.md)
:::
### 3. 访问测试

View File

@@ -20,58 +20,59 @@
| 16.| **APISIX授权** | |
| 17.| **亚马逊云aws授权** | |
| 18.| **亚马逊云科技(国区)授权** | |
| 19.| **CacheFly** | CacheFly |
| 20.| **EAB授权** | ZeroSSL证书申请需要EAB授权 |
| 21.| **google cloud** | 谷歌云授权 |
| 22.| **cloudflare授权** | |
| 23.| **中国移动CND授权** | |
| 24.| **授权插件示例** | 这是一个示例授权插件,用于演示如何实现一个授权插件 |
| 25.| **dns.la授权** | |
| 26.| **多吉云** | |
| 27.| **Dokploy授权** | |
| 28.| **farcdn授权** | |
| 29.| **FlexCDN授权** | |
| 30.| **Gcore** | Gcore |
| 31.| **Github授权** | |
| 32.| **godaddy授权** | |
| 33.| **金山云授权** | |
| 34.| **FTP授权** | |
| 35.| **七牛OSS授权** | |
| 36.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 |
| 37.| **s3/minio授权** | S3/minio oss授权 |
| 38.| **namesilo授权** | |
| 39.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 |
| 40.| **1panel授权** | 账号和密码 |
| 41.| **支付宝** | |
| 42.| **白山云授权** | |
| 43.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 |
| 44.| **cdnfly授权** | |
| 45.| **k8s授权** | |
| 46.| **括彩云cdn授权** | 括彩云CDN每月免费30G[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
| 47.| **LeCDN授权** | |
| 48.| **lucky** | |
| 49.| **猫云授权** | |
| 50.| **plesk授权** | |
| 51.| **长亭雷池授权** | |
| 52.| **群晖登录授权** | |
| 53.| **uniCloud** | unicloud授权 |
| 54.| **微信支付** | |
| 55.| **易盾rcdn授权** | 易盾CDN每月免费30G[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
| 56.| **易发云短信** | sms.yfyidc.cn/ |
| 57.| **易盾DCDN授权** | https://user.yiduncdn.com |
| 58.| **易支付** | |
| 59.| **proxmox** | |
| 60.| **UCloud授权** | 优刻得授权 |
| 61.| **又拍云** | |
| 62.| **网宿授权** | |
| 63.| **西部数码授权** | |
| 64.| **我爱云授权** | 我爱云CDN |
| 65.| **新网授权(代理方式)** | |
| 66.| **新网授权** | |
| 67.| **新网互联授权** | 仅支持代理账号ip需要加入白名单 |
| 68.| **Zenlayer授权** | Zenlayer授权 |
| 69.| **GoEdge授权** | |
| 70.| **雨云授权** | https://app.rainyun.com/ |
| 19.| **BIND9 DNS 授权** | 通过 SSH 连接到 BIND9 服务器,使用 nsupdate 命令管理 DNS 记录 |
| 20.| **CacheFly** | CacheFly |
| 21.| **EAB授权** | ZeroSSL证书申请需要EAB授权 |
| 22.| **google cloud** | 谷歌云授权 |
| 23.| **cloudflare授权** | |
| 24.| **中国移动CND授权** | |
| 25.| **授权插件示例** | 这是一个示例授权插件,用于演示如何实现一个授权插件 |
| 26.| **dns.la授权** | |
| 27.| **多吉云** | |
| 28.| **Dokploy授权** | |
| 29.| **farcdn授权** | |
| 30.| **FlexCDN授权** | |
| 31.| **Gcore** | Gcore |
| 32.| **Github授权** | |
| 33.| **godaddy授权** | |
| 34.| **金山云授权** | |
| 35.| **FTP授权** | |
| 36.| **七牛OSS授权** | |
| 37.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 |
| 38.| **s3/minio授权** | S3/minio oss授权 |
| 39.| **namesilo授权** | |
| 40.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 |
| 41.| **1panel授权** | 账号和密码 |
| 42.| **支付宝** | |
| 43.| **白山云授权** | |
| 44.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 |
| 45.| **cdnfly授权** | |
| 46.| **k8s授权** | |
| 47.| **括彩云cdn授权** | 括彩云CDN每月免费30G[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
| 48.| **LeCDN授权** | |
| 49.| **lucky** | |
| 50.| **猫云授权** | |
| 51.| **plesk授权** | |
| 52.| **长亭雷池授权** | |
| 53.| **群晖登录授权** | |
| 54.| **uniCloud** | unicloud授权 |
| 55.| **微信支付** | |
| 56.| **易盾rcdn授权** | 易盾CDN每月免费30G[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
| 57.| **易发云短信** | sms.yfyidc.cn/ |
| 58.| **易盾DCDN授权** | https://user.yiduncdn.com |
| 59.| **易支付** | |
| 60.| **proxmox** | |
| 61.| **UCloud授权** | 优刻得授权 |
| 62.| **又拍云** | |
| 63.| **网宿授权** | |
| 64.| **西部数码授权** | |
| 65.| **我爱云授权** | 我爱云CDN |
| 66.| **新网授权(代理方式)** | |
| 67.| **新网授权** | |
| 68.| **新网互联授权** | 仅支持代理账号ip需要加入白名单 |
| 69.| **Zenlayer授权** | Zenlayer授权 |
| 70.| **GoEdge授权** | |
| 71.| **雨云授权** | https://app.rainyun.com/ |
<style module>
table th:first-of-type {

View File

@@ -1,5 +1,5 @@
# 任务插件
`125` 款任务插件
`128` 款任务插件
## 1. 证书申请
| 序号 | 名称 | 说明 |
@@ -81,22 +81,23 @@
| 序号 | 名称 | 说明 |
|-----|-----|-----|
| 1.| **阿里云-部署到Ack** | 部署到阿里云Ack集群Ingress等通过Secret管理证书的应用 |
| 2.| **阿里云-部署至ALB应用负载均衡** | ALB,更新监听器的默认证书 |
| 3.| **阿里云-部署至任意云资源** | 【不建议使用】需要消耗阿里云自动部署次数支持SLB、LIVE、webHosting、VOD、CR、DCDN、DDoS、CDN、ALB、APIGateway、FC、GA、MSE、NLB、OSS、SAE、WAF等云产品 |
| 4.| **阿里云-部署至云原生API网关/AI网关** | 自动部署域名证书至云原生API网关、AI网关 |
| 5.| **阿里云-部署证书至API网关** | 自动部署域名证书至阿里云API网关APIGateway |
| 6.| **阿里云-部署证书至CDN** | 自动部署域名证书至阿里云CDN |
| 7.| **阿里云-部署证书至DCDN** | 依赖证书申请前置任务,自动部署域名证书至阿里云DCDN |
| 8.| **阿里云-部署至ESA** | 部署证书到阿里云ESA(边缘安全加速),自动删除过期证书 |
| 9.| **阿里云-部署至阿里云FC(3.0)** | 部署证书到阿里云函数计算FC3.0 |
| 10.| **阿里云-部署至GA** | 部署证书到阿里云GA(全球加速),支持更新默认证书和扩展证书 |
| 11.| **阿里云-部署至NLB网络负载均衡** | NLB,网络负载均衡,更新监听器的默认证书 |
| 12.| **阿里云-部署证书至OSS** | 部署域名证书至阿里云OSS自定义域名不是上传到阿里云oss |
| 13.| **阿里云-部署至CLB(传统负载均衡)** | 部署证书到阿里云CLB(传统负载均衡) |
| 14.| **阿里云-部署至VOD** | 部署证书到阿里云视频点播vod |
| 15.| **阿里云-部署至阿里云WAF** | 部署证书到阿里云WAF |
| 16.| **阿里云-上传证书到CAS** | 上传证书到阿里云证书管理服务CAS如果不想在阿里云上同一份证书上传多次可以把此任务作为前置任务其他阿里云任务证书那一项选择此任务的输出 |
| 1.| **阿里云-删除即将过期证书** | 仅删除未使用的证书 |
| 2.| **阿里云-部署到Ack** | 部署到阿里云Ack集群Ingress等通过Secret管理证书的应用 |
| 3.| **阿里云-部署至ALB应用负载均衡** | ALB,更新监听器的默认证书 |
| 4.| **阿里云-部署至任意云资源** | 【不建议使用】需要消耗阿里云自动部署次数支持SLB、LIVE、webHosting、VOD、CR、DCDN、DDoS、CDN、ALB、APIGateway、FC、GA、MSE、NLB、OSS、SAE、WAF等云产品 |
| 5.| **阿里云-部署至云原生API网关/AI网关** | 自动部署域名证书至云原生API网关、AI网关 |
| 6.| **阿里云-部署证书至API网关** | 自动部署域名证书至阿里云API网关APIGateway |
| 7.| **阿里云-部署证书至CDN** | 自动部署域名证书至阿里云CDN |
| 8.| **阿里云-部署证书至DCDN** | 依赖证书申请前置任务自动部署域名证书至阿里云DCDN |
| 9.| **阿里云-部署至ESA** | 部署证书到阿里云ESA(边缘安全加速),自动删除过期证书 |
| 10.| **阿里云-部署至阿里云FC(3.0)** | 部署证书到阿里云函数计算FC3.0 |
| 11.| **阿里云-部署至GA** | 部署证书到阿里云GA(全球加速),支持更新默认证书和扩展证书 |
| 12.| **阿里云-部署至NLB网络负载均衡** | NLB,网络负载均衡,更新监听器的默认证书 |
| 13.| **阿里云-部署证书至OSS** | 部署域名证书至阿里云OSS自定义域名不是上传到阿里云oss |
| 14.| **阿里云-部署至CLB(传统负载均衡)** | 部署证书到阿里云CLB(传统负载均衡) |
| 15.| **阿里云-部署至VOD** | 部署证书到阿里云视频点播vod |
| 16.| **阿里云-部署至阿里云WAF** | 部署证书到阿里云WAF |
| 17.| **阿里云-上传证书到CAS** | 上传证书到阿里云证书管理服务CAS如果不想在阿里云上同一份证书上传多次可以把此任务作为前置任务其他阿里云任务证书那一项选择此任务的输出 |
## 6. 华为云
| 序号 | 名称 | 说明 |
@@ -112,9 +113,9 @@
| 1.| **腾讯云-删除即将过期证书** | 仅删除未使用的证书 |
| 2.| **腾讯云-部署证书到任意云资源** | 支持负载均衡、CDN、DDoS、直播、点播、Web应用防火墙、API网关、TEO、容器服务、对象存储、轻应用服务器、云原生微服务、云开发 |
| 3.| **腾讯云-部署到CDN废弃** | 已废弃请使用v2版 |
| 4.| **腾讯云-部署到CDN-v2** | 推荐使用 |
| 4.| **腾讯云-部署到CDN-v2** | 推荐使用支持CDN域名以及COS加速域名 |
| 5.| **腾讯云-部署到CLB** | 暂时只支持单向认证证书,暂时只支持通用负载均衡 |
| 6.| **腾讯云-部署证书到COS** | 部署到腾讯云COS源站域名证书【注意很不稳定需要重试很多次偶尔才能成功一次】 |
| 6.| **腾讯云-部署证书到COS** | 部署到腾讯云COS源站域名证书注意是源站域名加速域名请使用腾讯云CDN v2插件【注意:很不稳定,需要重试很多次偶尔才能成功一次】 |
| 7.| **腾讯云-部署到腾讯云EO** | 腾讯云边缘安全加速平台EdgeOne(EO) |
| 8.| **腾讯云-部署到腾讯云直播** | https://console.cloud.tencent.com/live/ |
| 9.| **腾讯云-部署到TKE** | 修改TKE集群密钥配置支持Opaque和TLS证书类型。注意 1. serverless集群请使用K8S部署插件 2. Opaque类型需要【上传到腾讯云】作为前置任务 3. ApiServer需要开通公网访问或者certd可访问实际上底层仍然是通过KubeClient进行部署 |
@@ -130,8 +131,9 @@
| 3.| **火山引擎-部署证书至CLB** | 部署至火山引擎负载均衡 |
| 4.| **火山引擎-部署证书至DCDN** | 部署至火山引擎全站加速 |
| 5.| **火山引擎-部署证书至Live** | 部署至火山引擎视频直播 |
| 6.| **火山引擎-部署证书至VOD** | 部署至火山引擎视频点播(暂不可用) |
| 7.| **火山引擎-上传证书至证书中心** | 上传证书至火山引擎证书中心 |
| 6.| **火山引擎-部署证书至TOS自定义域名** | 仅限TOS自定义域名加速域名请选择火山引擎的CDN插件 |
| 7.| **火山引擎-部署证书至VOD** | 部署至火山引擎视频点播 |
| 8.| **火山引擎-上传证书至证书中心** | 上传证书至火山引擎证书中心 |
## 9. 京东云
| 序号 | 名称 | 说明 |

View File

@@ -9,18 +9,19 @@
| 5.| **京东云** | 京东云DNS解析提供商 |
| 6.| **新网(代理方式)** | 新网域名解析(代理方式) |
| 7.| **新网** | 新网域名解析 |
| 8.| **cloudflare** | cloudflare dns provider |
| 9.| **dns.la** | dns.la |
| 10.| **godaddy** | GoDaddy |
| 11.| **华为云** | 华为云DNS解析提供商 |
| 12.| **namesilo** | namesilo dns provider |
| 13.| **雨云** | 雨云DNS解析提供商 |
| 14.| **腾讯** | 腾讯云域名DNS解析提供 |
| 15.| **腾讯云EO DNS** | 腾讯云EO DNS解析提供者 |
| 16.| **西部数码** | west dns provider |
| 17.| **Dns提供商Demo** | dns provider示例 |
| 18.| **51dns** | 51DNS |
| 19.| **新网互联** | 新网互联 |
| 8.| **BIND9 DNS** | 通过 SSH 连接到 BIND9 服务器,使用 nsupdate 命令管理 DNS 记录 |
| 9.| **cloudflare** | cloudflare dns provider |
| 10.| **dns.la** | dns.la |
| 11.| **godaddy** | GoDaddy |
| 12.| **华为云** | 华为云DNS解析提供商 |
| 13.| **namesilo** | namesilo dns provider |
| 14.| **** | 雨云DNS解析提供 |
| 15.| **腾讯云** | 腾讯云域名DNS解析提供者 |
| 16.| **腾讯云EO DNS** | 腾讯云EO DNS解析提供者 |
| 17.| **西部数码** | west dns provider |
| 18.| **Dns提供商Demo** | dns provider示例 |
| 19.| **51dns** | 51DNS |
| 20.| **新网互联** | 新网互联 |
<style module>
table th:first-of-type {

View File

@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.39.1"
"version": "1.39.7"
}

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",

View File

@@ -3,6 +3,32 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/publishlab/node-acme-client/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/acme-client
## [1.39.6](https://github.com/publishlab/node-acme-client/compare/v1.39.5...v1.39.6) (2026-03-22)
**Note:** Version bump only for package @certd/acme-client
## [1.39.5](https://github.com/publishlab/node-acme-client/compare/v1.39.4...v1.39.5) (2026-03-18)
**Note:** Version bump only for package @certd/acme-client
## [1.39.4](https://github.com/publishlab/node-acme-client/compare/v1.39.3...v1.39.4) (2026-03-17)
**Note:** Version bump only for package @certd/acme-client
## [1.39.3](https://github.com/publishlab/node-acme-client/compare/v1.39.2...v1.39.3) (2026-03-17)
**Note:** Version bump only for package @certd/acme-client
## [1.39.2](https://github.com/publishlab/node-acme-client/compare/v1.39.1...v1.39.2) (2026-03-16)
### Bug Fixes
* 修复京东云报错不准确的bug ([10dd89a](https://github.com/publishlab/node-acme-client/commit/10dd89ae62e438a211a15e729559af823a096583))
## [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

View File

@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.39.1",
"version": "1.39.7",
"type": "module",
"module": "scr/index.js",
"main": "src/index.js",
@@ -18,7 +18,7 @@
"types"
],
"dependencies": {
"@certd/basic": "^1.39.1",
"@certd/basic": "^1.39.7",
"@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": "3bb29abe32c311e1cf840f97d485d1147411992a"
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
}

View File

@@ -174,7 +174,7 @@ export default async (client, userOpts) => {
} catch (e) {
log(`[auto] [${d}] challengeCreateFn threw error: ${e.message}`);
log(`[auto] [${d}] challengeCreateFn threw error: ${e.message || e}`);
await deactivateAuth(e);
throw e;
}

View File

@@ -3,6 +3,32 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/basic
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
**Note:** Version bump only for package @certd/basic
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
**Note:** Version bump only for package @certd/basic
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
**Note:** Version bump only for package @certd/basic
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
**Note:** Version bump only for package @certd/basic
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
### Bug Fixes
* 修复群晖测试时报addSecret undefine错误 ([5eb4aa3](https://github.com/certd/certd/commit/5eb4aa3a0eab9ffa729c8e813cbf973d9683cc13))
## [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

View File

@@ -1 +1 @@
23:44
01:02

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.39.1",
"version": "1.39.7",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -47,5 +47,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a"
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
}

View File

@@ -19,6 +19,7 @@ export function resetLogConfigure() {
}
resetLogConfigure();
export const logger: ILogger = log4js.getLogger("default") as any;
logger.addSecret = (secret: string) => {};
export function resetLogFilePath(filePath: string) {
logFilePath = filePath;
@@ -106,8 +107,8 @@ export class PipelineLogger implements ILogger {
logger: ILogger;
customWriter!: (text: string) => void;
constructor(name: string, write: (text: string) => void) {
this.customWriter = write;
constructor(name: string, write?: (text: string) => void) {
this.customWriter = write || (() => {});
//@ts-ignore
this.logger = log4js.getLogger(name);
}

View File

@@ -3,6 +3,32 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/pipeline
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
**Note:** Version bump only for package @certd/pipeline
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
**Note:** Version bump only for package @certd/pipeline
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
**Note:** Version bump only for package @certd/pipeline
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
**Note:** Version bump only for package @certd/pipeline
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
### Bug Fixes
* cname provider授权修改为sys级别 ([d01bfbe](https://github.com/certd/certd/commit/d01bfbec96a3a2109ec864953b0c9e8c1f95b97b))
## [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

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.39.1",
"version": "1.39.7",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -18,8 +18,8 @@
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/basic": "^1.39.1",
"@certd/plus-core": "^1.39.1",
"@certd/basic": "^1.39.7",
"@certd/plus-core": "^1.39.7",
"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": "3bb29abe32c311e1cf840f97d485d1147411992a"
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
}

View File

@@ -18,6 +18,7 @@ export type PluginRequestHandleReq<T = any> = {
input: T;
data: any;
record: { id: number; type: string; title: string };
fromType?: "sys" | "user"; // sys、user
};
export type UserInfo = {

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/lib-huawei
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
**Note:** Version bump only for package @certd/lib-huawei
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
**Note:** Version bump only for package @certd/lib-huawei
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
**Note:** Version bump only for package @certd/lib-huawei
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
**Note:** Version bump only for package @certd/lib-huawei
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
**Note:** Version bump only for package @certd/lib-huawei
## [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

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.39.1",
"version": "1.39.7",
"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": "3bb29abe32c311e1cf840f97d485d1147411992a"
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
}

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/lib-iframe
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
**Note:** Version bump only for package @certd/lib-iframe
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
**Note:** Version bump only for package @certd/lib-iframe
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
**Note:** Version bump only for package @certd/lib-iframe
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
**Note:** Version bump only for package @certd/lib-iframe
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
**Note:** Version bump only for package @certd/lib-iframe
## [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

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.39.1",
"version": "1.39.7",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -31,5 +31,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a"
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
}

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/jdcloud
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
**Note:** Version bump only for package @certd/jdcloud
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
**Note:** Version bump only for package @certd/jdcloud
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
**Note:** Version bump only for package @certd/jdcloud
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
**Note:** Version bump only for package @certd/jdcloud
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
**Note:** Version bump only for package @certd/jdcloud
## [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

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/jdcloud",
"version": "1.39.1",
"version": "1.39.7",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
@@ -56,5 +56,5 @@
"fetch"
]
},
"gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a"
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
}

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/lib-k8s
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
**Note:** Version bump only for package @certd/lib-k8s
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
**Note:** Version bump only for package @certd/lib-k8s
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
**Note:** Version bump only for package @certd/lib-k8s
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
**Note:** Version bump only for package @certd/lib-k8s
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
**Note:** Version bump only for package @certd/lib-k8s
## [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

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/lib-k8s",
"private": false,
"version": "1.39.1",
"version": "1.39.7",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -17,7 +17,7 @@
"pub": "npm publish"
},
"dependencies": {
"@certd/basic": "^1.39.1",
"@certd/basic": "^1.39.7",
"@kubernetes/client-node": "0.21.0"
},
"devDependencies": {
@@ -32,5 +32,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a"
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
}

View File

@@ -3,6 +3,40 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
### Bug Fixes
* 修复cname校验报该授权无权限的bug ([b1eb706](https://github.com/certd/certd/commit/b1eb7069258d6ff2b128091911fa448eaffc5f33))
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
**Note:** Version bump only for package @certd/lib-server
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
**Note:** Version bump only for package @certd/lib-server
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
**Note:** Version bump only for package @certd/lib-server
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
**Note:** Version bump only for package @certd/lib-server
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
### Bug Fixes
* 修复京东云报错不准确的bug ([10dd89a](https://github.com/certd/certd/commit/10dd89ae62e438a211a15e729559af823a096583))
### Performance Improvements
* 优化阿里云连接超时时长为10秒支持配置环境变量 ([1588461](https://github.com/certd/certd/commit/1588461633bd275765daa96fc68320abb58d616d))
* 优化个人账户页面 ([e506116](https://github.com/certd/certd/commit/e50611666ef731a903d7bdd8eb62333b97e2cc5b))
* 支持批量转移流水线到其他项目 ([8a3841f](https://github.com/certd/certd/commit/8a3841f6382b53ce2343307fb035e74fa5383fef))
## [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

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.39.1",
"version": "1.39.7",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -28,11 +28,11 @@
],
"license": "AGPL",
"dependencies": {
"@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",
"@certd/acme-client": "^1.39.7",
"@certd/basic": "^1.39.7",
"@certd/pipeline": "^1.39.7",
"@certd/plugin-lib": "^1.39.7",
"@certd/plus-core": "^1.39.7",
"@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": "3bb29abe32c311e1cf840f97d485d1147411992a"
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
}

View File

@@ -115,11 +115,17 @@ export abstract class BaseController {
if (projectId) {
await authService.checkProjectId(service, id, projectId);
}else{
if(allowAdmin){
await authService.checkUserIdButAllowAdmin(this.ctx, service, id);
if(userId === Constants.systemUserId){
//系统级别,不检查权限
}else{
await authService.checkUserId( service, id, userId);
if(allowAdmin){
await authService.checkUserIdButAllowAdmin(this.ctx, service, id);
}else{
await authService.checkUserId( service, id, userId);
}
}
}
return {projectId,userId}
}

View File

@@ -4,6 +4,7 @@ import { Inject } from '@midwayjs/core';
import { TypeORMDataSourceManager } from '@midwayjs/typeorm';
import { EntityManager } from 'typeorm/entity-manager/EntityManager.js';
import { FindManyOptions } from 'typeorm';
import { Constants } from './constants.js';
export type PageReq<T = any> = {
page?: { offset: number; limit: number };
@@ -258,7 +259,7 @@ export abstract class BaseService<T> {
export function checkUserProjectParam(userId: number, projectId: number) {
if (projectId != null ){
if( userId !==-1) {
if( userId !== Constants.enterpriseUserId) {
throw new ValidateException('userId projectId 错误');
}
return true

View File

@@ -120,4 +120,6 @@ export const Constants = {
message: '用户邮箱还未配置',
},
},
systemUserId: 0, // 系统级别userid固定为0
enterpriseUserId: -1 // 企业模式用户id固定为-1
};

View File

@@ -88,6 +88,10 @@ export class SysPrivateSettings extends BaseSettings {
pipelineMaxRunningCount?: number;
environmentVars?: string = '';
sms?: {
type?: string;
config?: any;

View File

@@ -9,8 +9,10 @@ import { cache, logger, mergeUtils, setGlobalProxy } from '@certd/basic';
import * as dns from 'node:dns';
import { BaseService, setAdminMode } from '../../../basic/index.js';
import { executorQueue } from '../../basic/service/executor-queue.js';
import { isComm } from '@certd/plus-core';
import { isComm, isPlus } 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();
@@ -153,7 +155,9 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
async reloadPublicSettings() {
const publicSetting = await this.getPublicSettings()
setAdminMode(publicSetting.adminMode)
if (isPlus()){
setAdminMode(publicSetting.adminMode )
}
}
async reloadPrivateSettings() {
@@ -173,6 +177,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) {

View File

@@ -7,8 +7,12 @@ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
export class AccessEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: 'key_id', comment: 'key_id', length: 100 })
keyId: string;
@Column({ name: 'user_id', comment: '用户id' })
userId: number; // 0为系统级别, -1为企业大于1为用户
@Column({ comment: '名称', length: 100 })
name: string;

View File

@@ -3,8 +3,8 @@ import { IAccessService } from '@certd/pipeline';
export class AccessGetter implements IAccessService {
userId: number;
projectId?: number;
getter: <T>(id: any, userId?: number, projectId?: number) => Promise<T>;
constructor(userId: number, projectId: number, getter: (id: any, userId: number, projectId?: number) => Promise<any>) {
getter: <T>(id: any, userId?: number, projectId?: number, ignorePermission?: boolean) => Promise<T>;
constructor(userId: number, projectId: number, getter: (id: any, userId: number, projectId?: number, ignorePermission?: boolean) => Promise<any>) {
this.userId = userId;
this.projectId = projectId;
this.getter = getter;

View File

@@ -5,6 +5,7 @@ import {AccessGetter, BaseService, PageReq, PermissionException, ValidateExcepti
import {AccessEntity} from '../entity/access.js';
import {AccessDefine, accessRegistry, newAccess} from '@certd/pipeline';
import {EncryptService} from './encrypt-service.js';
import { logger, utils } from '@certd/basic';
/**
* 授权
@@ -46,6 +47,7 @@ export class AccessService extends BaseService<AccessEntity> {
}
delete param._copyFrom
this.encryptSetting(param, oldEntity);
param.keyId = "ac_" + utils.id.simpleNanoId();
return await super.add(param);
}
@@ -117,6 +119,7 @@ export class AccessService extends BaseService<AccessEntity> {
throw new ValidateException('该授权配置不存在,请确认是否已被删除');
}
this.encryptSetting(param, oldEntity);
delete param.keyId
return await super.update(param);
}
@@ -215,4 +218,37 @@ export class AccessService extends BaseService<AccessEntity> {
});
}
/**
* 复制授权到其他项目
* @param accessId
* @param projectId
*/
async copyTo(accessId: number,projectId?: number) {
const access = await this.info(accessId);
if (access == null) {
throw new Error(`该授权配置不存在,请确认是否已被删除:id=${accessId}`);
}
const keyId = access.keyId;
//检查目标项目里是否已经有相同keyId的配置
const existAccess = await this.repository.findOne({
where: {
keyId,
projectId,
},
});
if (existAccess) {
logger.info(`目标项目已存在相同keyId的授权配置,跳过复制:keyId=${keyId}`);
return existAccess.id;
}
const newAccess = {
...access,
userId:-1,
id: undefined,
projectId,
}
await this.repository.save(newAccess);
return newAccess.id;
}
}

View File

@@ -6,6 +6,8 @@ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
export class AddonEntity {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: 'key_id', comment: 'key_id', length: 100 })
keyId: string;
@Column({ name: 'user_id', comment: '用户id' })
userId: number;
@Column({ comment: '名称', length: 100 })

View File

@@ -4,6 +4,7 @@ import { In, Repository } from "typeorm";
import { AddonDefine, BaseService, PageReq, ValidateException } from "../../../index.js";
import { addonRegistry } from "../api/index.js";
import { AddonEntity } from "../entity/addon.js";
import { utils } from "@certd/basic";
/**
* Addon
@@ -43,6 +44,7 @@ export class AddonService extends BaseService<AddonEntity> {
} else {
param.isSystem = false;
}
param.keyId = "ad_" + utils.id.simpleNanoId();
delete param._copyFrom;
return await super.add(param);
}
@@ -57,6 +59,7 @@ export class AddonService extends BaseService<AddonEntity> {
if (oldEntity == null) {
throw new ValidateException("该Addon配置不存在,请确认是否已被删除");
}
delete param.keyId
return await super.update(param);
}
@@ -67,6 +70,7 @@ export class AddonService extends BaseService<AddonEntity> {
}
return {
id: entity.id,
keyId: entity.keyId,
name: entity.name,
userId: entity.userId,
addonType: entity.addonType,
@@ -100,6 +104,7 @@ export class AddonService extends BaseService<AddonEntity> {
},
select: {
id: true,
keyId: true,
name: true,
addonType: true,
type: true,
@@ -132,6 +137,7 @@ export class AddonService extends BaseService<AddonEntity> {
const setting = JSON.parse(res.setting);
return {
id: res.id,
keyId: res.keyId,
addonType: res.addonType,
type: res.type,
name: res.name,

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
**Note:** Version bump only for package @certd/midway-flyway-js
## [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

View File

@@ -1,6 +1,6 @@
{
"name": "@certd/midway-flyway-js",
"version": "1.39.1",
"version": "1.39.7",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -46,5 +46,5 @@
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a"
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
}

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/plugin-cert
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
**Note:** Version bump only for package @certd/plugin-cert
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
**Note:** Version bump only for package @certd/plugin-cert
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
**Note:** Version bump only for package @certd/plugin-cert
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
**Note:** Version bump only for package @certd/plugin-cert
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
**Note:** Version bump only for package @certd/plugin-cert
## [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

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-cert",
"private": false,
"version": "1.39.1",
"version": "1.39.7",
"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.1",
"@certd/basic": "^1.39.1",
"@certd/pipeline": "^1.39.1",
"@certd/plugin-lib": "^1.39.1",
"@certd/acme-client": "^1.39.7",
"@certd/basic": "^1.39.7",
"@certd/pipeline": "^1.39.7",
"@certd/plugin-lib": "^1.39.7",
"psl": "^1.9.0",
"punycode.js": "^2.3.1"
},
@@ -38,5 +38,5 @@
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "3bb29abe32c311e1cf840f97d485d1147411992a"
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
}

View File

@@ -3,6 +3,35 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/plugin-lib
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
**Note:** Version bump only for package @certd/plugin-lib
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
**Note:** Version bump only for package @certd/plugin-lib
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
### Bug Fixes
* 修复阿里云证书订单翻页问题 ([6d43623](https://github.com/certd/certd/commit/6d43623f459a7594599e50a7ed89d67fcc775518))
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
**Note:** Version bump only for package @certd/plugin-lib
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
### Performance Improvements
* 查看证书增加证书详情显示,包括域名,过期时间,颁发机构,指纹等 ([0b9933d](https://github.com/certd/certd/commit/0b9933df1e8d1685d14271435a8a7488974cc47b))
* dns-provider 支持bind9 support bind9 ([76d12d6](https://github.com/certd/certd/commit/76d12d60624c0672fd3717a80a2cfef6845b14b8))
## [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

View File

@@ -1,7 +1,7 @@
{
"name": "@certd/plugin-lib",
"private": false,
"version": "1.39.1",
"version": "1.39.7",
"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.1",
"@certd/basic": "^1.39.1",
"@certd/pipeline": "^1.39.1",
"@certd/plus-core": "^1.39.1",
"@certd/acme-client": "^1.39.7",
"@certd/basic": "^1.39.7",
"@certd/pipeline": "^1.39.7",
"@certd/plus-core": "^1.39.7",
"@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": "3bb29abe32c311e1cf840f97d485d1147411992a"
"gitHead": "b0ccab41e17cb16c87bfc0ec3b052d5e8a5d8435"
}

View File

@@ -2,6 +2,7 @@ import fs from "fs";
import os from "os";
import path from "path";
import { CertificateInfo, crypto } from "@certd/acme-client";
import cryptoLib from "crypto";
import { ILogger } from "@certd/basic";
import dayjs from "dayjs";
import { uniq } from "lodash-es";
@@ -119,9 +120,28 @@ export class CertReader {
const detail = crypto.readCertificateInfo(crt.toString());
const effective = detail.notBefore;
const expires = detail.notAfter;
const fingerprints = CertReader.getFingerprintX509(crt);
// @ts-ignore
detail.fingerprints = fingerprints;
return { detail, effective, expires };
}
static getFingerprintX509(crt: string) {
try {
// 创建X509Certificate实例
const cert = new cryptoLib.X509Certificate(crt);
// 获取指纹
return {
fingerprint: cert.fingerprint,
fingerprint256: cert.fingerprint256,
fingerprint512: cert.fingerprint512,
};
} catch (error) {
console.error("处理证书失败:", error.message);
return null;
}
}
getAllDomains() {
const { detail } = this.getCrtDetail();
const domains = [];

View File

@@ -1,5 +1,5 @@
import { HttpClient, ILogger, utils } from "@certd/basic";
import { IAccess, IServiceGetter, PageRes, PageSearch, Registrable } from "@certd/pipeline";
import { IAccess, IAccessService, IServiceGetter, PageRes, PageSearch, Registrable } from "@certd/pipeline";
export type DnsProviderDefine = Registrable & {
accessType: string;
@@ -26,6 +26,7 @@ export type DnsProviderContext = {
utils: typeof utils;
domainParser: IDomainParser;
serviceGetter: IServiceGetter;
accessGetter?: IAccessService;
};
export type DomainRecord = {

View File

@@ -1,5 +1,5 @@
import { HttpClient, ILogger } from "@certd/basic";
import { PageRes, PageSearch } from "@certd/pipeline";
import { IAccessService, PageRes, PageSearch } from "@certd/pipeline";
import punycode from "punycode.js";
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, DomainRecord, IDnsProvider, RemoveRecordOptions } from "./api.js";
import { dnsProviderRegistry } from "./registry.js";
@@ -59,6 +59,11 @@ export async function createDnsProvider(opts: { dnsProviderType: string; context
if (dnsProviderDefine.deprecated) {
context.logger.warn(dnsProviderDefine.deprecated);
}
if (!context.accessGetter) {
const accessGetter: IAccessService = await context.serviceGetter.get("accessService");
context.accessGetter = accessGetter;
}
// @ts-ignore
const dnsProvider: IDnsProvider = new DnsProviderClass();
dnsProvider.setCtx(context);

View File

@@ -43,6 +43,7 @@ export function createRemoteSelectInputDefine(opts?: {
pager?: boolean;
component?: any;
value?: any;
pageSize?: number;
}) {
const title = opts?.title || "请选择";
const certDomainsInputKey = opts?.certDomainsInputKey || "certDomains";
@@ -54,13 +55,11 @@ export function createRemoteSelectInputDefine(opts?: {
const helper = opts?.helper || "请选择";
const search = opts?.search ?? false;
const pager = opts?.pager ?? false;
let mode = "tags";
if (opts.multi === false) {
mode = undefined;
} else {
mode = opts?.mode ?? "tags";
let mode = "default";
const multi = opts?.multi ?? true;
if (multi) {
mode = "tags";
}
const item = {
title,
component: {
@@ -72,6 +71,8 @@ export function createRemoteSelectInputDefine(opts?: {
action,
search,
pager,
multi,
pageSize: opts?.pageSize,
watches: [certDomainsInputKey, accessIdInputKey, ...watches],
...opts.component,
},

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
# 根据架构下载不同的文件

View File

@@ -3,6 +3,71 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.39.7](https://github.com/certd/certd/compare/v1.39.6...v1.39.7) (2026-03-25)
**Note:** Version bump only for package @certd/ui-client
## [1.39.6](https://github.com/certd/certd/compare/v1.39.5...v1.39.6) (2026-03-22)
### Bug Fixes
* 修复模版id不正确导致修改到错误的模版流水线bug ([b1ff163](https://github.com/certd/certd/commit/b1ff163a2828b205297408d5aed21cf1eff335e8))
* remote-select默认pageSize设置为50阿里云WAF不支持pageSize100 ([285532d](https://github.com/certd/certd/commit/285532d4318b90d0d7f8154f070274c0a0ec0269))
### Performance Improvements
* 企业模式下面增加个人数据迁移的引导 ([431afd6](https://github.com/certd/certd/commit/431afd618f547cecf9a29433f46d4367619e2ecf))
* 优化远程数据选择框,选择数据时不刷新闪烁 ([7f6a8bc](https://github.com/certd/certd/commit/7f6a8bc87e364685defe7f039264b2de064806c5))
* 支持复制粘贴任务步骤 ([acc2df2](https://github.com/certd/certd/commit/acc2df29def017fb8165f931b41ef95414966afc))
## [1.39.5](https://github.com/certd/certd/compare/v1.39.4...v1.39.5) (2026-03-18)
### Bug Fixes
* 修复修改分组报错的bug ([224db7d](https://github.com/certd/certd/commit/224db7da57dbdddf25bcac7faa0a29eb228c5a33))
### Performance Improvements
* passkey 支持Bitwarden ([29f44c6](https://github.com/certd/certd/commit/29f44c67c808bed9ff1c9d4884d39a1a62d043a7))
* passkey登录放到下方其他登录位置 ([1413e1a](https://github.com/certd/certd/commit/1413e1aff4aabcfd471716338c210fbcfd76c8f9))
## [1.39.4](https://github.com/certd/certd/compare/v1.39.3...v1.39.4) (2026-03-17)
### Bug Fixes
* 修复阿里云证书订单翻页问题 ([6d43623](https://github.com/certd/certd/commit/6d43623f459a7594599e50a7ed89d67fcc775518))
* 修复查看证书详情页面错位的bug ([7f37df4](https://github.com/certd/certd/commit/7f37df42274e657892d92e868ceac67e139f3bf2))
* 修复选择插件页面无法滚动的bug ([d8425bc](https://github.com/certd/certd/commit/d8425bc9c5ee81bb669706c6de6bad69d7c38d8e))
### Performance Improvements
* 优化passkey ([9e12412](https://github.com/certd/certd/commit/9e12412f5fa7800df1d7efaf62cd8fd5d79bb569))
## [1.39.3](https://github.com/certd/certd/compare/v1.39.2...v1.39.3) (2026-03-17)
### Bug Fixes
* 修复多选框只能单选的bug ([12700e1](https://github.com/certd/certd/commit/12700e1754319513ac02822ff1588d63420b964e))
## [1.39.2](https://github.com/certd/certd/compare/v1.39.1...v1.39.2) (2026-03-16)
### Bug Fixes
* 修复群晖测试时报addSecret undefine错误 ([5eb4aa3](https://github.com/certd/certd/commit/5eb4aa3a0eab9ffa729c8e813cbf973d9683cc13))
* 修复提示支付失败的bug ([12fed34](https://github.com/certd/certd/commit/12fed34e109f3254de664813954081a52513bd38))
* 修复修改项目名称后没有同步刷新的bug ([3abee72](https://github.com/certd/certd/commit/3abee72fee286864b665033b23b172ef0ea92d83))
* cname provider授权修改为sys级别 ([d01bfbe](https://github.com/certd/certd/commit/d01bfbec96a3a2109ec864953b0c9e8c1f95b97b))
### Performance Improvements
* 查看证书增加证书详情显示,包括域名,过期时间,颁发机构,指纹等 ([0b9933d](https://github.com/certd/certd/commit/0b9933df1e8d1685d14271435a8a7488974cc47b))
* 获取阿里证书订单id组件增加翻页功能突破50的上限 ([d79db3b](https://github.com/certd/certd/commit/d79db3bd3f0d5ad39664bb47ec3814d43ad93304))
* 优化阿里云连接超时时长为10秒支持配置环境变量 ([1588461](https://github.com/certd/certd/commit/1588461633bd275765daa96fc68320abb58d616d))
* 优化个人账户页面 ([e506116](https://github.com/certd/certd/commit/e50611666ef731a903d7bdd8eb62333b97e2cc5b))
* 支持批量转移流水线到其他项目 ([8a3841f](https://github.com/certd/certd/commit/8a3841f6382b53ce2343307fb035e74fa5383fef))
* 支持passkey登录 ([10b7644](https://github.com/certd/certd/commit/10b7644bb7ba5f82776537bc0c4f5eb95d5f8e4e))
* dns-provider 支持bind9 support bind9 ([76d12d6](https://github.com/certd/certd/commit/76d12d60624c0672fd3717a80a2cfef6845b14b8))
## [1.39.1](https://github.com/certd/certd/compare/v1.39.0...v1.39.1) (2026-03-09)
### Bug Fixes

View File

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

View File

@@ -19,6 +19,7 @@ defineOptions({
name: "ApiTest",
});
const fromType: any = inject("getFromType");
const getScope: any = inject("get:scope");
const getPluginType: any = inject("get:plugin:type", () => {
return "access";
@@ -55,6 +56,7 @@ const doTest = async () => {
action: props.action,
input,
record,
fromType,
},
{
onError(err: any) {

View File

@@ -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 ref="selectRef" class="remote-select-input" show-search :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">
@@ -44,6 +44,8 @@ defineOptions({
name: "RemoteSelect",
});
const selectRef = ref(null);
const VNodes = defineComponent({
props: {
vnodes: {
@@ -61,6 +63,8 @@ const props = defineProps<
watches?: string[];
search?: boolean;
pager?: boolean;
multi?: boolean;
pageSize?: number;
} & ComponentPropsType
>();
@@ -68,6 +72,18 @@ const emit = defineEmits<{
"update:value": any;
}>();
function updateValue(value: any) {
// if (props.multi !== false) {
// emit("update:value", value);
// } else {
// const last = value?.[value.length - 1];
// emit("update:value", last);
// selectRef.value.blur();
// }
emit("update:value", value);
}
const attrs = useAttrs();
const getCurrentPluginDefine: any = inject("getCurrentPluginDefine", () => {
@@ -88,7 +104,7 @@ const loading = ref(false);
const pagerRef: Ref = ref({
pageNo: 1,
total: 0,
pageSize: 100,
pageSize: props.pageSize || 50,
});
const getOptions = async () => {
if (loading.value) {
@@ -166,7 +182,7 @@ const getOptions = async () => {
pagerRef.value.pageNo = res.pageNo ?? 1;
}
if (res.pageSize != null) {
pagerRef.value.pageSize = res.pageSize ?? 100;
pagerRef.value.pageSize = res.pageSize ?? pageSize;
}
if (res.total != null) {
pagerRef.value.total = res.total ?? list.length;
@@ -219,9 +235,10 @@ watch(
const { form } = value;
const oldForm: any = oldValue?.form;
let changed = oldForm == null || optionsRef.value.length == 0;
debugger;
if (props.watches && props.watches.length > 0) {
for (const key of props.watches) {
if (oldForm && form[key] != oldForm[key]) {
if (oldForm && JSON.stringify(form[key]) != JSON.stringify(oldForm[key])) {
changed = true;
break;
}

View File

@@ -13,11 +13,12 @@ export type RequestHandleReq<T = any> = {
data?: any;
input: T;
record?: any;
fromType?: string; // sys、user
};
export async function doRequest(req: RequestHandleReq, opts: any = {}) {
const url = `/pi/handle/${req.type}`;
const { typeName, action, data, input, record } = req;
const { typeName, action, data, input, record, fromType } = req;
const res = await request({
url,
method: "post",
@@ -27,6 +28,7 @@ export async function doRequest(req: RequestHandleReq, opts: any = {}) {
data,
input,
record,
fromType,
},
...opts,
});

View File

@@ -18,7 +18,8 @@
</template>
<div class="rounded pl-3 pr-3 px-2 py-1 flex-center flex pointer items-center bg-accent h-10 button-text" title="当前项目">
<!-- <fs-icon icon="ion:apps" class="mr-1"></fs-icon> -->
当前项目{{ projectStore.currentProject?.name || "..." }}
<span class="hidden md:flex"> 当前项目</span>
<span class="text-ellipsis">{{ projectStore.currentProject?.name || "..." }}</span>
<fs-icon :icon="currentIcon" class="ml-5"></fs-icon>
</div>
</a-dropdown>
@@ -58,7 +59,7 @@ const currentIcon = computed(() => {
<style lang="less">
.project-selector {
&.button-text {
min-width: 150px;
min-width: 100px;
max-width: 250px;
overflow: hidden;
text-overflow: ellipsis;

View File

@@ -90,7 +90,7 @@
<div class="mt-10">
<div class="w-100 flex-col md:flex-row">
<span>{{ t("vip.site_id") }}</span>
<fs-copyable v-model="computedSiteId" class="mr-2"></fs-copyable>
<fs-copyable v-model="computedSiteId" class="mr-2 inline-flex"></fs-copyable>
<a @click="goBindAccount">{{ t("vip.not_effective") }}</a>
</div>
</div>

View File

@@ -1,6 +1,6 @@
<template>
<a-dropdown>
<div class="fs-user-info">{{ t("user.greeting") }}{{ userStore.getUserInfo?.nickName || userStore.getUserInfo?.username }}</div>
<div class="fs-user-info" @click="goUserProfile">{{ t("user.greeting") }}{{ userStore.getUserInfo?.nickName || userStore.getUserInfo?.username }}</div>
<template #overlay>
<a-menu>
<a-menu-item>

View File

@@ -48,6 +48,9 @@ const avatar = computed(() => {
async function handleLogout() {
await userStore.logout(true);
}
function goUserProfile() {
router.push("/certd/mine/user-profile");
}
const settingStore = useSettingStore();
@@ -90,7 +93,7 @@ const projectStore = useProjectStore();
</div>
</template>
<template #user-dropdown>
<UserDropdown :avatar="avatar" :menus="menus" :text="userStore.userInfo?.nickName || userStore.userInfo?.username" description="" tag-text="" @logout="handleLogout" />
<UserDropdown :avatar="avatar" :menus="menus" :text="userStore.userInfo?.nickName || userStore.userInfo?.username" description="" tag-text="" @logout="handleLogout" @user-profile="goUserProfile" />
</template>
<template #lock-screen>
<LockScreen :avatar @to-login="handleLogout" />

View File

@@ -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,11 @@ 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",
passkeyRegisterHelper: "Site domain change will invalidate passkey",
userInfo: "User Info",
securitySettingTip: "2FA Setting",
securitySetting: "2FA Setting",
};

View File

@@ -727,7 +727,7 @@ export default {
paymentSetting: "Payment Settings",
captchaSetting: "Captcha Setting",
pipelineSetting: "Pipeline Settings",
oauthSetting: "OAuth2 Settings",
oauthSetting: "Login Settings",
networkSetting: "Network Settings",
adminModeSetting: "Admin Mode Settings",
adminModeHelper: "enterprise mode : allow to create and manage pipelines, roles, users, etc.\n saas mode : only allow to create and manage pipelines",
@@ -769,6 +769,10 @@ export default {
oauthAutoRedirectHelper: "Whether to auto redirect to OAuth2 login when login (using the first enabled OAuth2 login type)",
oauthOnly: "OAuth2 Login Only",
oauthOnlyHelper: "Whether to only allow OAuth2 login, disable password login",
enablePasskey: "Enable Passkey Login",
passkeyHostnameNotSame: "Passkey hostname must be the same as the main domain",
passkeyEnabledHelper:
"1、Site must enable https \n2、Domain name must not change, otherwise the registered passkey will be invalid \n3、Domain name must be the same as the main domain, otherwise the registered passkey will be invalid",
email: {
templates: "Email Templates",
@@ -789,6 +793,10 @@ 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",
bindUrl: "Bind URL",
},
},
modal: {

View File

@@ -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: "为了您的账户安全,请立即修改密码",
@@ -88,8 +96,15 @@ export default {
nickName: "昵称",
phoneNumber: "手机号",
changePassword: "修改密码",
updateProfile: "修改个人信息",
updateProfile: "修改信息",
oauthLoginTitle: "其他登录方式",
oauthOnlyLoginTitle: "登录",
registerPasskey: "注册Passkey",
deviceName: "设备名称",
deviceNameHelper: "请输入当前设备名称,绑定多个时好做区分",
passkeyRegisterHelper: "1、站点域名变更会导致passkey失效;\n2、同一设备同一个用户绑定多次只有最后一次的有效之前绑定的会失效需要手动删除",
userInfo: "账号信息",
securitySettingTip: "2FA设置",
securitySetting: "2FA设置",
};

View File

@@ -780,7 +780,9 @@ export default {
oauthAutoRedirectHelper: "是否自动跳转第三方登录(使用第一个已启用的第三方登录类型)",
oauthOnly: "仅使用第三方登录",
oauthOnlyHelper: "是否仅使用第三方登录,关闭密码登录(注意:请务必在测试第三方登录功能正常后再开启,否则会导致无法登录)\n 如果无法登录,请访问 http://你的certd地址/#/login?oauthOnly=false 来临时关闭此模式",
enablePasskey: "启用Passkey登录",
passkeyHostnameNotSame: "当前域名与主绑定域名不同",
passkeyEnabledHelper: "1、站点必须启用https \n2、passkey的rpId以主绑定域名为准当前主域名:{0} \n3、站点域名不能变否则会导致已注册的passkey失效。",
email: {
templates: "邮件模板",
templateType: "模板类型",
@@ -800,6 +802,9 @@ export default {
reverseProxyHelper: "证书颁发机构ACME地址的反向代理在申请证书时自动使用",
reverseProxyPlaceholder: "http://le.px.handfree.work",
reverseProxyEmpty: "未配置反向代理",
environmentVars: "环境变量",
environmentVarsHelper: "配置运行时环境变量每行一个格式KEY=VALUE",
bindUrl: "绑定URL",
},
},
modal: {

View File

@@ -275,7 +275,7 @@ export const certdResources = [
meta: {
icon: "ion:person-outline",
auth: true,
isMenu: false,
isMenu: true,
},
},
],

View File

@@ -38,6 +38,7 @@ export type SysPublicSetting = {
passwordLoginEnabled?: boolean;
smsLoginEnabled?: boolean;
defaultLoginType?: string;
passkeyEnabled?: boolean;
selfServicePasswordRetrievalEnabled?: boolean;
limitUserPipelineCount?: number;
@@ -101,6 +102,8 @@ export type SysPrivateSetting = {
commonCnameEnabled?: boolean;
// 同一个用户同时最大运行流水线数量
pipelineMaxRunningCount?: number;
// 环境变量
environmentVars?: string;
sms?: {
type?: string;

View File

@@ -272,14 +272,27 @@ export const useSettingStore = defineStore({
},
async checkUrlBound() {
const userStore = useUserStore();
const settingStore = useSettingStore();
if (!userStore.isAdmin) {
return;
}
const bindUrl = this.installInfo.bindUrl;
const bindUrl2 = this.installInfo.bindUrl2;
if (!bindUrl) {
//绑定url
await this.doBindUrl("url");
} else {
//检查当前url 是否与绑定的url一致
const url = window.location.href;
if (!url.startsWith(bindUrl) && !url.startsWith(bindUrl2)) {
this.openBindUrlModal();
}
}
},
openBindUrlModal() {
const event: any = { ModalRef: null };
mitter.emit("getModal", event);
const Modal = event.ModalRef;
let modalRef: any = null;
const bindUrl = this.installInfo.bindUrl;
const bindUrl2 = this.installInfo.bindUrl2;
@@ -289,57 +302,47 @@ export const useSettingStore = defineStore({
modalRef.destroy();
}
};
if (!bindUrl) {
//绑定url
await this.doBindUrl("url");
} else {
//检查当前url 是否与绑定的url一致
const url = window.location.href;
if (!url.startsWith(bindUrl) && !url.startsWith(bindUrl2)) {
modalRef = Modal.warning({
title: "URL地址未绑定是否绑定此地址",
width: 500,
keyboard: false,
content: () => {
return (
<div class="p-4">
<div class="flex items-center justify-between">
<span>
1
<a-tag color="green">{bindUrl || "未占用"}</a-tag>
</span>
<a-button type="primary" onClick={() => doBindRequest("url")}>
1
</a-button>
</div>
<div class="flex items-center justify-between mt-3">
<span>
2
<a-tag color="green">{bindUrl2 || "未占用"}</a-tag>
</span>
<a-button type="primary" onClick={() => doBindRequest("url2")}>
2
</a-button>
</div>
</div>
);
},
onOk: async () => {
// await this.doBindUrl();
window.location.href = bindUrl;
},
okButtonProps: {
danger: true,
},
okText: "不,回到原来的地址",
cancelText: "不,回到原来的地址",
onCancel: () => {
window.location.href = bindUrl;
},
});
}
}
const modalRef: any = Modal.warning({
title: "URL地址未绑定是否绑定此地址",
width: 500,
keyboard: false,
content: () => {
return (
<div class="p-4">
<div class="flex items-center justify-between">
<span>
1
<a-tag color="green">{bindUrl || "未占用"}</a-tag>
</span>
<a-button type="primary" onClick={() => doBindRequest("url")}>
1
</a-button>
</div>
<div class="flex items-center justify-between mt-3">
<span>
2
<a-tag color="green">{bindUrl2 || "未占用"}</a-tag>
</span>
<a-button type="primary" onClick={() => doBindRequest("url2")}>
2
</a-button>
</div>
</div>
);
},
onOk: async () => {
// await this.doBindUrl();
window.location.href = bindUrl;
},
okButtonProps: {
danger: true,
},
okText: "不,回到原来的地址",
cancelText: "不,回到原来的地址",
onCancel: () => {
window.location.href = bindUrl;
},
});
},
async loadProductInfo() {
try {

View File

@@ -107,3 +107,41 @@ export async function OauthProviders() {
method: "post",
});
}
export async function generatePasskeyRegistrationOptions() {
return await request({
url: "/passkey/generateRegistration",
method: "post",
});
}
export async function verifyPasskeyRegistration(response: any, challenge: string) {
return await request({
url: "/passkey/verifyRegistration",
method: "post",
data: { response, challenge },
});
}
export async function generatePasskeyAuthenticationOptions() {
return await request({
url: "/passkey/generateAuthentication",
method: "post",
});
}
export async function loginByPasskey(form: { credential: any; challenge: string }) {
return await request({
url: "/loginByPasskey",
method: "post",
data: form,
});
}
export async function registerPasskey(form: { response: any; challenge: string }) {
return await request({
url: "/passkey/register",
method: "post",
data: form,
});
}

View File

@@ -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);

View File

@@ -118,3 +118,16 @@ span.fs-icon-svg {
}
}
button.ant-btn.ant-btn-default.isPlus{
color: #c5913f;
border: 1px solid #c5913f;
&:disabled {
cursor: not-allowed;
border-color: hsl(240 5.9% 90%);
color: rgba(50, 54, 57, 0.25);
background-color: rgba(50, 54, 57, 0.04);
box-shadow: none;
}
}

View File

@@ -21,8 +21,10 @@ div#app {
height: 100%;
}
pre.pre {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
pre{
&.pre,&.helper{
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji" !important;
}
}
h1,
@@ -110,10 +112,10 @@ h6 {
flex: 0;
}
.flex-col {
display: flex;
flex-direction: column;
}
// .flex-col {
// display: flex;
// flex-direction: column;
// }
.align-left {
text-align: left;
@@ -295,10 +297,13 @@ h6 {
}
.helper {
color: #aeaeae;
color: #8f8f8f;
font-size: 12px;
margin-top: 3px;
margin-bottom: 3px;
white-space: pre-wrap;
word-wrap: break-word;
word-break: break-word;
&.error {
color: #ff4d4f;

View File

@@ -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;
}

View File

@@ -67,7 +67,7 @@ const props = withDefaults(defineProps<Props>(), {
hoverDelay: 500,
});
const emit = defineEmits<{ logout: [] }>();
const emit = defineEmits<{ logout: []; userProfile: [] }>();
const { globalLockScreenShortcutKey, globalLogoutShortcutKey } = usePreferences();
const lockStore = useLockStore();
@@ -132,6 +132,11 @@ function handleSubmitLogout() {
logoutModalApi.close();
}
function handleUserProfile() {
emit("userProfile");
openPopover.value = false;
}
if (enableShortcutKey.value) {
const keys = useMagicKeys();
whenever(keys["Alt+KeyQ"]!, () => {
@@ -173,7 +178,7 @@ if (enableShortcutKey.value) {
</DropdownMenuTrigger>
<DropdownMenuContent class="mr-2 min-w-[240px] p-0 pb-1">
<div ref="refContent">
<DropdownMenuLabel class="flex items-center p-3">
<DropdownMenuLabel class="flex items-center p-3 pointer" @click="handleUserProfile">
<VbenAvatar :alt="text" :src="avatar" class="size-12" dot dot-class="bottom-0 right-1 border-2 size-4 bg-green-500" />
<div class="ml-2 w-full">
<div v-if="tagText || text || $slots.tagText" class="text-foreground mb-1 flex items-center text-sm font-medium">

View File

@@ -18,6 +18,8 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const { form, row } = req;
form.id = row.id;
form.type = props.type;
delete form.access;
delete form.keyId;
const res = await context.api.UpdateObj(form);
lastResRef.value = res;
return res;
@@ -30,6 +32,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const addRequest = async (req: AddReq) => {
const { form } = req;
form.type = props.type;
delete form.access;
const res = await context.api.AddObj(form);
lastResRef.value = res;
return res;
@@ -70,6 +73,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
width: "1050px",
},
},
rowHandle: {
width: 200,
},
@@ -89,6 +93,9 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}, // 点击行
};
},
remove: {
confirmMessage: "授权如果已经被使用,可能会导致流水线无法正常运行,请谨慎操作",
},
},
columns: {
id: {

View File

@@ -3,6 +3,7 @@ import { request } from "/src/api/service";
export function createAccessApi(from = "user") {
const apiPrefix = from === "sys" ? "/sys/access" : "/pi/access";
return {
from,
async GetList(query: any) {
if (query?.query) {
delete query.query.access;

View File

@@ -6,6 +6,7 @@ import SecretPlainGetter from "/@/views/certd/access/access-selector/access/secr
import { utils } from "/@/utils";
export function getCommonColumnDefine(crudExpose: any, typeRef: any, api: any) {
provide("getFromType", api.from);
provide("accessApi", api);
provide("get:plugin:type", () => {
return "access";

View File

@@ -15,6 +15,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const editRequest = async (req: EditReq) => {
const { form, row } = req;
form.id = row.id;
delete form.access;
const res = await api.UpdateObj(form);
return res;
};
@@ -25,6 +26,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const addRequest = async (req: AddReq) => {
const { form } = req;
delete form.access;
const res = await api.AddObj(form);
return res;
};

View File

@@ -16,6 +16,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const editRequest = async (req: EditReq) => {
const { form, row } = req;
form.id = row.id;
delete form.body;
const res = await api.UpdateObj(form);
return res;
};
@@ -26,6 +27,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const addRequest = async (req: AddReq) => {
const { form } = req;
delete form.body;
const res = await api.AddObj(form);
return res;
};

View File

@@ -55,3 +55,78 @@ export async function OauthBoundUrl(type: string) {
},
});
}
export async function GetPasskeys() {
return await request({
url: "/mine/passkey/list",
method: "POST",
});
}
export async function UnbindPasskey(id: number) {
return await request({
url: "/mine/passkey/unbind",
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: "/mine/passkey/generateRegistration",
method: "post",
});
}
export async function verifyPasskeyRegistration(response: any, challenge: string, deviceName: string) {
return await request({
url: "/mine/passkey/verifyRegistration",
method: "post",
data: { response, challenge, deviceName },
});
}
export async function registerPasskey(response: any, challenge: string, deviceName: string) {
return await request({
url: "/mine/passkey/register",
method: "post",
data: { response, challenge, deviceName },
});
}

View File

@@ -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,
};
}

View File

@@ -3,33 +3,133 @@
<template #header>
<div class="title">{{ t("certd.myInfo") }}</div>
</template>
<div class="p-10">
<a-descriptions title="" bordered :column="2">
<a-descriptions-item :label="t('authentication.username')">{{ userInfo.username }}</a-descriptions-item>
<a-descriptions-item :label="t('authentication.nickName')">{{ userInfo.nickName }}</a-descriptions-item>
<a-descriptions-item :label="t('authentication.avatar')">
<a-avatar v-if="userInfo.avatar" size="large" :src="userAvatar" style="background-color: #eee"> </a-avatar>
<a-avatar v-else size="large" style="background-color: #00b4f5">
{{ userInfo.username }}
</a-avatar>
</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 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">
<fs-icon :icon="item.icon" class="mr-2 text-blue-500 w-5 flex justify-center items-center" />
<span class="mr-2 w-36">{{ item.title }}</span>
<a-button v-if="item.bound" type="primary" danger @click="unbind(item.name)">解绑</a-button>
<a-button v-else type="primary" @click="bind(item.name)">绑定</a-button>
<div class="profile-container md:p-8">
<div class="profile-card md:rounded">
<div class="card-header">
<div class="header-bg-gradient"></div>
<div class="header-content">
<div class="avatar-wrapper">
<a-avatar v-if="userInfo.avatar" :size="100" :src="userAvatar" class="user-avatar"> </a-avatar>
<a-avatar v-else size="100" class="user-avatar default-avatar">
{{ userInfo.username }}
</a-avatar>
<!-- <div class="status-indicator"></div> -->
</div>
</template>
</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>
</a-descriptions-item>
</a-descriptions>
<div class="user-info">
<h2 class="user-name flex items-center">
{{ userInfo.nickName }}
<fs-values-format :model-value="userInfo.roleIds" :dict="roleDict" color="blue" />
</h2>
<div class="user-details">
<a-tag color="blue" class="detail-tag">
<span class="tag-icon">👤</span>
{{ userInfo.username }}
</a-tag>
<a-tag v-if="userInfo.email" color="green" class="detail-tag">
<span class="tag-icon">📧</span>
{{ userInfo.email }}
</a-tag>
<a-tag v-if="userInfo.mobile" color="purple" class="detail-tag">
<span class="tag-icon">📱</span>
{{ userInfo.mobile }}
</a-tag>
</div>
</div>
<div class="action-buttons gap-2">
<a-button type="primary" class="action-btn" @click="doUpdate">
{{ t("authentication.updateProfile") }}
</a-button>
<change-password-button :show-button="true" />
<a-button type="primary" class="action-btn" @click="goSecuritySetting">
{{ t("authentication.securitySettingTip") }}
</a-button>
</div>
</div>
</div>
</div>
<div class="flex flex-wrap">
<div v-if="settingStore.sysPublic.oauthEnabled && settingStore.isPlus" class="w-full md:w-1/2">
<div class="bindings-card md:rounded">
<div class="card-title">
<fs-icon icon="ion:link-outline" class="title-icon" />
<span>第三方账号绑定</span>
</div>
<div class="bindings-list">
<template v-for="item in computedOauthBounds" :key="item.name">
<div v-if="item.addonId" class="binding-item">
<div class="binding-icon">
<fs-icon :icon="item.icon" class="icon" />
</div>
<div class="binding-info">
<span class="binding-name">{{ item.title }}</span>
<span>
<a-tag v-if="item.bound" color="green" class="bound-tag1">已绑定</a-tag>
<a-tag v-else color="red" class="bound-tag1">未绑定</a-tag>
</span>
</div>
<a-button v-if="item.bound" type="primary" danger class="action-btn" @click="unbind(item.name)">
<template #icon><fs-icon icon="ion:unlink-outline" /></template>
解绑
</a-button>
<a-button v-else type="primary" class="action-btn" @click="bind(item.name)">
<template #icon><fs-icon icon="ion:link-outline" /></template>
绑定
</a-button>
</div>
</template>
<div v-if="computedOauthBounds.length === 0" class="empty-text">暂无可用的第三方账号绑定</div>
</div>
</div>
</div>
<div v-if="settingStore.sysPublic.passkeyEnabled && settingStore.isPlus" class="w-full md:w-1/2">
<div class="passkey-card md:rounded">
<div class="card-title">
<fs-icon icon="ion:finger-print" class="title-icon" />
<span>Passkey 安全密钥</span>
</div>
<div class="passkey-list">
<div v-for="passkey in passkeys" :key="passkey.id" class="passkey-item">
<div class="passkey-icon hidden md:flex">
<fs-icon icon="ion:finger-print" class="icon" />
</div>
<div class="passkey-info">
<div class="passkey-name">{{ passkey.deviceName }}</div>
<div class="passkey-meta flex items-center">
<span class="meta-item flex items-center">
<fs-icon icon="ion:calendar-outline" class="meta-icon" />
{{ formatDate(passkey.registeredAt) }}
</span>
<span class="meta-item flex items-center">
<fs-icon icon="ion:time-outline" class="meta-icon" />
最近使用<fs-time-humanize :model-value="passkey.updateTime" />
</span>
</div>
</div>
<a-button type="primary" danger class="remove-btn" @click="unbindPasskey(passkey.id)">
<template #icon><fs-icon icon="ion:trash-outline" /></template>
移除
</a-button>
</div>
</div>
<div v-if="passkeys.length === 0" class="empty-state">
<fs-icon icon="ion:finger-print" class="empty-icon" />
<p class="empty-text">暂无Passkey</p>
</div>
<div v-if="!passkeySupported" class="warning-box">
<fs-icon icon="ion:warning-outline" class="warning-icon" />
<span>{{ t("authentication.passkeyNotSupported") }}</span>
</div>
<a-button v-if="passkeySupported" type="primary" class="add-btn" @click="registerPasskey">
<template #icon><fs-icon icon="ion:add-circle-outline" /></template>
注册新的Passkey
</a-button>
<pre class="helper pre">{{ t("authentication.passkeyRegisterHelper") }}</pre>
</div>
</div>
</div>
</div>
</fs-page>
</template>
@@ -40,9 +140,13 @@ 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, notification } from "ant-design-vue";
import { useSettingStore } from "/@/store/settings";
import { isEmpty } from "lodash-es";
import { dict } from "@fast-crud/fast-crud";
import dayjs from "dayjs";
import { useRouter } from "vue-router";
const { t } = useI18n();
@@ -53,11 +157,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({
@@ -67,12 +180,19 @@ function doUpdate() {
});
}
const router = useRouter();
function goSecuritySetting() {
router.push("/certd/mine/security");
}
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 +222,136 @@ 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 excludeCredentials = passkeys.value.map(item => ({
// id: new TextEncoder().encode(item.passkeyId),
// type: "public-key",
// }));
console.log("passkey register options:", options, JSON.stringify(options));
const 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: excludeCredentials,
// extensions: options.extensions,
// authenticatorSelection: options.authenticatorSelection,
// hints: options.hints,
user: {
id: new TextEncoder().encode(options.userId + ""),
name: userInfo.value.username + "@" + deviceName,
displayName: deviceName,
},
// 关键配置在这里 👇
authenticatorSelection: {
residentKey: "required", // 或 "preferred",请求创建可发现凭证
requireResidentKey: true, // 为兼容旧浏览器,设置与 residentKey 相同的值
userVerification: "preferred", // 用户验证策略
},
};
console.log("passkey register publicKey:", publicKey, JSON.stringify(publicKey));
const credential = await (navigator.credentials as any).create({
publicKey,
});
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),
},
};
console.log("credential", credential, response);
const verifyRes: any = await api.verifyPasskeyRegistration(response, options.challenge, deviceName);
console.log("verifyRes:", verifyRes, JSON.stringify(verifyRes));
await loadPasskeys();
} catch (e: any) {
console.error("Passkey注册失败:", e);
notification.error({ message: "错误", description: e.message || "Passkey注册失败" });
}
}
const formatDate = (dateString: string) => {
if (!dateString) return "";
return dayjs(dateString).format("YYYY-MM-DD HH:mm:ss");
};
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 +367,514 @@ onMounted(async () => {
await getUserInfo();
await loadOauthBounds();
await loadOauthProviders();
await loadPasskeys();
checkPasskeySupport();
});
</script>
<style lang="less">
.page-user-profile {
:deep(.ant-descriptions-item-label) {
font-weight: 500;
color: rgba(0, 0, 0, 0.85);
}
}
.dark {
.page-user-profile {
:deep(.ant-descriptions-item-label) {
color: rgba(255, 255, 255, 0.85);
}
}
.profile-container {
.profile-card,
.bindings-card,
.passkey-card {
background: linear-gradient(135deg, #1f1f1f 0%, #2d2d2d 100%);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
&:hover {
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
border-color: rgba(255, 255, 255, 0.2);
}
}
.card-header {
.header-bg-gradient {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
opacity: 0.15;
}
}
.header-content {
.user-avatar {
border-color: #3b3b3b;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
.user-name {
color: #e5e5e5;
}
.detail-tag {
background: #3b3b3b;
color: #e5e5e5;
.tag-icon {
color: #e5e5e5;
}
}
}
.bindings-list {
.binding-item {
background: #2d2d2d;
border-color: rgba(255, 255, 255, 0.1);
color: #e5e5e5;
&:hover {
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
}
.binding-name {
color: #e5e5e5;
}
.binding-status {
&.bound {
background: #1a3a2f;
color: #4caf50;
}
&.unbound {
background: #3a352a;
color: #ffb300;
}
}
}
}
.passkey-list {
.passkey-item {
background: #2d2d2d;
border-color: rgba(255, 255, 255, 0.1);
color: #e5e5e5;
&:hover {
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
}
.passkey-name {
color: #e5e5e5;
}
.passkey-meta {
.meta-item {
color: #b0b0b0;
}
.meta-icon {
color: #888888;
}
}
}
}
.empty-state {
color: #b0b0b0;
.empty-icon {
opacity: 0.6;
}
}
.warning-box {
background: #3a2a2a;
border-color: #5a3a3a;
color: #e5e5e5;
.warning-icon {
color: #ef5350;
}
}
.helper {
background: #2d2d2d;
border-color: rgba(255, 255, 255, 0.1);
color: #b0b0b0;
}
}
}
.profile-container {
display: flex;
flex-direction: column;
gap: 10px;
// max-width: 1000px;
.profile-card,
.bindings-card,
.passkey-card {
background: #fff;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
overflow: hidden;
transition: all 0.3s ease;
margin: 5px;
}
.bindings-card,
.passkey-card {
padding: 18px;
}
.profile-card:hover,
.bindings-card:hover,
.passkey-card:hover {
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12);
transform: translateY(-2px);
}
.card-header {
position: relative;
padding: 40px 30px;
}
.header-bg-gradient {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 100%;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
opacity: 0.08;
}
.header-content {
position: relative;
z-index: 1;
display: flex;
align-items: center;
gap: 30px;
}
.avatar-wrapper {
position: relative;
flex-shrink: 0;
}
.user-avatar {
border: 4px solid #ffffff;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.status-indicator {
position: absolute;
bottom: 8px;
right: 8px;
width: 16px;
height: 16px;
background: #52c41a;
border: 3px solid #ffffff;
border-radius: 50%;
}
.user-info {
flex: 1;
min-width: 0;
}
.user-name {
margin: 0 0 12px 0;
font-size: 24px;
font-weight: 600;
color: #2c3e50;
display: flex;
align-items: center;
gap: 10px;
}
.user-details {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.detail-tag {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: 20px;
font-size: 13px;
}
.tag-icon {
font-size: 14px;
}
.action-buttons {
display: flex;
gap: 10px;
align-items: center;
}
.action-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 10px 20px;
font-weight: 500;
border-radius: 8px;
transition: all 0.3s ease;
}
.action-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.card-title {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 2px solid #f0f0f0;
}
.title-icon {
font-size: 20px;
color: #667eea;
}
.bindings-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.binding-item {
display: flex;
align-items: center;
gap: 16px;
padding: 16px;
background: #ffffff;
border-radius: 12px;
border: 1px solid #f0f0f0;
transition: all 0.3s ease;
}
.binding-item:hover {
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1);
}
.binding-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #ebefff 0%, #e5d4ff 100%);
border-radius: 10px;
}
.binding-icon .icon {
font-size: 20px;
color: #ffffff;
}
.binding-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
.binding-name {
font-size: 16px;
font-weight: 500;
color: #2c3e50;
}
.binding-status {
font-size: 12px;
padding: 2px 8px;
border-radius: 4px;
font-weight: 500;
}
.binding-status.bound {
background: #e6fffa;
color: #38a169;
}
.binding-status.unbound {
background: #fffaf0;
color: #d69e2e;
}
.passkey-list {
display: flex;
flex-direction: column;
gap: 12px;
margin-bottom: 20px;
}
.passkey-item {
display: flex;
align-items: center;
gap: 16px;
padding: 16px;
background: #ffffff;
border-radius: 12px;
border: 1px solid #f0f0f0;
transition: all 0.3s ease;
}
.passkey-item:hover {
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1);
}
.passkey-icon {
width: 48px;
height: 48px;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
border-radius: 12px;
}
.passkey-icon .icon {
font-size: 24px;
color: #ffffff;
}
.passkey-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 6px;
}
.passkey-name {
font-size: 16px;
font-weight: 600;
color: #2c3e50;
}
.passkey-meta {
display: flex;
flex-wrap: wrap;
gap: 4px;
font-size: 12px;
}
.meta-item {
display: flex;
align-items: center;
gap: 4px;
color: #6b7280;
}
.meta-icon {
font-size: 14px;
color: #9ca3af;
}
.remove-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
border-radius: 8px;
transition: all 0.3s ease;
}
.remove-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.2);
}
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px 10px;
color: #9ca3af;
}
.empty-icon {
font-size: 48px;
margin-bottom: 12px;
opacity: 0.5;
}
.empty-text {
margin: 0;
font-size: 14px;
}
.warning-box {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 16px;
background: #fff7ed;
border: 1px solid #fed7d7;
border-radius: 8px;
margin-bottom: 16px;
}
.warning-icon {
font-size: 18px;
color: #f56565;
}
.add-btn {
display: flex;
align-items: center;
gap: 6px;
padding: 10px 20px;
border-radius: 8px;
transition: all 0.3s ease;
}
.add-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.helper {
background: #f8f9fa;
padding: 12px 16px;
border-radius: 8px;
font-size: 13px;
color: #6b7280;
border: 1px solid #e5e7eb;
margin-top: 16px;
}
@media (max-width: 768px) {
.header-content {
flex-direction: column;
text-align: center;
}
.user-details {
justify-content: center;
}
.action-buttons {
justify-content: center;
width: 100%;
}
}
}
</style>

View File

@@ -11,6 +11,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const editRequest = async (req: EditReq) => {
const { form, row } = req;
form.id = row.id;
delete form.body;
const res = await api.UpdateObj(form);
return res;
};
@@ -21,6 +22,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const addRequest = async (req: AddReq) => {
const { form } = req;
delete form.body;
const res = await api.AddObj(form);
return res;
};

View File

@@ -14,6 +14,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const editRequest = async (req: EditReq) => {
const { form, row } = req;
form.id = row.id;
delete form.body;
const res = await context.api.UpdateObj(form);
lastResRef.value = res;
return res;
@@ -25,6 +26,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const addRequest = async (req: AddReq) => {
const { form } = req;
delete form.body;
const res = await context.api.AddObj(form);
lastResRef.value = res;
return res;

View File

@@ -50,4 +50,12 @@ export const openkeyApi = {
data: { id },
});
},
async GetSecret(id: number) {
return await request({
url: apiPrefix + "/getSecret",
method: "post",
data: { id },
});
},
};

Some files were not shown because too many files have changed in this diff Show More