mirror of
https://github.com/certd/certd.git
synced 2026-10-14 09:55:57 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b12f7c54d4 | ||
|
|
b055bbecfc | ||
|
|
a52a6ce8f6 | ||
|
|
fcbf1045fe | ||
|
|
f5cfe054b7 | ||
|
|
5ded5ff2b6 | ||
|
|
8d31346cd3 | ||
|
|
44b41b6a08 | ||
|
|
4420472f8f | ||
|
|
c4497a7018 | ||
|
|
03f1a7bd0e | ||
|
|
97d2b50074 | ||
|
|
2d0b85b305 | ||
|
|
28052c9387 | ||
|
|
c4277350bc | ||
|
|
97160373d6 | ||
|
|
0ce24618a1 | ||
|
|
f60e63fbaf | ||
|
|
2735feb330 | ||
|
|
b6381d79fc | ||
|
|
241b2e5584 | ||
|
|
bb1f232f41 | ||
|
|
f82d3a5fcc | ||
|
|
bbeaf617d1 | ||
|
|
ea7d14c289 | ||
|
|
3272a68ff6 | ||
|
|
b5715d99ff | ||
|
|
3f5ba14fde | ||
|
|
07f0d3b4d6 | ||
|
|
3dbbe72fc1 | ||
|
|
7f006d1359 | ||
|
|
d30f1c4785 | ||
|
|
96e0122b93 | ||
|
|
7786a87b21 | ||
|
|
34476ba14f | ||
|
|
4b942385a8 | ||
|
|
14809127d6 | ||
|
|
bd8b251d6e | ||
|
|
5563abb13a | ||
|
|
51e83e4c38 | ||
|
|
2b1a5296fe | ||
|
|
bded368cbb | ||
|
|
6a649aaca0 | ||
|
|
e971145686 | ||
|
|
bbca449f94 | ||
|
|
e6852c2715 | ||
|
|
47cb523307 | ||
|
|
cbf46b5c48 | ||
|
|
7ed3105d97 | ||
|
|
61b945c08f | ||
|
|
cc60cd4cd1 | ||
|
|
dade599acb | ||
|
|
1070417a36 | ||
|
|
81f676f1ca | ||
|
|
0cf0608d3f | ||
|
|
dac94b64f0 | ||
|
|
8c88092235 | ||
|
|
2a2e963914 | ||
|
|
1b882a2ca4 | ||
|
|
bd9433478a | ||
|
|
32ade8e4a3 | ||
|
|
f2bcdeeb33 | ||
|
|
4099af7f5d | ||
|
|
67f3daae62 | ||
|
|
c72fa68e4c | ||
|
|
1f5cda452c | ||
|
|
5a19d2513d | ||
|
|
c0f8145615 | ||
|
|
2ad14b82fa | ||
|
|
c8bdcdc778 | ||
|
|
15d2bd8484 | ||
|
|
7d4311115c | ||
|
|
3efa0582ef | ||
|
|
7624babc99 | ||
|
|
6cf8f350f5 | ||
|
|
0e08555220 | ||
|
|
fe738fa7d4 | ||
|
|
2439f4ab02 | ||
|
|
75b2b146ce | ||
|
|
1f3c096517 | ||
|
|
d264388c00 | ||
|
|
b8eaeeaa64 | ||
|
|
dba1a486cb | ||
|
|
5408ae6ad8 | ||
|
|
f5430a8925 | ||
|
|
9cc21204b1 | ||
|
|
f911e06a69 | ||
|
|
4a6eab0ce6 | ||
|
|
1d90fadd0c | ||
|
|
1b1a5ec94d | ||
|
|
d67379f297 | ||
|
|
249b98df66 | ||
|
|
aebbb5cc7d | ||
|
|
4f020aa8a5 | ||
|
|
02426c507a | ||
|
|
ffb7ae79eb | ||
|
|
f58f2f3cd6 | ||
|
|
794abc3d7a | ||
|
|
da406014ad | ||
|
|
9842a24a50 | ||
|
|
fb21ec04a6 | ||
|
|
15b74207dd | ||
|
|
6055e9b6ba | ||
|
|
df3e0814c4 | ||
|
|
81f660dc49 | ||
|
|
f56e2a07c6 | ||
|
|
a2fce6cf0b | ||
|
|
ccb5356bfa | ||
|
|
f44084dfd2 | ||
|
|
948d76f98f | ||
|
|
6606f32678 | ||
|
|
6af12d49fc | ||
|
|
9decb49d04 | ||
|
|
69466c1c41 | ||
|
|
adf5163a79 |
@@ -92,6 +92,8 @@ jobs:
|
||||
tags: |
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:latest
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:${{steps.get_certd_version.outputs.result}}
|
||||
greper/certd:latest
|
||||
greper/certd:${{steps.get_certd_version.outputs.result}}
|
||||
ghcr.io/${{ github.repository }}:latest
|
||||
@@ -111,8 +113,8 @@ jobs:
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}-slim
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:slim
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:${{steps.get_certd_version.outputs.result}}-slim
|
||||
certd/certd:slim
|
||||
certd/certd:${{steps.get_certd_version.outputs.result}}-slim
|
||||
greper/certd:slim
|
||||
greper/certd:${{steps.get_certd_version.outputs.result}}-slim
|
||||
ghcr.io/${{ github.repository }}:slim
|
||||
ghcr.io/${{ github.repository }}:${{steps.get_certd_version.outputs.result}}-slim
|
||||
|
||||
@@ -129,22 +131,26 @@ jobs:
|
||||
registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:armv7
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
certd/certd:armv7
|
||||
certd/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
greper/certd:armv7
|
||||
greper/certd:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
ghcr.io/${{ github.repository }}:armv7
|
||||
ghcr.io/${{ github.repository }}:${{steps.get_certd_version.outputs.result}}-armv7
|
||||
|
||||
# - name: Build agent
|
||||
# uses: docker/build-push-action@v6
|
||||
# with:
|
||||
# platforms: linux/amd64,linux/arm64
|
||||
# push: true
|
||||
# context: ./packages/ui/agent/
|
||||
# tags: |
|
||||
# registry.cn-shenzhen.aliyuncs.com/handsfree/certd-agent:latest
|
||||
# registry.cn-shenzhen.aliyuncs.com/handsfree/certd-agent:${{steps.get_certd_version.outputs.result}}
|
||||
# greper/certd-agent:latest
|
||||
# greper/certd-agent:${{steps.get_certd_version.outputs.result}}
|
||||
- name: Login to Certd Docker Hub account
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.dockerhub_username2 }}
|
||||
password: ${{ secrets.dockerhub_password2 }}
|
||||
|
||||
- name: Publish to certd Docker Hub repository
|
||||
run: |
|
||||
docker buildx imagetools create --tag certd/certd:latest greper/certd:latest
|
||||
docker buildx imagetools create --tag certd/certd:${{ steps.get_certd_version.outputs.result }} greper/certd:${{ steps.get_certd_version.outputs.result }}
|
||||
docker buildx imagetools create --tag certd/certd:slim greper/certd:slim
|
||||
docker buildx imagetools create --tag certd/certd:${{ steps.get_certd_version.outputs.result }}-slim greper/certd:${{ steps.get_certd_version.outputs.result }}-slim
|
||||
docker buildx imagetools create --tag certd/certd:armv7 greper/certd:armv7
|
||||
docker buildx imagetools create --tag certd/certd:${{ steps.get_certd_version.outputs.result }}-armv7 greper/certd:${{ steps.get_certd_version.outputs.result }}-armv7
|
||||
|
||||
- name: deploy-certd-doc
|
||||
uses: tyrrrz/action-http-request@prime
|
||||
with:
|
||||
|
||||
@@ -3,7 +3,7 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "版本号(如 v1.42.5)"
|
||||
description: "版本号(如 1.42.5)"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
@@ -40,8 +40,8 @@ jobs:
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.dockerhub_username }}
|
||||
password: ${{ secrets.dockerhub_password }}
|
||||
username: ${{ secrets.dockerhub_username2 }}
|
||||
password: ${{ secrets.dockerhub_password2 }}
|
||||
|
||||
# stable 镜像:叠加 ENV 层
|
||||
- name: Build and push stable images
|
||||
@@ -50,16 +50,14 @@ jobs:
|
||||
ENV certd_release_mode=stable" > Dockerfile.stable
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--provenance=false \
|
||||
--sbom=false \
|
||||
--push \
|
||||
-f Dockerfile.stable \
|
||||
-t registry.cn-shenzhen.aliyuncs.com/handsfree/certd:stable \
|
||||
-t registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{ inputs.version }}-stable \
|
||||
-t registry.cn-shenzhen.aliyuncs.com/certd/certd:stable \
|
||||
-t registry.cn-shenzhen.aliyuncs.com/certd/certd:${{ inputs.version }}-stable \
|
||||
-t certd/certd:stable \
|
||||
-t certd/certd:${{ inputs.version }}-stable \
|
||||
-t ghcr.io/${{ github.repository }}:stable \
|
||||
-t ghcr.io/${{ github.repository }}:${{ inputs.version }}-stable \
|
||||
.
|
||||
|
||||
# slim-stable 镜像
|
||||
@@ -69,20 +67,18 @@ jobs:
|
||||
ENV certd_release_mode=stable" > Dockerfile.slim-stable
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--provenance=false \
|
||||
--sbom=false \
|
||||
--push \
|
||||
-f Dockerfile.slim-stable \
|
||||
-t registry.cn-shenzhen.aliyuncs.com/handsfree/certd:slim-stable \
|
||||
-t registry.cn-shenzhen.aliyuncs.com/handsfree/certd:${{ inputs.version }}-slim-stable \
|
||||
-t registry.cn-shenzhen.aliyuncs.com/certd/certd:slim-stable \
|
||||
-t registry.cn-shenzhen.aliyuncs.com/certd/certd:${{ inputs.version }}-slim-stable \
|
||||
-t certd/certd:slim-stable \
|
||||
-t certd/certd:${{ inputs.version }}-slim-stable \
|
||||
-t ghcr.io/${{ github.repository }}:slim-stable \
|
||||
-t ghcr.io/${{ github.repository }}:${{ inputs.version }}-slim-stable \
|
||||
.
|
||||
|
||||
- name: Set AtomGit release as stable
|
||||
run: |
|
||||
export ATOMGIT_TOKEN=${{ secrets.ATOMGIT_TOKEN }}
|
||||
export VERSION=${{ inputs.version }}
|
||||
npm run set-release-stable
|
||||
node ./scripts/set-release-stable-fetch.js
|
||||
|
||||
+2
-1
@@ -42,4 +42,5 @@ output/
|
||||
.uploads/
|
||||
.certd-plugin-history/
|
||||
.tmp
|
||||
.plugin-dev/
|
||||
.plugin-dev/
|
||||
.tmp_chunks/
|
||||
@@ -392,6 +392,66 @@ export class OAuthDemoAccess extends BaseAccess {
|
||||
}
|
||||
```
|
||||
|
||||
## 字段动态显隐(mergeScript)
|
||||
|
||||
多种鉴权方式共用一个授权时,用 `@AccessInput` 上的 `mergeScript` 按所选方式显隐字段:
|
||||
|
||||
```typescript
|
||||
@AccessInput({
|
||||
title: '鉴权方式',
|
||||
value: 'apikey',
|
||||
component: {
|
||||
name: 'a-select',
|
||||
vModel: 'value',
|
||||
options: [
|
||||
{ label: 'AccessKey签名', value: 'aksk' },
|
||||
{ label: 'ApiKey', value: 'apikey' },
|
||||
],
|
||||
},
|
||||
required: true,
|
||||
})
|
||||
authType = 'apikey';
|
||||
|
||||
@AccessInput({
|
||||
title: 'accessKeyId',
|
||||
component: { name: 'a-input', vModel: 'value' },
|
||||
required: true,
|
||||
mergeScript: `
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return !form.access.authType || form.access.authType === 'aksk';
|
||||
})
|
||||
}
|
||||
`,
|
||||
})
|
||||
accessKeyId = '';
|
||||
```
|
||||
|
||||
要点:
|
||||
|
||||
- `mergeScript` 返回的对象会深合并进本字段定义,可动态化的属性有 `show`、`required`、`helper`、
|
||||
`component` 下的属性(`options`、`placeholder` 等)以及 `component.on` 事件。
|
||||
- **动态判断必须写在 `ctx.compute(({form})=>{ ... })` 回调里**。`mergeScript` 本身在插件类
|
||||
定义阶段就被拼成字符串,那时表单还没打开,用参数在服务端判断字段值没有意义。
|
||||
- 授权表单字段值在 `form.access.xxx`(任务表单才是 `form.xxx`)。
|
||||
- 历史授权没有新字段,条件里要用 `!form.access.authType || ...` 兜底走旧逻辑,保证老授权还能编辑保存。
|
||||
- 联动其他字段用 `component.on` 事件回调,回调参数是 `{ form }`:
|
||||
|
||||
```typescript
|
||||
mergeScript: `
|
||||
return {
|
||||
component: {
|
||||
on: {
|
||||
selectedChange: (scope) => {
|
||||
const form = scope.form;
|
||||
form.acmeAccountAccessId = null;
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
`,
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **插件命名**:插件名称应简洁明了,反映其功能。
|
||||
|
||||
@@ -9,7 +9,7 @@ description: 用于通过 Certd API 开发、修改、读取、保存和恢复
|
||||
|
||||
本 Skill 使用 Certd HTTP API,不使用 WebSocket,也不依赖浏览器传递代码草稿。
|
||||
|
||||
- 前端只生成包含需求、API 地址和认证 Token 的启动提示词。
|
||||
- 前端只生成包含需求、插件作者、API 地址和认证 Token 的启动提示词。
|
||||
- Agent 直接调用 Certd API 读取和保存插件 YAML。
|
||||
- Agent 的进度、日志和代码修改在 Codex/Trae 中查看。
|
||||
- 插件开发临时文件和修改历史统一保存在 Agent 工作区的 `.plugin-dev/` 下,不保存到浏览器或 Certd 后端。
|
||||
@@ -43,15 +43,13 @@ Certd 插件按来源分为三类:
|
||||
|
||||
## API 认证
|
||||
|
||||
提示词会提供 Certd API 地址和仅限 AI 插件开发接口的受限 Token。调用 API 时使用:
|
||||
提示词会提供 Certd API 地址和仅限 AI 插件开发接口的受限的短时效的 Token。调用 API 时使用:
|
||||
|
||||
```http
|
||||
Authorization: <token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
不要把 Token 写入代码、历史摘要、日志、提交信息或插件 YAML。
|
||||
|
||||
所有 Certd API 请求统一使用 Node.js 的 `fetch`。不要使用 PowerShell 的 `Invoke-RestMethod`、`Invoke-WebRequest` 或 .NET HTTP 客户端发送插件 YAML/JSON;它们在 Windows 上可能造成中文乱码或使完整 YAML 导入请求长时间无响应。
|
||||
|
||||
Node 请求须直接读取 UTF-8 文件或在 Node 内构造 JSON,并使用 `JSON.stringify`:
|
||||
@@ -118,6 +116,7 @@ const response = await fetch(`${apiBase}/scoped/sys/ai/plugin/find`, {
|
||||
- 修改后保存修改摘要。
|
||||
- 恢复前再次备份当前版本。
|
||||
- 不上传历史文件,不保存 Token、证书、私钥或真实授权值。
|
||||
- 修改完一版后立即提交更新到certd,但不要发布到市场。
|
||||
|
||||
详细格式见 `references/local-history.md`。
|
||||
|
||||
@@ -125,6 +124,7 @@ const response = await fetch(`${apiBase}/scoped/sys/ai/plugin/find`, {
|
||||
|
||||
插件始终以完整 YAML 传递和保存,脚本源码放在顶层 `content` 字段。
|
||||
|
||||
- 新插件的 `author` 必须使用启动提示词中给出的插件作者,不要自行编造作者名;修改已有插件时保留原 `author`,不要改成其他作者。
|
||||
- 统一使用 `await _ctx.import(...)` 引用模块。
|
||||
- `"/@/..."` 表示以绝对路径引用 `server/src/` 下的模块。
|
||||
- 最后返回继承目标基类的 class。
|
||||
@@ -135,6 +135,28 @@ const response = await fetch(`${apiBase}/scoped/sys/ai/plugin/find`, {
|
||||
|
||||
需要字段格式时读取 `references/online-yaml-format.md`。
|
||||
需要组件示例时读取 `references/component-examples.md`。
|
||||
需要按其他字段动态显隐/必填/联动时读取 `references/merge-script.md`。
|
||||
|
||||
### 字段动态显隐(mergeScript)
|
||||
|
||||
字段定义支持 `mergeScript`:脚本 `return` 的对象会合并进本字段,用来做「选了 A 才显示 B」这类联动。
|
||||
|
||||
```yaml
|
||||
mergeScript: |2-
|
||||
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return form.access.authType === 'apikey';
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
- 动态逻辑必须写在 `ctx.compute(({form})=>{ ... })` 回调里;`mergeScript` 本身在插件类定义阶段就
|
||||
被拼成字符串,那时表单还没打开,用参数在 node 侧判断字段值没有任何意义。
|
||||
- 授权表单字段值在 `form.access.xxx`,任务表单字段值在 `form.xxx`。
|
||||
- 新增可选字段时要给历史数据兜底,例如 `!form.access.authType || form.access.authType === 'aksk'`。
|
||||
|
||||
完整用法(能力、可动态化的属性、事件联动、常见错误、验证方式)见 `references/merge-script.md`。
|
||||
|
||||
## 示例插件
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: DemoAccess
|
||||
icon: logos:airflow-icon
|
||||
title: Demo-授权插件示例 # 模块-插件名
|
||||
title: Demo-授权插件示例 # 格式:平台-功能 ,例如: 网宿-授权 ; 阿里云-授权 ; 天翼云-授权
|
||||
group: null
|
||||
desc: 这只是一个示例
|
||||
version: 1.0.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: DemoDeploy
|
||||
icon: logos:amp-icon
|
||||
title: Demo-部署插件示例 # 模块-插件名
|
||||
title: Demo-部署插件示例 # 格式:平台-功能 ,例如: 网宿-更新CDNpro证书 ; 阿里云-上传到CAS证书管理服务 ; 天翼云-更新CDN证书
|
||||
group: cdn
|
||||
desc: 这仅仅是一个示例
|
||||
version: 1.0.0
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: DemoDnsProvider
|
||||
icon: fa-solid:frog
|
||||
title: Demo-Dns提供商插件示例 # 模块-插件名
|
||||
title: Demo-Dns提供商插件示例 # 格式:平台-功能 ,例如: 阿里云-DNS提供商 ;腾讯云-DNS提供商
|
||||
desc: 这只是一个示例
|
||||
type: custom
|
||||
version: 1.0.0
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
# mergeScript 用法
|
||||
|
||||
来源:前端 `packages/ui/certd-client/src/use/use-refrence.tsx`,类型定义来自 `@fast-crud/fast-crud`。
|
||||
|
||||
`mergeScript` 用来让输入字段的定义**依赖当前表单里的其他字段值动态变化**,例如:
|
||||
选了某个鉴权方式才显示对应字段、切换类型后清空已选值、下拉选项跟着另一个字段变。
|
||||
|
||||
它写成字符串放在字段定义上(内置插件放在 `@AccessInput` / `@TaskInput` 里,在线插件放在 `input.<字段>` 下):
|
||||
|
||||
```ts
|
||||
@AccessInput({
|
||||
title: "API Key",
|
||||
component: { name: "a-input", vModel: "value" },
|
||||
mergeScript: `
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
//access里面 要用 form.access.[字段名]
|
||||
return form.access.authType === 'apikey';
|
||||
//deploy插件里面要用form.[字段名]
|
||||
return form.authType === 'apikey';
|
||||
})
|
||||
}
|
||||
`,
|
||||
})
|
||||
apiKey = "";
|
||||
```
|
||||
|
||||
## 执行时机
|
||||
|
||||
字段定义被前端加载时,对**每一个字段**执行一次:
|
||||
|
||||
```js
|
||||
const func = new Function('ctx', formItem.mergeScript);
|
||||
const merged = func(ctx); // 字段脚本只执行这一次
|
||||
merge(formItem, merged); // 返回值深合并进该字段的定义
|
||||
delete formItem.mergeScript; // 执行完就删掉,最终定义里不再有 mergeScript
|
||||
```
|
||||
|
||||
要点:
|
||||
|
||||
- 脚本体内**必须 `return` 一个对象**,返回的对象会深合并到本字段的定义上。
|
||||
- 脚本本身只在加载字段时跑一次,**拿不到、也不应该判断用户填了什么值**。
|
||||
- 真正的动态逻辑写在返回对象里的 `ctx.compute(...)` 回调中,由 fast-crud 在渲染时求值。
|
||||
|
||||
## ctx 提供的能力
|
||||
|
||||
| 能力 | 说明 |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `ctx.compute(fn)` | 最常用。返回一个响应式的计算值:`fn` 里读到的表单字段会被自动追踪,字段变化时重新计算,视图跟着刷新 |
|
||||
|
||||
`ctx.compute` 的回调参数是一个 context 对象,常用字段:
|
||||
|
||||
- `form`:当前表单数据。**这是唯一常用的一个**。
|
||||
- `row`:行数据。
|
||||
- `mode`:表单模式 `add` / `edit` / `view`。
|
||||
- `getComponentRef(key)`:取其他字段组件的 ref。
|
||||
|
||||
注意:Certd 在把 `form` 传进回调前做过一次兼容处理(`form.input || form.body || form`),
|
||||
所以两种表单取值路径不同:
|
||||
|
||||
- **授权(Access)表单**:字段值在 `form.access.xxx`(字段 key 会被加上 `access.` 前缀)。
|
||||
- **任务(Task)表单**:字段值直接是 `form.xxx`。
|
||||
|
||||
## 可以动态化的属性
|
||||
|
||||
返回的对象里,任何字段定义属性都可以用 `ctx.compute`,常用的有:
|
||||
|
||||
| 属性 | 用途 |
|
||||
| -------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| `show` | 显隐(最常用) |
|
||||
| `required` | 动态必填,例如 `required: ctx.compute(({form}) => form.targetType === 'ingress')` |
|
||||
| `disabled` | 动态禁用,例如账号已创建后不允许再改:`disabled: ctx.compute(({form}) => !!form.access?.account)` |
|
||||
| `helper` | 动态帮助文案,回调里按条件 `return` 不同字符串 |
|
||||
| `value` | 动态默认值 |
|
||||
| `component` 下的属性 | 动态 `options`、`placeholder`、`inputKey`、`type`、`disabled` 等 |
|
||||
| `component.on` | 事件处理,见下面「联动」 |
|
||||
| `col` / `order` | 布局与排序 |
|
||||
|
||||
只写了一层 `show` 时最简形式就是本文开头的例子。
|
||||
|
||||
## 联动(监听事件并改动其他字段)
|
||||
|
||||
`component.on` 的事件回调**不**用 `ctx.compute` 包,它本身就是回调,参数是
|
||||
`{ form, $event, ... }`;`form` 同样做过 `form.input || form.body || form` 的兼容处理。
|
||||
事件名按组件而定,例如 `selectedChange`。
|
||||
|
||||
```ts
|
||||
mergeScript: `
|
||||
return {
|
||||
component: {
|
||||
on: {
|
||||
selectedChange: (scope) => {
|
||||
const form = scope.form;
|
||||
// 切换颁发机构后清空 ACME 账号,避免账号与机构不匹配
|
||||
form.acmeAccountAccessId = null;
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
`,
|
||||
```
|
||||
|
||||
另一种写法是把回调也做成计算值(需要用到组件上配置的 `onSelectedChange` / `onChange` 属性):
|
||||
|
||||
```ts
|
||||
mergeScript: `
|
||||
return {
|
||||
component: {
|
||||
onSelectedChange: ctx.compute(({form}) => {
|
||||
return ($event) => {
|
||||
form.dnsProviderAccessType = $event.accessType;
|
||||
};
|
||||
}),
|
||||
},
|
||||
}
|
||||
`,
|
||||
```
|
||||
|
||||
两种写法都能用,优先参考同目录已有插件的写法,保持一致。
|
||||
|
||||
## 常见错误
|
||||
|
||||
- **不要用 node 侧变量决定条件**。`mergeScript` 是在插件类定义阶段(node 侧)拼出来的字符串,
|
||||
此时表单还没打开,拿不到任何用户输入;用参数拼条件等于把逻辑写死在服务端。
|
||||
条件必须原样落到 `ctx.compute` 回调里。
|
||||
- **不要用 `if` 直接判断字段值**。回调必须 `return` 一个值,写成
|
||||
`show: ctx.compute(({form}) => { if (...) { return true } })` 也可以,但不要漏掉 `return`。
|
||||
- **不要漏掉历史数据兼容**。新增可选字段时,历史数据里没有这个字段(值为 `undefined`),
|
||||
条件要按旧行为兜底,例如 `!form.access.authType || form.access.authType === 'aksk'`。
|
||||
- **不要访问 `form.access` 以外的路径**去读授权字段,也不要在授权表单里按任务表单的路径取值。
|
||||
- **不要依赖脚本执行时的副作用**(打印日志、改全局状态),它在每次打开表单时都会重新执行。
|
||||
|
||||
## YAML 写法(在线插件)
|
||||
|
||||
`mergeScript` 是含换行的脚本字符串,必须用 YAML 块标量:
|
||||
|
||||
```yaml
|
||||
input:
|
||||
apiKey:
|
||||
title: API Key
|
||||
component:
|
||||
name: a-input
|
||||
vModel: value
|
||||
mergeScript: |2-
|
||||
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return form.access.authType === 'apikey';
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
导出生成的缩进比较特殊(块标量内容统一缩进),**不要手工调整缩进**:
|
||||
用脚本生成 YAML,并保证导出后再导入内容一致。转换成 block scalar 时注意:
|
||||
|
||||
- `|` / `|2-` 等块标量不需要转义换行,不要把脚本写成带 `\n` 的单行。
|
||||
- 脚本里不要出现 `??` 这类容易被误判为乱码的写法,用显式 `if (x == null)` 代替。
|
||||
|
||||
## 验证方式
|
||||
|
||||
前端表单没法在插件开发流程里直接断言,可以在后端加一个 `src/**/*.test.ts`,
|
||||
按前端 `useReference` 的路径执行字段上的 `mergeScript`:
|
||||
|
||||
```ts
|
||||
const computedGetters = [];
|
||||
const ctx = {
|
||||
compute: getter => {
|
||||
computedGetters.push(getter);
|
||||
return getter;
|
||||
},
|
||||
asyncCompute: () => undefined,
|
||||
computed: () => undefined,
|
||||
};
|
||||
new Function('ctx', mergeScript)(ctx);
|
||||
// 用假的 form 求值,断言显隐结果
|
||||
const show = computedGetters[0]({ form: { access: { authType: 'apikey' } } });
|
||||
```
|
||||
|
||||
参考实现:`packages/ui/certd-server/src/plugins/plugin-wangsu/access-input.test.ts`。
|
||||
要点是断言「同一个 show 函数换 `authType` 结果会变」,证明显隐确实是运行时算的,
|
||||
而不是在 node 侧定死的。
|
||||
@@ -7,7 +7,7 @@
|
||||
```yaml
|
||||
name: DemoTask
|
||||
author: demo
|
||||
title: Demo 任务
|
||||
title: Demo-插件名称 # 格式:平台-功能 ,例如: 网宿-更新CDNpro证书 ; 阿里云-上传到CAS证书管理服务 ; 天翼云-更新CDN证书
|
||||
desc: 插件说明
|
||||
icon: clarity:plugin-line
|
||||
pluginType: task
|
||||
@@ -61,6 +61,8 @@ content: |
|
||||
### 编辑规则
|
||||
|
||||
- 修改已有插件时保留 `name`、`author`、`pluginType` 和已有兼容字段。
|
||||
- 新插件的 `author` 使用启动提示词中给出的插件作者,不要自行编造作者名;启动提示词未给出作者时先向用户确认。
|
||||
- `fullName` 由 `author/name` 组成,作者变化会导致插件身份变化,不要随意修改。
|
||||
- 只修改需求涉及的字段,避免删除未知的 YAML 字段。
|
||||
- 脚本过长时仍放在同一个 `content` block scalar 中。
|
||||
- 提交前检查 YAML 可解析、`content` 非空、版本和插件类型没有被意外修改。
|
||||
|
||||
@@ -57,4 +57,41 @@ return class DemoAccess extends BaseAccess {
|
||||
- `onTestRequest` 应调用实际 API 方法并在失败时抛出异常。
|
||||
- 对外 API 方法应统一处理分页、错误和返回字段。
|
||||
- 使用 `this.logger` 或框架提供的 logger,禁止 `console.log`。
|
||||
- 一般将API接口方法封装到Access中,其他plugin调用access的方法当做client sdk使用
|
||||
- 一般将API接口方法封装到Access中,其他plugin调用access的方法当做client sdk使用
|
||||
|
||||
## 字段动态显隐
|
||||
|
||||
多种鉴权方式(例如「ApiKey」和「AccessKey签名」)共用一个授权时,
|
||||
用字段上的 `mergeScript` 按所选方式显隐对应字段,只让用户看到当前方式需要的字段:
|
||||
|
||||
```yaml
|
||||
input:
|
||||
authType:
|
||||
title: 鉴权方式
|
||||
value: apikey
|
||||
component:
|
||||
name: a-select
|
||||
vModel: value
|
||||
options:
|
||||
- { label: AccessKey签名, value: aksk }
|
||||
- { label: ApiKey, value: apikey }
|
||||
accessKeyId:
|
||||
title: accessKeyId
|
||||
component:
|
||||
name: a-input
|
||||
vModel: value
|
||||
mergeScript: |2-
|
||||
|
||||
return {
|
||||
show: ctx.compute(({form})=>{
|
||||
return !form.access.authType || form.access.authType === 'aksk';
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
- 判断必须写在 `ctx.compute(({form})=>{ ... })` 回调里;脚本本身在插件定义阶段就拼好了,
|
||||
那时表单还没打开,用参数在服务端判断字段值没有意义。
|
||||
- 授权表单的字段值在 `form.access.xxx`(任务表单才是 `form.xxx`)。
|
||||
- 历史授权没有 `authType` 字段,条件里要用 `!form.access.authType || ...` 兜底走旧逻辑。
|
||||
|
||||
完整用法见父 Skill 的 `references/merge-script.md`。
|
||||
@@ -75,11 +75,12 @@ Certd 是可私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web
|
||||
- 只有真正机械化的大批量替换、格式化或生成任务才考虑脚本/工具。若必须使用临时脚本,应放在临时目录并在同一个受控步骤内完成创建、执行、删除;不要把临时脚本落在仓库里跨多步工具调用执行。
|
||||
- 不要主动运行 `pnpm install`;缺依赖、TTY、网络导致安装或测试失败时,停止尝试并说明环境问题。
|
||||
- 优先沿用现有模块、插件、service、页面模式;不要为形式上的复用制造过度抽象。
|
||||
- 代码可读性优先于短写法。复杂条件、三元表达式、链式调用、内联对象和多层 helper 调用要拆成命名清晰的中间变量或小方法。
|
||||
- 【重要重要重要】代码可读性优先于短写法。尽量少用三元表达式,优先使用完整的 `if/else`【其他技能与此条要求冲突的,以此条要求为准】;复杂条件、链式调用、内联对象和多层 helper 调用要拆成命名清晰的中间变量或小方法。
|
||||
- 方法调用链不要直接塞进另一个方法参数;先用有意义的局部变量承接返回值,再传入下一步。
|
||||
- 不要在单一表达式内嵌套分支、对象构造与方法调用。优先使用清晰的 `if/else` 分支;仅在确实能降低复杂度时才提取有意义的中间变量,避免为拆分而增加阅读跳转。
|
||||
- 注释优先使用中文,尤其是业务规则、兼容逻辑、协议细节和隐藏风险;文件已有英文风格或引用外部术语时可保持一致。
|
||||
- 遵守 DRY 和单一职责;第三次出现的业务规则、字段转换、权限判断、Repository 选择、事务传播、金额计算等逻辑,应优先抽成合适 helper 或 service 方法。
|
||||
- 如无明确复用价值,不要随意新增 `normalizeXxx` 等辅助方法;优先在现有业务方法中直接处理简单兼容或字段转换逻辑,避免增加不必要的抽象层。
|
||||
|
||||
|
||||
## 测试与验证
|
||||
|
||||
@@ -3,6 +3,74 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [BUG]使用天翼云CDN部署证书时无法匹配域名 ([bb1f232](https://github.com/certd/certd/commit/bb1f232f415c522851dede979f5d609dda844fd3))
|
||||
* 缺失运行时嵌套依赖时添加清除缓存提示 ([2d0b85b](https://github.com/certd/certd/commit/2d0b85b3054fc1aa82b3ab5db34a2a56308f71f2))
|
||||
* 支持google persist校验方式 ([97d2b50](https://github.com/certd/certd/commit/97d2b5007409ec79ea864d437c473a2f9b5dfae4))
|
||||
* **config:** 修复环境变量加载逻辑并更新配置文件 ([07f0d3b](https://github.com/certd/certd/commit/07f0d3b4d626628a0e3dfc7e5c52e8c4a98bad47))
|
||||
* **ctyun-cdn:** 修复天翼云CDN部署插件的域名选择问题 ([ea7d14c](https://github.com/certd/certd/commit/ea7d14c289b8720781bf101bfef59eac7bb03765))
|
||||
* **webhook:** 修复模版转义换行符,避免手动上传的证书报错 ([2735feb](https://github.com/certd/certd/commit/2735feb3302f8308708dafc8b295adcd4b91d7f0))
|
||||
|
||||
### Features
|
||||
|
||||
* **client:** 新增客户端管理功能 ([3dbbe72](https://github.com/certd/certd/commit/3dbbe72fc15c114ca6b5e8ff45cb21f258b80650))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 宝塔站点列表支持搜索和排序 ([4420472](https://github.com/certd/certd/commit/4420472f8fac272d30fb8ccb1cc2ee9763989001))
|
||||
* 网宿插件双鉴权支持 ([241b2e5](https://github.com/certd/certd/commit/241b2e55848471e61b5d2be0c87a7dc9a0d29557))
|
||||
* **monitor:** 站点证书监控支持手动选中触发检查 ([f60e63f](https://github.com/certd/certd/commit/f60e63fbaf00460d5050cd7705bd75d57dec202d))
|
||||
* **open-api:** openkey增加用途备注 ([bbeaf61](https://github.com/certd/certd/commit/bbeaf617d19750515f6eca51863e74e23bc1de50))
|
||||
* **tencent clb:** 修改所有CLB地域的endpoint为就近接入 ([c427735](https://github.com/certd/certd/commit/c4277350bcc8cf034cf4e76db2a6daa1842bc5d7))
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复添加步骤会修改所有步骤标题的bug ([51e83e4](https://github.com/certd/certd/commit/51e83e4c38f7f319314fdc38ef33cf5720b2dde0))
|
||||
* 修复证书插件配置无法打开的bug ([5563abb](https://github.com/certd/certd/commit/5563abb13a62140f4dee668ab768cf4ff9371ddb))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 新增余额管理,管理员充值功能 ([bded368](https://github.com/certd/certd/commit/bded368cbb8974b6937ae260e448c25fae5c33ad))
|
||||
* **acme-account-access:** AcmeAccount授权允许修改eab,重新生成account ([6a649aa](https://github.com/certd/certd/commit/6a649aaca083ccd3b517f7d676a58220aa33b37d))
|
||||
* **ui:** 商业版套餐购买不要求设置支付方式 ([e971145](https://github.com/certd/certd/commit/e9711456860d7db40914a0c295534f7bca56f83c))
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **plugin-service:** 修复同步自己开发的云端插件不显示已安装状态问题 ([dac94b6](https://github.com/certd/certd/commit/dac94b64f0ceb7176e2cf07ce220be0aa5ed98d2))
|
||||
* **plugin:** 修复插件配置保存与加载的相关问题 ([bd94334](https://github.com/certd/certd/commit/bd9433478a43d5d64ce6370386d0fe0aaff07d2e))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **plugin:** add Fastly custom TLS certificates integration ([#797](https://github.com/certd/certd/issues/797)) [@szhu25](https://github.com/szhu25) ([2a2e963](https://github.com/certd/certd/commit/2a2e963914c4dfc192cbe0a4bbb9c115e626da01))
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复插件市场插件安装无效的bug ([15d2bd8](https://github.com/certd/certd/commit/15d2bd8484a9ddd142bf4ef03d839c74405f9e6d))
|
||||
* preserve Route53 TXT record values ([#791](https://github.com/certd/certd/issues/791)) ([f5430a8](https://github.com/certd/certd/commit/f5430a8925cf6cf4e16c75f2358fbd78437e6705))
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复编辑证书申请任务导致清空ACME账号的bug ([d67379f](https://github.com/certd/certd/commit/d67379f29725bbd6dcdefe97d101023c7fd11f03))
|
||||
* 修复大量问题 ([f911e06](https://github.com/certd/certd/commit/f911e06a698d568ae228880ec89b7e25fdd1305b))
|
||||
* 修复旧流水线保存时清空证书仓库证书内容的bug ([f58f2f3](https://github.com/certd/certd/commit/f58f2f3cd603c37e55ac5868fdd2ca99aa937e44))
|
||||
* **pipeline-controller:** 处理非编辑场景下保留id的问题 ([6606f32](https://github.com/certd/certd/commit/6606f3267868e3aadaca0092ef4c83198312553c))
|
||||
* **pipeline:** 修正流水线保存时的ID判断逻辑并增加参数校验 ([6af12d4](https://github.com/certd/certd/commit/6af12d49fce1144943dc37ba574b3c9356998dcd))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **dashboard:** 新增仪表盘站点监控和域名管理的统计卡片 ([9842a24](https://github.com/certd/certd/commit/9842a24a50a841f710ee7c8050a38e3334c58394))
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -105,13 +105,17 @@ https://certd.handfree.work/
|
||||
|
||||
#### Docker镜像说明:
|
||||
|
||||
##### 1. 镜像地址格式:
|
||||
##### 1. 镜像地址:
|
||||
|
||||
```
|
||||
registry.cn-shenzhen.aliyuncs.com/certd/certd:[version-][system-][latest/stable]
|
||||
------------ ↑ 镜像地址 ------------- ↑ 镜像名 -- ↑指定版本- ↑基础系统- ↑最新版本类型
|
||||
```
|
||||
##### 2. 版本标签:
|
||||
| 镜像仓库 | 最新预览版 | 稳定版 | slim版 | armv7版 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 阿里云【默认】 | `registry.cn-shenzhen.aliyuncs.com/certd/certd:latest` | `certd:stable` | `certd:slim` | `certd:armv7` |
|
||||
| Docker Hub | `certd/certd:latest` | `certd:stable` | `certd:slim` | `certd:armv7` |
|
||||
| GitHub Packages | `ghcr.io/certd/certd:latest` | `certd:stable` | `certd:slim` | `certd:armv7` |
|
||||
|
||||
> 后面的各个版本省略了镜像地址,使用时需要将镜像地址拼接完整。
|
||||
|
||||
##### 2. 版本标签说明:
|
||||
|
||||
**最新版本标签:**
|
||||
|
||||
@@ -124,25 +128,17 @@ registry.cn-shenzhen.aliyuncs.com/certd/certd:[version-][system-][latest/stable]
|
||||
|
||||
> 根据基础镜像不同,分为如下三个分支版本,没有特殊需求选择默认的即可(他们功能是一样的)
|
||||
|
||||
| 系统版本 | 版本标签 | 基础系统 | 说明 | 稳定版标签 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| alpine【默认】 | `certd:latest` | Alpine Linux | 默认版本,镜像体积小 | `certd:stable` |
|
||||
| slim | `certd:slim` | Debian slim | 基于glibc,dns解析兼容性好 | `certd:slim-stable` |
|
||||
| armv7 | `certd:armv7` | Alpine Linux | ARMv7 架构专用版本 | `certd:armv7-stable` |
|
||||
|
||||
##### 2. 镜像地址:
|
||||
|
||||
| 镜像仓库 | 最新预览版 | slim | armv7 |
|
||||
| --- | --- | --- | --- |
|
||||
| 阿里云【默认】 | `registry.cn-shenzhen.aliyuncs.com/certd/certd:latest` | `certd:slim` | `certd:armv7` |
|
||||
| Docker Hub | `greper/certd:latest` | `certd:slim` |
|
||||
| GitHub Packages | `ghcr.io/certd/certd:latest` | `certd:slim` | `certd:armv7` |
|
||||
| 系统版本 | 版本标签 | 基础系统 | 说明 | 稳定版标签 | 指定版本 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| alpine【默认】 | `certd:latest` | Alpine Linux | 默认版本,镜像体积小 | `certd:stable` | `certd:1.43.0` |
|
||||
| slim | `certd:slim` | Debian slim | 基于glibc,dns解析兼容性好 | `certd:slim-stable` | `certd:1.43.0-slim` |
|
||||
| armv7 | `certd:armv7` | Alpine Linux | ARMv7 架构专用版本 | `certd:armv7-stable` | `certd:1.43.0-armv7` |
|
||||
|
||||
|
||||
> 注意:
|
||||
> 1. 后面的各个版本省略了镜像地址,使用时需要将镜像地址拼接完整。
|
||||
> 2. 稳定版在后面加 `-stable` 即可。
|
||||
> 3. 如需指定具体的版本号,在冒号后面加 `version-`即可,例如 `certd:1.42.1-stable`。
|
||||
> 如果您不确定使用哪个版本,请使用默认的 `certd:latest` 版本。
|
||||
|
||||
最新版本号:
|
||||

|
||||
|
||||
|
||||
##### 3. 镜像构建说明:
|
||||
|
||||
@@ -2,9 +2,9 @@ version: '3.3' # 兼容旧版docker-compose
|
||||
services:
|
||||
certd:
|
||||
# 镜像 # ↓↓↓↓↓ ---- 镜像版本号,建议改成固定版本号,例如:certd:1.29.0
|
||||
image: registry.cn-shenzhen.aliyuncs.com/certd/certd:latest
|
||||
image: registry.cn-shenzhen.aliyuncs.com/certd/certd:latest # 稳定版为 certd/certd:stable
|
||||
# image: ghcr.io/certd/certd:latest # --------- 如果 报镜像not found,可以尝试其他镜像源
|
||||
# image: greper/certd:latest
|
||||
# image: certd/certd:latest
|
||||
# --------- 生产建议使用稳定版, latest改成stable即可
|
||||
# image: registry.cn-shenzhen.aliyuncs.com/certd/certd:stable
|
||||
|
||||
@@ -77,7 +77,13 @@ services:
|
||||
# - certd_typeorm_dataSource_default_password=yourpasswd # 密码
|
||||
# - certd_typeorm_dataSource_default_database=certd # 数据库名
|
||||
|
||||
# ↓↓↓↓ --------------------------------------------------------- 自动升级,上面certd的版本号要保持为latest
|
||||
# #↓↓↓↓ ---------------------------- 如果数据库服务端开启了加密传输 require_secure_transport-ON
|
||||
# - certd_typeorm_dataSource_default_ssl_rejectUnauthorized=false # 忽略证书校验,或者配置下面的三项路径
|
||||
# - certd_typeorm_dataSource_default_ssl_ca # 证书CA路径【如果上面false,无需配置】
|
||||
# - certd_typeorm_dataSource_default_ssl_key # 私钥路径【如果上面false,无需配置】
|
||||
# - certd_typeorm_dataSource_default_ssl_cert # 证书路径【如果上面false,无需配置】
|
||||
|
||||
# ↓↓↓↓ --------------------------------------------------------- 自动升级,上面certd的版本号要保持为latest / stable
|
||||
# certd-updater: # 添加 Watchtower 服务
|
||||
# image: containrrr/watchtower:latest
|
||||
# container_name: certd-updater
|
||||
|
||||
@@ -3,6 +3,114 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [BUG]使用天翼云CDN部署证书时无法匹配域名 ([bb1f232](https://github.com/certd/certd/commit/bb1f232f415c522851dede979f5d609dda844fd3))
|
||||
* 缺失运行时嵌套依赖时添加清除缓存提示 ([2d0b85b](https://github.com/certd/certd/commit/2d0b85b3054fc1aa82b3ab5db34a2a56308f71f2))
|
||||
* 支持google persist校验方式 ([97d2b50](https://github.com/certd/certd/commit/97d2b5007409ec79ea864d437c473a2f9b5dfae4))
|
||||
* **config:** 修复环境变量加载逻辑并更新配置文件 ([07f0d3b](https://github.com/certd/certd/commit/07f0d3b4d626628a0e3dfc7e5c52e8c4a98bad47))
|
||||
* **ctyun-cdn:** 修复天翼云CDN部署插件的域名选择问题 ([ea7d14c](https://github.com/certd/certd/commit/ea7d14c289b8720781bf101bfef59eac7bb03765))
|
||||
* **webhook:** 修复模版转义换行符,避免手动上传的证书报错 ([2735feb](https://github.com/certd/certd/commit/2735feb3302f8308708dafc8b295adcd4b91d7f0))
|
||||
|
||||
### Features
|
||||
|
||||
* **client:** 新增客户端管理功能 ([3dbbe72](https://github.com/certd/certd/commit/3dbbe72fc15c114ca6b5e8ff45cb21f258b80650))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 宝塔站点列表支持搜索和排序 ([4420472](https://github.com/certd/certd/commit/4420472f8fac272d30fb8ccb1cc2ee9763989001))
|
||||
* 网宿插件双鉴权支持 ([241b2e5](https://github.com/certd/certd/commit/241b2e55848471e61b5d2be0c87a7dc9a0d29557))
|
||||
* **monitor:** 站点证书监控支持手动选中触发检查 ([f60e63f](https://github.com/certd/certd/commit/f60e63fbaf00460d5050cd7705bd75d57dec202d))
|
||||
* **open-api:** openkey增加用途备注 ([bbeaf61](https://github.com/certd/certd/commit/bbeaf617d19750515f6eca51863e74e23bc1de50))
|
||||
* **tencent clb:** 修改所有CLB地域的endpoint为就近接入 ([c427735](https://github.com/certd/certd/commit/c4277350bcc8cf034cf4e76db2a6daa1842bc5d7))
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复添加步骤会修改所有步骤标题的bug ([51e83e4](https://github.com/certd/certd/commit/51e83e4c38f7f319314fdc38ef33cf5720b2dde0))
|
||||
* 修复证书插件配置无法打开的bug ([5563abb](https://github.com/certd/certd/commit/5563abb13a62140f4dee668ab768cf4ff9371ddb))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 新增余额管理,管理员充值功能 ([bded368](https://github.com/certd/certd/commit/bded368cbb8974b6937ae260e448c25fae5c33ad))
|
||||
* **acme-account-access:** AcmeAccount授权允许修改eab,重新生成account ([6a649aa](https://github.com/certd/certd/commit/6a649aaca083ccd3b517f7d676a58220aa33b37d))
|
||||
* **ui:** 商业版套餐购买不要求设置支付方式 ([e971145](https://github.com/certd/certd/commit/e9711456860d7db40914a0c295534f7bca56f83c))
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **plugin-service:** 修复同步自己开发的云端插件不显示已安装状态问题 ([dac94b6](https://github.com/certd/certd/commit/dac94b64f0ceb7176e2cf07ce220be0aa5ed98d2))
|
||||
* **plugin:** 修复插件配置保存与加载的相关问题 ([bd94334](https://github.com/certd/certd/commit/bd9433478a43d5d64ce6370386d0fe0aaff07d2e))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **plugin:** add Fastly custom TLS certificates integration ([#797](https://github.com/certd/certd/issues/797)) [@szhu25](https://github.com/szhu25) ([2a2e963](https://github.com/certd/certd/commit/2a2e963914c4dfc192cbe0a4bbb9c115e626da01))
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复插件市场插件安装无效的bug ([15d2bd8](https://github.com/certd/certd/commit/15d2bd8484a9ddd142bf4ef03d839c74405f9e6d))
|
||||
* preserve Route53 TXT record values ([#791](https://github.com/certd/certd/issues/791)) ([f5430a8](https://github.com/certd/certd/commit/f5430a8925cf6cf4e16c75f2358fbd78437e6705))
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复编辑证书申请任务导致清空ACME账号的bug ([d67379f](https://github.com/certd/certd/commit/d67379f29725bbd6dcdefe97d101023c7fd11f03))
|
||||
* 修复大量问题 ([f911e06](https://github.com/certd/certd/commit/f911e06a698d568ae228880ec89b7e25fdd1305b))
|
||||
* 修复旧流水线保存时清空证书仓库证书内容的bug ([f58f2f3](https://github.com/certd/certd/commit/f58f2f3cd603c37e55ac5868fdd2ca99aa937e44))
|
||||
* **pipeline-controller:** 处理非编辑场景下保留id的问题 ([6606f32](https://github.com/certd/certd/commit/6606f3267868e3aadaca0092ef4c83198312553c))
|
||||
* **pipeline:** 修正流水线保存时的ID判断逻辑并增加参数校验 ([6af12d4](https://github.com/certd/certd/commit/6af12d49fce1144943dc37ba574b3c9356998dcd))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **dashboard:** 新增仪表盘站点监控和域名管理的统计卡片 ([9842a24](https://github.com/certd/certd/commit/9842a24a50a841f710ee7c8050a38e3334c58394))
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复证书申请时EAB判断逻辑,已选择Acme账号的情况下不需要EAB ([d87821e](https://github.com/certd/certd/commit/d87821ee02b17136fc925df4547223332bf22823))
|
||||
* **pipeline:** 校验定时触发器cron表达式,拦截2月31号等无效日期 ([#785](https://github.com/certd/certd/issues/785)) [@yukaidi1220](https://github.com/yukaidi1220) ([f8e492d](https://github.com/certd/certd/commit/f8e492d6cb49257e56bbd06b28fd6eb32c923c28))
|
||||
* **plugin-ai-dev:** 优化AI插件测试流程并修复日志获取问题 ([faf9828](https://github.com/certd/certd/commit/faf9828168f6ce82adc530a65eaf313d6c9904b5))
|
||||
* **plugin-item-card:** 移除分数格式化时去掉.0的逻辑 ([a91d7c0](https://github.com/certd/certd/commit/a91d7c000a2aa6456a249433ff90a47787ec90f0))
|
||||
* **ssh:** 修复 keyboard-interactive 认证可能抛类型错误的问题 ([5e867c8](https://github.com/certd/certd/commit/5e867c86c30eb43f1e5aeee8c22d47596e173a8c))
|
||||
* **start.sh:** 优化前端zip包下载校验逻辑 ([15ee6ea](https://github.com/certd/certd/commit/15ee6ead49db3620d2ade641aadbfcb13ac3e6dc))
|
||||
* **tencent access:** 修复腾讯云国际站接口endpoint拼接问题 ([f614872](https://github.com/certd/certd/commit/f61487284f79d3f5a72c9f7eee04999a93728326))
|
||||
|
||||
### Features
|
||||
|
||||
* 实现自定义ACME证书颁发机构支持 ([97c417b](https://github.com/certd/certd/commit/97c417b1ad64b9a14bee8bfe98a2e789b9278820))
|
||||
* 新增证书吊销后置任务与证书状态管理 ([1cc9641](https://github.com/certd/certd/commit/1cc9641f5669710d1e5809995ad395c843855a12))
|
||||
* 新增AI插件开发全流程能力 ([6f9dffe](https://github.com/certd/certd/commit/6f9dffe6e59cc125bd394d7db6086d66a9c71e49))
|
||||
* 支持审计日志,操作日志 ([f2855d6](https://github.com/certd/certd/commit/f2855d6dac74c9db71d92f99601e35c360b10666))
|
||||
* 支持在线插件下载安装审核等 ([255960d](https://github.com/certd/certd/commit/255960d434eebb3d567a08f737f1642ab417b5d0))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 统一镜像地址格式 certd/certd ([49007d3](https://github.com/certd/certd/commit/49007d391565099d94dec375eb88e946856506b0))
|
||||
* 新增AI插件开发受限令牌与隔离API ([3a9069f](https://github.com/certd/certd/commit/3a9069f6e6242940229fd63c686f7e445408dbbb))
|
||||
* 优化源码启动脚本,增加快速启动 ([0cfc3b7](https://github.com/certd/certd/commit/0cfc3b7ae90fa031d75f0b395556a04d4efee76c))
|
||||
* 统一将路由前缀从/certd改为/cert ([4db0f83](https://github.com/certd/certd/commit/4db0f83304dc6fd9199673c48b1805421f024601))
|
||||
* 新增官方客户端Certd Client的多语言配置与页面入口 ([fbfe125](https://github.com/certd/certd/commit/fbfe1256a5c8be6dfb2fbae7e6130eb5f6c6d946))
|
||||
* 新增证书吊销功能,添加证书状态字段 ([5074572](https://github.com/certd/certd/commit/5074572694383c8e1bf2880e26827987683dc62c))
|
||||
* 新增自定义ACME管理功能,完善证书颁发机构适配 ([2ab23f5](https://github.com/certd/certd/commit/2ab23f50b216e27495fc823d2269ca0ba6366d15))
|
||||
* 移除全局反向代理配置,迁移到自定义ACME配置 ([c57110e](https://github.com/certd/certd/commit/c57110e42aada31dd0028b56b5baec1b19722525))
|
||||
* 在线插件支持通知类型 ([c0f194f](https://github.com/certd/certd/commit/c0f194f4438bd7ca3f52f63ca088543abde7428c))
|
||||
* 增加稳定版,预览版区分,预览版是最新版,稳定版在预览版验证稳定后,再将预览版转为稳定版 ([1574090](https://github.com/certd/certd/commit/15740904e5e97ee767c39e4401dbc1d645795c38))
|
||||
* 支持AI开发在线插件 ([eae7edb](https://github.com/certd/certd/commit/eae7edbda0fd7357163f33fb4084fcba21396f5f))
|
||||
* **domain:** 域名管理增加remark字段 ([e4041ea](https://github.com/certd/certd/commit/e4041eabece1bab2c1c4b955129cf7a7fe7302d0))
|
||||
* **login,settings:** 仅第三方登录开启后,在重启10分钟后将无法使用密码登录 ([1d12ad7](https://github.com/certd/certd/commit/1d12ad73c6ca827d2eb36bda867f9d9fe62dab48))
|
||||
* **plugin:** 插件安装支持检测依赖 ([e79660c](https://github.com/certd/certd/commit/e79660c5d17ed4049508a00a359950a54470332f))
|
||||
* **plugin:** 新增插件评分展示、排序与个人插件筛选功能 ([7ab5029](https://github.com/certd/certd/commit/7ab502991e0c001777b268b9f159311ef5e000a0))
|
||||
* **server:** response优化为gzip压缩 ([f38522c](https://github.com/certd/certd/commit/f38522ca0b2185f7f35538f53cb51dcf86d0bfc8))
|
||||
* **tencent-clb:** 重构腾讯云CLB部署插件的地域选择组件,改为远程下拉选择器 ([30d4fc8](https://github.com/certd/certd/commit/30d4fc84a9e940d1d07e1f25856eb6fa13da9ba1))
|
||||
|
||||
# [1.43.0](https://github.com/certd/certd/compare/v1.42.6...v1.43.0) (2026-08-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -125,9 +125,9 @@ docker-compose up -d
|
||||
|
||||
## 三、问题
|
||||
|
||||
### 1. 迁移到PG之后,所有表的Sequence会从1开始
|
||||
### 1. 迁移到PostgreSQL之后,所有表的Sequence会从1开始
|
||||
|
||||
执行如下sql 语句,批量将所有表的id自增设置到当前数据的最大值
|
||||
执行如下sql 语句,批量将所有表的id自增设置到当前数据的最大值【仅迁移到postgresql需要执行】
|
||||
```sql
|
||||
DO $$
|
||||
DECLARE
|
||||
|
||||
@@ -6,23 +6,42 @@
|
||||
|
||||
Certd 提供多种 Docker 镜像版本,您可以根据需要选择:
|
||||
|
||||
**最新版本:**
|
||||
#### 1. 镜像地址:
|
||||
|
||||
| 镜像仓库 | 最新预览版 | 稳定版 | slim版 | armv7版 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 阿里云【默认】 | `registry.cn-shenzhen.aliyuncs.com/certd/certd:latest` | `certd:stable` | `certd:slim` | `certd:armv7` |
|
||||
| Docker Hub | `certd/certd:latest` | `certd:stable` | `certd:slim` | `certd:armv7` |
|
||||
| GitHub Packages | `ghcr.io/certd/certd:latest` | `certd:stable` | `certd:slim` | `certd:armv7` |
|
||||
|
||||
> 后面的各个版本省略了镜像地址,使用时需要将镜像地址拼接完整。
|
||||
|
||||
#### 2. 版本标签说明:
|
||||
|
||||
**最新版本标签:**
|
||||
|
||||
| 版本 | 标签 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 预览版【默认】 | `certd:latest` | 指向最新开发版本,包含最新功能,但稳定性不如稳定版 |
|
||||
| 稳定版 | `certd:stable` | 指向经过充分测试的生产就绪版本,推荐生产环境使用 |
|
||||
| 最新预览版【默认】 | `certd:latest` | 指向最新开发版本,包含最新功能,但稳定性不如稳定版 |
|
||||
| 最新稳定版 | `certd:stable` | 指向经过充分测试的生产就绪版本,推荐生产环境使用 |
|
||||
|
||||
**系统版本分支:**
|
||||
**系统分支版本:**
|
||||
|
||||
> 根据基础镜像不同,分为如下三个分支版本,没有特殊需求选择默认的即可(他们功能是一样的)
|
||||
|
||||
| 系统版本 | 版本标签 | 基础系统 | 说明 | 稳定版标签 | 指定版本 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| alpine【默认】 | `certd:latest` | Alpine Linux | 默认版本,镜像体积小 | `certd:stable` | `certd:1.43.0` |
|
||||
| slim | `certd:slim` | Debian slim | 基于glibc,dns解析兼容性好 | `certd:slim-stable` | `certd:1.43.0-slim` |
|
||||
| armv7 | `certd:armv7` | Alpine Linux | ARMv7 架构专用版本 | `certd:armv7-stable` | `certd:1.43.0-armv7` |
|
||||
|
||||
| 分支版本标签 | 基础系统 | 说明 | 指定版本 | 稳定版 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `certd:latest` 【默认】 | Alpine Linux | 默认版本,镜像体积小 | `certd:[version]` | `certd:[version-]stable` |
|
||||
| `certd:slim` | Debian slim | glibc版本,dns解析兼容性更好(可能需要配置security_opt -seccomp=unconfined)| `certd:[version-]slim` | `certd:[version-]slim-stable` |
|
||||
| `certd:armv7` | Alpine Linux | ARMv7 架构专用版本 | `certd:[version]-armv7` | `certd:[version-]armv7-ststable` |
|
||||
|
||||
> 如果您不确定使用哪个版本,请使用默认的 `certd:latest` 版本。
|
||||
|
||||
最新版本号:
|
||||

|
||||
|
||||
|
||||
### 一键脚本安装(推荐)
|
||||
|
||||
如果您的服务器未安装 Docker,该脚本会自动为您安装 Docker 和 Docker Compose,并启动 Certd 容器。
|
||||
|
||||
@@ -36,52 +36,53 @@
|
||||
| 32.| **Dokploy授权** | |
|
||||
| 33.| **Dynadot授权** | |
|
||||
| 34.| **farcdn授权** | |
|
||||
| 35.| **FlexCDN授权** | |
|
||||
| 36.| **Gcore** | Gcore |
|
||||
| 37.| **Github授权** | |
|
||||
| 38.| **godaddy授权** | |
|
||||
| 39.| **HiPM DNSMgr** | HiPM DNSMgr API Token 授权 |
|
||||
| 40.| **金山云授权** | |
|
||||
| 41.| **FTP授权** | |
|
||||
| 42.| **七牛OSS授权** | |
|
||||
| 43.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 |
|
||||
| 44.| **s3/minio授权** | S3/minio oss授权 |
|
||||
| 45.| **namesilo授权** | |
|
||||
| 46.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 |
|
||||
| 47.| **Nginx Proxy Manager 授权** | 用于登录 Nginx Proxy Manager,并为代理主机证书部署提供授权。 |
|
||||
| 48.| **1panel授权** | 账号和密码 |
|
||||
| 49.| **支付宝** | |
|
||||
| 50.| **白山云授权** | |
|
||||
| 51.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 |
|
||||
| 52.| **cdnfly授权** | |
|
||||
| 53.| **k8s授权** | |
|
||||
| 54.| **括彩云cdn授权** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 55.| **LeCDN授权** | |
|
||||
| 56.| **lucky** | |
|
||||
| 57.| **猫云授权** | |
|
||||
| 58.| **plesk授权** | |
|
||||
| 59.| **长亭雷池授权** | |
|
||||
| 60.| **群晖登录授权** | |
|
||||
| 61.| **uniCloud** | unicloud授权 |
|
||||
| 62.| **微信支付** | |
|
||||
| 63.| **易盾rcdn授权** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 64.| **易发云短信** | sms.yfyidc.cn/ |
|
||||
| 65.| **易盾DCDN授权** | https://user.yiduncdn.com |
|
||||
| 66.| **易支付** | |
|
||||
| 67.| **proxmox** | |
|
||||
| 68.| **Spaceship.com 授权** | Spaceship.com API 授权插件 |
|
||||
| 69.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器授权 |
|
||||
| 70.| **UCloud授权** | 优刻得授权 |
|
||||
| 71.| **又拍云** | |
|
||||
| 72.| **网宿授权** | |
|
||||
| 73.| **西部数码授权** | |
|
||||
| 74.| **我爱云授权** | 我爱云CDN |
|
||||
| 75.| **新网授权(代理方式)** | |
|
||||
| 76.| **新网授权** | |
|
||||
| 77.| **新网互联授权** | 仅支持代理账号,ip需要加入白名单 |
|
||||
| 78.| **Zenlayer授权** | Zenlayer授权 |
|
||||
| 79.| **GoEdge授权** | |
|
||||
| 80.| **雨云授权** | https://app.rainyun.com/ |
|
||||
| 35.| **Fastly授权** | Fastly CDN / TLS Custom Certificates API 授权 |
|
||||
| 36.| **FlexCDN授权** | |
|
||||
| 37.| **Gcore** | Gcore |
|
||||
| 38.| **Github授权** | |
|
||||
| 39.| **godaddy授权** | |
|
||||
| 40.| **HiPM DNSMgr** | HiPM DNSMgr API Token 授权 |
|
||||
| 41.| **金山云授权** | |
|
||||
| 42.| **FTP授权** | |
|
||||
| 43.| **七牛OSS授权** | |
|
||||
| 44.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 |
|
||||
| 45.| **s3/minio授权** | S3/minio oss授权 |
|
||||
| 46.| **namesilo授权** | |
|
||||
| 47.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 |
|
||||
| 48.| **Nginx Proxy Manager 授权** | 用于登录 Nginx Proxy Manager,并为代理主机证书部署提供授权。 |
|
||||
| 49.| **1panel授权** | 账号和密码 |
|
||||
| 50.| **支付宝** | |
|
||||
| 51.| **白山云授权** | |
|
||||
| 52.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 |
|
||||
| 53.| **cdnfly授权** | |
|
||||
| 54.| **k8s授权** | |
|
||||
| 55.| **括彩云cdn授权** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 56.| **LeCDN授权** | |
|
||||
| 57.| **lucky** | |
|
||||
| 58.| **猫云授权** | |
|
||||
| 59.| **plesk授权** | |
|
||||
| 60.| **长亭雷池授权** | |
|
||||
| 61.| **群晖登录授权** | |
|
||||
| 62.| **uniCloud** | unicloud授权 |
|
||||
| 63.| **微信支付** | |
|
||||
| 64.| **易盾rcdn授权** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 65.| **易发云短信** | sms.yfyidc.cn/ |
|
||||
| 66.| **易盾DCDN授权** | https://user.yiduncdn.com |
|
||||
| 67.| **易支付** | |
|
||||
| 68.| **proxmox** | |
|
||||
| 69.| **Spaceship.com 授权** | Spaceship.com API 授权插件 |
|
||||
| 70.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器授权 |
|
||||
| 71.| **UCloud授权** | 优刻得授权 |
|
||||
| 72.| **又拍云** | |
|
||||
| 73.| **网宿授权** | |
|
||||
| 74.| **西部数码授权** | |
|
||||
| 75.| **我爱云授权** | 我爱云CDN |
|
||||
| 76.| **新网授权(代理方式)** | |
|
||||
| 77.| **新网授权** | |
|
||||
| 78.| **新网互联授权** | 仅支持代理账号,ip需要加入白名单 |
|
||||
| 79.| **Zenlayer授权** | Zenlayer授权 |
|
||||
| 80.| **GoEdge授权** | |
|
||||
| 81.| **雨云授权** | https://app.rainyun.com/ |
|
||||
|
||||
<style module>
|
||||
table th:first-of-type {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 任务插件
|
||||
共 `135` 款任务插件
|
||||
共 `139` 款任务插件
|
||||
## 1. 证书申请
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
@@ -31,26 +31,30 @@
|
||||
| 4.| **中国移动-部署证书到CDN** | 中国移动自动部署证书到CDN |
|
||||
| 5.| **多吉云-部署到多吉云CDN** | |
|
||||
| 6.| **farcdn-更新证书** | www.farcdn.net |
|
||||
| 7.| **FlexCDN-更新证书** | |
|
||||
| 8.| **Gcore-刷新Gcore证书** | 刷新现有的证书 |
|
||||
| 9.| **Gcore-部署证书到Gcore** | 仅上传 并不会部署到cdn |
|
||||
| 10.| **GoEdge-更新证书** | GoEdge |
|
||||
| 11.| **金山云-更新CDN证书** | 金山云自动更新CDN证书 |
|
||||
| 12.| **白山云-更新证书** | |
|
||||
| 13.| **cdnfly-部署证书到cdnfly** | cdnfly |
|
||||
| 14.| **天翼云-部署证书到CDN** | 部署证书到天翼云CDN和全站加速 |
|
||||
| 15.| **括彩云-部署到括彩云CDN** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 16.| **LeCDN-更新证书V2** | 支持新版本LeCDN |
|
||||
| 17.| **LeCDN-更新证书** | |
|
||||
| 18.| **Maoyun-更新猫云CDN证书** | |
|
||||
| 19.| **易盾-部署到易盾DCDN** | 主要是防御,http://user.yiduncdn.com/ |
|
||||
| 20.| **易盾-部署到易盾RCDN** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 21.| **雨云-更新证书** | app.rainyun.com |
|
||||
| 22.| **又拍云-部署证书到CDN/USS** | 支持又拍云CDN,又拍云云存储USS |
|
||||
| 23.| **网宿-更新证书** | 网宿证书自动更新 |
|
||||
| 24.| **西数-部署到虚拟主机** | 西部数码部署证书到虚拟主机 |
|
||||
| 25.| **我爱云-部署证书到我爱云CDN** | 部署证书到我爱云CDN |
|
||||
| 26.| **Zenlayer-刷新证书** | 刷新Zenlayer CDN证书 |
|
||||
| 7.| **Fastly-部署TLS激活** | 部署 Fastly 证书 (创建 TLS Activation 绑定证书到域名) |
|
||||
| 8.| **Fastly-清除缓存** | 清除 Fastly 指定 Service 的所有缓存 (Purge All) |
|
||||
| 9.| **Fastly-更新证书** | 自动更新 Fastly CDN 上的已有自定义证书 |
|
||||
| 10.| **Fastly-上传证书到Fastly** | 部署 / 上传 SSL/TLS 自定义证书到 Fastly CDN |
|
||||
| 11.| **FlexCDN-更新证书** | |
|
||||
| 12.| **Gcore-刷新Gcore证书** | 刷新现有的证书 |
|
||||
| 13.| **Gcore-部署证书到Gcore** | 仅上传 并不会部署到cdn |
|
||||
| 14.| **GoEdge-更新证书** | GoEdge |
|
||||
| 15.| **金山云-更新CDN证书** | 金山云自动更新CDN证书 |
|
||||
| 16.| **白山云-更新证书** | |
|
||||
| 17.| **cdnfly-部署证书到cdnfly** | cdnfly |
|
||||
| 18.| **天翼云-部署证书到CDN** | 部署证书到天翼云CDN和全站加速 |
|
||||
| 19.| **括彩云-部署到括彩云CDN** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 20.| **LeCDN-更新证书V2** | 支持新版本LeCDN |
|
||||
| 21.| **LeCDN-更新证书** | |
|
||||
| 22.| **Maoyun-更新猫云CDN证书** | |
|
||||
| 23.| **易盾-部署到易盾DCDN** | 主要是防御,http://user.yiduncdn.com/ |
|
||||
| 24.| **易盾-部署到易盾RCDN** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
|
||||
| 25.| **雨云-更新证书** | app.rainyun.com |
|
||||
| 26.| **又拍云-部署证书到CDN/USS** | 支持又拍云CDN,又拍云云存储USS |
|
||||
| 27.| **网宿-更新证书** | 网宿证书自动更新 |
|
||||
| 28.| **西数-部署到虚拟主机** | 西部数码部署证书到虚拟主机 |
|
||||
| 29.| **我爱云-部署证书到我爱云CDN** | 部署证书到我爱云CDN |
|
||||
| 30.| **Zenlayer-刷新证书** | 刷新Zenlayer CDN证书 |
|
||||
## 4. 面板
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
`Certd`默认启用https,监听7002端口
|
||||
如果你想关闭https,或者修改端口,可以在环境变量中配置
|
||||
```shell
|
||||
CERTD_HTTPS_ENABLE=true
|
||||
CERTD_HTTPS_port=7002
|
||||
certd_https_enabled=true
|
||||
certd_https_port=7002
|
||||
|
||||
```
|
||||
|
||||
|
||||
+1
-1
@@ -9,5 +9,5 @@
|
||||
}
|
||||
},
|
||||
"npmClient": "pnpm",
|
||||
"version": "1.44.0"
|
||||
"version": "1.45.0"
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
"dependencies": {
|
||||
"copyfiles": "^2.4.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"root": "link:",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"workspaces": [
|
||||
|
||||
@@ -3,6 +3,28 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/publishlab/node-acme-client/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 支持google persist校验方式 ([97d2b50](https://github.com/publishlab/node-acme-client/commit/97d2b5007409ec79ea864d437c473a2f9b5dfae4))
|
||||
|
||||
## [1.44.4](https://github.com/publishlab/node-acme-client/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.44.3](https://github.com/publishlab/node-acme-client/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.44.2](https://github.com/publishlab/node-acme-client/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
## [1.44.1](https://github.com/publishlab/node-acme-client/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/acme-client
|
||||
|
||||
# [1.44.0](https://github.com/publishlab/node-acme-client/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"description": "Simple and unopinionated ACME client",
|
||||
"private": false,
|
||||
"author": "nmorsman",
|
||||
"version": "1.44.0",
|
||||
"version": "1.45.0",
|
||||
"type": "module",
|
||||
"module": "./dist/index.js",
|
||||
"main": "./dist/index.js",
|
||||
@@ -18,7 +18,7 @@
|
||||
"types"
|
||||
],
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.44.0",
|
||||
"@certd/basic": "^1.45.0",
|
||||
"@peculiar/x509": "^1.11.0",
|
||||
"asn1js": "^3.0.5",
|
||||
"axios": "^1.9.0",
|
||||
@@ -27,6 +27,7 @@
|
||||
"https-proxy-agent": "^7.0.5",
|
||||
"lodash-es": "^4.17.21",
|
||||
"node-forge": "^1.3.1",
|
||||
"psl": "^1.15.0",
|
||||
"punycode.js": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -58,7 +59,7 @@
|
||||
"test": "mocha -t 60000 \"test/setup.js\" \"test/**/*.spec.js\"",
|
||||
"test:unit": "cross-env NODE_ENV=unittest mocha --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||
"pub": "npm publish",
|
||||
"compile": "rimraf dist && tsc --skipLibCheck --watch",
|
||||
"compile": " tsc --skipLibCheck --watch",
|
||||
"format": "prettier --write src"
|
||||
},
|
||||
"repository": {
|
||||
@@ -76,5 +77,5 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// @ts-ignore
|
||||
import psl from "psl";
|
||||
|
||||
export function getDnsPersistIssuer(directoryUrl?: string) {
|
||||
if (!directoryUrl) {
|
||||
return "letsencrypt.org";
|
||||
}
|
||||
|
||||
let directory: URL;
|
||||
try {
|
||||
directory = new URL(directoryUrl);
|
||||
} catch {
|
||||
throw new Error(`ACME Directory URL无效,无法生成DNS持久验证Issuer:${directoryUrl}`);
|
||||
}
|
||||
|
||||
const issuer = psl.get(directory.hostname);
|
||||
if (!issuer) {
|
||||
throw new Error(`无法从ACME Directory URL获取可注册主域名:${directory.hostname}`);
|
||||
}
|
||||
return issuer;
|
||||
}
|
||||
@@ -79,3 +79,4 @@ export * from './verify.js'
|
||||
export * from './error.js'
|
||||
|
||||
export * from './util.js'
|
||||
export * from './dns-persist.js'
|
||||
|
||||
@@ -3,6 +3,28 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **webhook:** 修复模版转义换行符,避免手动上传的证书报错 ([2735feb](https://github.com/certd/certd/commit/2735feb3302f8308708dafc8b295adcd4b91d7f0))
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/basic
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1 +1 @@
|
||||
00:39
|
||||
00:51
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/basic",
|
||||
"private": false,
|
||||
"version": "1.44.0",
|
||||
"version": "1.45.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -15,7 +15,7 @@
|
||||
"test": "mocha --loader=ts-node/esm",
|
||||
"test:unit": "cross-env NODE_ENV=unittest mocha --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||
"pub": "npm publish",
|
||||
"compile": "rimraf dist && tsc --skipLibCheck --watch",
|
||||
"compile": " tsc --skipLibCheck --watch",
|
||||
"format": "prettier --write src",
|
||||
"lint": "eslint --fix --ext .ts src"
|
||||
},
|
||||
@@ -54,5 +54,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export const stringUtils = {
|
||||
replaceTemplate(target: string, body: Record<string, unknown>, urlEncode = false) {
|
||||
let bodyStr = target;
|
||||
for (const key of Object.keys(body)) {
|
||||
const rawValue = body[key] == null ? "" : String(body[key]);
|
||||
let value = urlEncode ? encodeURIComponent(rawValue) : rawValue;
|
||||
value = value.replace(/[\u0000-\u001f]/g, char => {
|
||||
const escapes: Record<string, string> = { "\b": "\\b", "\f": "\\f", "\n": "\\n", "\r": "\\r", "\t": "\\t" };
|
||||
return escapes[char] ?? `\\u${char.charCodeAt(0).toString(16).padStart(4, "0")}`;
|
||||
});
|
||||
bodyStr = bodyStr.replaceAll(`\${${key}}`, value);
|
||||
bodyStr = bodyStr.replaceAll(`{${key}}`, value);
|
||||
}
|
||||
return bodyStr;
|
||||
},
|
||||
|
||||
maxLength(str?: string, length = 100) {
|
||||
if (str) {
|
||||
return str.length > length ? str.slice(0, length) + "..." : str;
|
||||
|
||||
@@ -3,6 +3,28 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 缺失运行时嵌套依赖时添加清除缓存提示 ([2d0b85b](https://github.com/certd/certd/commit/2d0b85b3054fc1aa82b3ab5db34a2a56308f71f2))
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/pipeline
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/pipeline",
|
||||
"private": false,
|
||||
"version": "1.44.0",
|
||||
"version": "1.45.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -16,13 +16,13 @@
|
||||
"test": "mocha --loader=ts-node/esm",
|
||||
"test:unit": "cross-env NODE_ENV=unittest mocha --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||
"pub": "npm publish",
|
||||
"compile": "rimraf dist && tsc --skipLibCheck --watch",
|
||||
"compile": "tsc --skipLibCheck --watch",
|
||||
"format": "prettier --write src",
|
||||
"lint": "eslint --fix --ext .ts src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.44.0",
|
||||
"@certd/plus-core": "^1.44.0",
|
||||
"@certd/basic": "^1.45.0",
|
||||
"@certd/plus-core": "^1.45.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"lodash-es": "^4.17.21",
|
||||
"reflect-metadata": "^0.2.2"
|
||||
@@ -51,5 +51,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
|
||||
}
|
||||
|
||||
@@ -64,6 +64,28 @@ describe("RuntimeDepsService", () => {
|
||||
assert.equal(mod.value, 42);
|
||||
});
|
||||
|
||||
it("logs a cache clearing hint when a nested runtime dependency is missing", async () => {
|
||||
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-missing-nested-"));
|
||||
const packageDir = path.join(rootDir, "node_modules", "runtime-only");
|
||||
fs.mkdirSync(packageDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(rootDir, "package.json"), JSON.stringify({ name: "runtime-root", type: "module" }), "utf8");
|
||||
fs.writeFileSync(path.join(packageDir, "package.json"), JSON.stringify({ name: "runtime-only", main: "index.js" }), "utf8");
|
||||
fs.writeFileSync(path.join(packageDir, "index.js"), "module.exports = require('missing-nested');\n", "utf8");
|
||||
const logs: string[] = [];
|
||||
const logger = {
|
||||
info() {},
|
||||
warn() {},
|
||||
error(message: string, ...args: any[]) {
|
||||
logs.push([message, ...args.map(item => item?.message || String(item))].join(" "));
|
||||
},
|
||||
};
|
||||
const service = new RuntimeDepsService({ rootDir }, null);
|
||||
|
||||
await assert.rejects(() => service.importRuntime("runtime-only", logger));
|
||||
assert.ok(logs.some(message => message.includes("Cannot find module 'missing-nested'")));
|
||||
assert.ok(logs.some(message => message.includes("插件管理页面->清除缓存依赖->重启certd")));
|
||||
});
|
||||
|
||||
it("installs configured lazy dependency when import target is missing", async () => {
|
||||
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-deps-lazy-"));
|
||||
const service = new RuntimeDepsService({ rootDir, lazyDependencies: { "lazy-pkg": "^1.2.3" } }, null);
|
||||
|
||||
@@ -251,6 +251,7 @@ export class RuntimeDepsService {
|
||||
}
|
||||
|
||||
let resolved: string;
|
||||
let fromRuntimeDeps = false;
|
||||
try {
|
||||
//尝试加载本项目依赖
|
||||
resolved = this.resolveProjectSpecifier(specifier).resolved;
|
||||
@@ -258,13 +259,22 @@ export class RuntimeDepsService {
|
||||
if (!this.isModuleNotFoundError(error)) {
|
||||
throw error;
|
||||
}
|
||||
fromRuntimeDeps = true;
|
||||
resolved = await this.resolveLazyOrInstallSpecifier(specifier, logger);
|
||||
}
|
||||
if (!resolved) {
|
||||
throw new Error(`依赖未安装成功: ${specifier}`);
|
||||
}
|
||||
|
||||
return await import(pathToFileURL(resolved).href);
|
||||
try {
|
||||
return await import(pathToFileURL(resolved).href);
|
||||
} catch (error: any) {
|
||||
if (fromRuntimeDeps && this.isModuleNotFoundError(error)) {
|
||||
logger.error?.("动态依赖加载失败:", error);
|
||||
logger.error?.("动态依赖加载失败,请前往插件管理页面->清除缓存依赖->重启certd后重试");
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//动态获取模块解析
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-huawei
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-huawei",
|
||||
"private": false,
|
||||
"version": "1.44.0",
|
||||
"version": "1.45.0",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
"types": "./dist/d/index.d.ts",
|
||||
@@ -13,7 +13,7 @@
|
||||
"preview": "vite preview",
|
||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||
"pub": "npm publish",
|
||||
"compile": "rimraf dist && npm run build",
|
||||
"compile": " npm run build",
|
||||
"format": "prettier --write src",
|
||||
"lint": "eslint --fix --ext .ts src"
|
||||
},
|
||||
@@ -31,5 +31,5 @@
|
||||
"prettier": "3.3.3",
|
||||
"tslib": "^2.8.1"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-iframe
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-iframe",
|
||||
"private": false,
|
||||
"version": "1.44.0",
|
||||
"version": "1.45.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -16,7 +16,7 @@
|
||||
"preview": "vite preview",
|
||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||
"pub": "npm publish",
|
||||
"compile": "rimraf dist && npm run build",
|
||||
"compile": " npm run build",
|
||||
"format": "prettier --write src",
|
||||
"lint": "eslint --fix --ext .ts src"
|
||||
},
|
||||
@@ -37,5 +37,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/jdcloud
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/jdcloud",
|
||||
"version": "1.44.0",
|
||||
"version": "1.45.0",
|
||||
"description": "jdcloud openApi sdk",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
@@ -63,5 +63,5 @@
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-k8s
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/lib-k8s",
|
||||
"private": false,
|
||||
"version": "1.44.0",
|
||||
"version": "1.45.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
@@ -16,12 +16,12 @@
|
||||
"preview": "vite preview",
|
||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||
"pub": "npm publish",
|
||||
"compile": "rimraf dist && tsc --skipLibCheck --watch",
|
||||
"compile": " tsc --skipLibCheck --watch",
|
||||
"format": "prettier --write src",
|
||||
"lint": "eslint --fix --ext .ts src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.44.0",
|
||||
"@certd/basic": "^1.45.0",
|
||||
"@kubernetes/client-node": "0.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -38,5 +38,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
|
||||
}
|
||||
|
||||
@@ -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.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
### Features
|
||||
|
||||
* **client:** 新增客户端管理功能 ([3dbbe72](https://github.com/certd/certd/commit/3dbbe72fc15c114ca6b5e8ff45cb21f258b80650))
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复证书插件配置无法打开的bug ([5563abb](https://github.com/certd/certd/commit/5563abb13a62140f4dee668ab768cf4ff9371ddb))
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/lib-server
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/lib-server",
|
||||
"version": "1.44.0",
|
||||
"version": "1.45.0",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -17,7 +17,7 @@
|
||||
"cov": "midway-bin cov --ts",
|
||||
"prepublish": "npm run build",
|
||||
"pub": "npm publish",
|
||||
"compile": "rimraf dist && tsc --skipLibCheck --watch",
|
||||
"compile": " tsc --skipLibCheck --watch",
|
||||
"format": "prettier --write src",
|
||||
"lint": "eslint --fix --ext .ts src"
|
||||
},
|
||||
@@ -29,11 +29,11 @@
|
||||
],
|
||||
"license": "AGPL",
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.44.0",
|
||||
"@certd/basic": "^1.44.0",
|
||||
"@certd/pipeline": "^1.44.0",
|
||||
"@certd/plugin-lib": "^1.44.0",
|
||||
"@certd/plus-core": "^1.44.0",
|
||||
"@certd/acme-client": "^1.45.0",
|
||||
"@certd/basic": "^1.45.0",
|
||||
"@certd/pipeline": "^1.45.0",
|
||||
"@certd/plugin-lib": "^1.45.0",
|
||||
"@certd/plus-core": "^1.45.0",
|
||||
"@midwayjs/cache": "3.14.0",
|
||||
"@midwayjs/core": "3.20.11",
|
||||
"@midwayjs/i18n": "3.20.13",
|
||||
@@ -69,5 +69,5 @@
|
||||
"typeorm": "^0.3.20",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
|
||||
}
|
||||
|
||||
@@ -75,7 +75,10 @@ export abstract class BaseService<T> {
|
||||
if (!id) {
|
||||
throw new ValidateException("id不能为空");
|
||||
}
|
||||
const info = await this.getRepository().findOneBy({ id } as any);
|
||||
const info = await this.getRepository().findOne({
|
||||
//@ts-ignore
|
||||
where: { id },
|
||||
});
|
||||
if (info && infoIgnoreProperty) {
|
||||
for (const property of infoIgnoreProperty) {
|
||||
delete info[property];
|
||||
@@ -114,6 +117,13 @@ export abstract class BaseService<T> {
|
||||
);
|
||||
}
|
||||
|
||||
async updateById(data: any) {
|
||||
if (!data.id) {
|
||||
throw new ValidateException("id 不能为空");
|
||||
}
|
||||
await this.addOrUpdate(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param ids 删除的ID集合 如:[1,2,3] 或者 1,2,3
|
||||
@@ -163,6 +173,7 @@ export abstract class BaseService<T> {
|
||||
const now = new Date();
|
||||
param.createTime = now;
|
||||
param.updateTime = now;
|
||||
delete param.id;
|
||||
await this.addOrUpdate(param);
|
||||
await this.modifyAfter(param);
|
||||
return {
|
||||
@@ -175,7 +186,9 @@ export abstract class BaseService<T> {
|
||||
* @param param 数据
|
||||
*/
|
||||
async update(param: any) {
|
||||
if (!param.id) throw new ValidateException("id 不能为空");
|
||||
if (!param.id) {
|
||||
throw new ValidateException("id 不能为空");
|
||||
}
|
||||
param.updateTime = new Date();
|
||||
await this.addOrUpdate(param);
|
||||
await this.modifyAfter(param);
|
||||
|
||||
@@ -133,12 +133,12 @@ export class SysPrivateSettings extends BaseSettings {
|
||||
// 内置项不在此配置 Directory URL:其端点由运行时按加密算法(pkType)通过 acme.getDirectoryUrl 获取,
|
||||
// 例如 ZeroSSL、SSL.com 的 RSA 与 EC 证书使用不同的端点,写死单一 URL 会导致 EC 申请走错端点。
|
||||
customAcmeProviders?: CustomAcmeProvider[] = [
|
||||
{ sslProvider: "letsencrypt", title: "Let's Encrypt", directoryUrl: "", needEAB: false, builtIn: true },
|
||||
{ sslProvider: "letsencrypt_staging", title: "Let's Encrypt测试环境", directoryUrl: "", needEAB: false, builtIn: true },
|
||||
{ sslProvider: "letsencrypt", title: "Let's Encrypt(支持7天IP证书)", directoryUrl: "", needEAB: false, builtIn: true },
|
||||
{ sslProvider: "google", title: "Google", directoryUrl: "", needEAB: true, builtIn: true },
|
||||
{ sslProvider: "zerossl", title: "ZeroSSL", directoryUrl: "", needEAB: true, builtIn: true },
|
||||
{ sslProvider: "litessl", title: "litessl", directoryUrl: "", needEAB: true, builtIn: true },
|
||||
{ sslProvider: "litessl", title: "Litessl", directoryUrl: "", needEAB: true, builtIn: true },
|
||||
{ sslProvider: "sslcom", title: "SSL.com", directoryUrl: "", needEAB: true, builtIn: true },
|
||||
{ sslProvider: "letsencrypt_staging", title: "Let's Encrypt测试环境(只能生成测试证书)", directoryUrl: "", needEAB: false, builtIn: true },
|
||||
];
|
||||
|
||||
removeSecret() {
|
||||
|
||||
@@ -174,11 +174,11 @@ export class AccessService extends BaseService<AccessEntity> {
|
||||
throw new ValidateException("userId不能为空");
|
||||
}
|
||||
if (userId !== entity.userId) {
|
||||
throw new PermissionException("您对该Access授权无访问权限");
|
||||
throw new PermissionException(`您对该Access授权${entity.name}(${entity.id})无访问权限`);
|
||||
}
|
||||
}
|
||||
if (projectId != null && projectId !== entity.projectId) {
|
||||
throw new PermissionException("您对该Access授权无访问权限");
|
||||
throw new PermissionException(`您对该Access授权${entity.name}(${entity.id})无访问权限`);
|
||||
}
|
||||
|
||||
// const access = accessRegistry.get(entity.type);
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/midway-flyway-js",
|
||||
"version": "1.44.0",
|
||||
"version": "1.45.0",
|
||||
"description": "midway with flyway, sql upgrade way ",
|
||||
"private": false,
|
||||
"type": "module",
|
||||
@@ -17,7 +17,7 @@
|
||||
"cov": "midway-bin cov --ts",
|
||||
"prepublish": "npm run build",
|
||||
"pub": "npm publish",
|
||||
"compile": "rimraf dist && npm run build",
|
||||
"compile": " npm run build",
|
||||
"format": "prettier --write src",
|
||||
"lint": "eslint --fix --ext .ts src"
|
||||
},
|
||||
@@ -52,5 +52,5 @@
|
||||
"typeorm": "^0.3.20",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-cert
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-cert",
|
||||
"private": false,
|
||||
"version": "1.44.0",
|
||||
"version": "1.45.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -20,7 +20,7 @@
|
||||
"lint": "eslint --fix --ext .ts src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/plugin-lib": "^1.44.0"
|
||||
"@certd/plugin-lib": "^1.45.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.3.12",
|
||||
@@ -38,5 +38,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
**Note:** Version bump only for package @certd/plugin-lib
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/plugin-lib",
|
||||
"private": false,
|
||||
"version": "1.44.0",
|
||||
"version": "1.45.0",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
@@ -12,14 +12,14 @@
|
||||
"dev-build": "npm run build",
|
||||
"test:unit": "cross-env NODE_ENV=unittest mocha",
|
||||
"pub": "npm publish",
|
||||
"compile": "rimraf dist && tsc --skipLibCheck --watch",
|
||||
"compile": " tsc --skipLibCheck --watch",
|
||||
"format": "prettier --write src",
|
||||
"lint": "eslint --fix --ext .ts src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.44.0",
|
||||
"@certd/basic": "^1.44.0",
|
||||
"@certd/pipeline": "^1.44.0",
|
||||
"@certd/acme-client": "^1.45.0",
|
||||
"@certd/basic": "^1.45.0",
|
||||
"@certd/pipeline": "^1.45.0",
|
||||
"dayjs": "^1.11.7",
|
||||
"jszip": "^3.10.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
@@ -45,5 +45,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "fcbf1045fe3806fa1a0bebf5038a6f67aec283d0"
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ export function createRemoteSelectInputDefine(opts?: {
|
||||
rules?: any;
|
||||
mergeScript?: string;
|
||||
search?: boolean;
|
||||
emitImmediate?: boolean;
|
||||
pager?: boolean;
|
||||
component?: any;
|
||||
value?: any;
|
||||
|
||||
@@ -3,6 +3,53 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.45.0](https://github.com/certd/certd/compare/v1.44.4...v1.45.0) (2026-10-02)
|
||||
|
||||
### Features
|
||||
|
||||
* **client:** 新增客户端管理功能 ([3dbbe72](https://github.com/certd/certd/commit/3dbbe72fc15c114ca6b5e8ff45cb21f258b80650))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **monitor:** 站点证书监控支持手动选中触发检查 ([f60e63f](https://github.com/certd/certd/commit/f60e63fbaf00460d5050cd7705bd75d57dec202d))
|
||||
* **open-api:** openkey增加用途备注 ([bbeaf61](https://github.com/certd/certd/commit/bbeaf617d19750515f6eca51863e74e23bc1de50))
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复添加步骤会修改所有步骤标题的bug ([51e83e4](https://github.com/certd/certd/commit/51e83e4c38f7f319314fdc38ef33cf5720b2dde0))
|
||||
* 修复证书插件配置无法打开的bug ([5563abb](https://github.com/certd/certd/commit/5563abb13a62140f4dee668ab768cf4ff9371ddb))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 新增余额管理,管理员充值功能 ([bded368](https://github.com/certd/certd/commit/bded368cbb8974b6937ae260e448c25fae5c33ad))
|
||||
* **ui:** 商业版套餐购买不要求设置支付方式 ([e971145](https://github.com/certd/certd/commit/e9711456860d7db40914a0c295534f7bca56f83c))
|
||||
|
||||
## [1.44.3](https://github.com/certd/certd/compare/v1.44.2...v1.44.3) (2026-09-06)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **plugin:** 修复插件配置保存与加载的相关问题 ([bd94334](https://github.com/certd/certd/commit/bd9433478a43d5d64ce6370386d0fe0aaff07d2e))
|
||||
|
||||
## [1.44.2](https://github.com/certd/certd/compare/v1.44.1...v1.44.2) (2026-09-03)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复插件市场插件安装无效的bug ([15d2bd8](https://github.com/certd/certd/commit/15d2bd8484a9ddd142bf4ef03d839c74405f9e6d))
|
||||
|
||||
## [1.44.1](https://github.com/certd/certd/compare/v1.44.0...v1.44.1) (2026-09-02)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* 修复编辑证书申请任务导致清空ACME账号的bug ([d67379f](https://github.com/certd/certd/commit/d67379f29725bbd6dcdefe97d101023c7fd11f03))
|
||||
* 修复大量问题 ([f911e06](https://github.com/certd/certd/commit/f911e06a698d568ae228880ec89b7e25fdd1305b))
|
||||
* **pipeline:** 修正流水线保存时的ID判断逻辑并增加参数校验 ([6af12d4](https://github.com/certd/certd/commit/6af12d49fce1144943dc37ba574b3c9356998dcd))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **dashboard:** 新增仪表盘站点监控和域名管理的统计卡片 ([9842a24](https://github.com/certd/certd/commit/9842a24a50a841f710ee7c8050a38e3334c58394))
|
||||
|
||||
# [1.44.0](https://github.com/certd/certd/compare/v1.43.0...v1.44.0) (2026-08-28)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-client",
|
||||
"version": "1.44.0",
|
||||
"version": "1.45.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite --open",
|
||||
@@ -104,8 +104,8 @@
|
||||
"zod-defaults": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@certd/lib-iframe": "^1.44.0",
|
||||
"@certd/pipeline": "^1.44.0",
|
||||
"@certd/lib-iframe": "^1.45.0",
|
||||
"@certd/pipeline": "^1.45.0",
|
||||
"@rollup/plugin-commonjs": "^25.0.7",
|
||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||
"@types/chai": "^4.3.12",
|
||||
|
||||
+1
-1
@@ -399,7 +399,7 @@ watch(
|
||||
width: 80px;
|
||||
}
|
||||
.input {
|
||||
width: 150px;
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ const props = defineProps<
|
||||
pager?: boolean;
|
||||
single?: boolean;
|
||||
pageSize?: number;
|
||||
emitImmediate?: boolean;
|
||||
uploadCert?: UploadCertProps;
|
||||
} & ComponentPropsType
|
||||
>();
|
||||
@@ -210,8 +211,9 @@ const getOptions = async () => {
|
||||
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
if (props.value) {
|
||||
if (props.value && props.emitImmediate) {
|
||||
emitSelectedChange(props.value);
|
||||
emit("update:value", props.value);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import sysauthority from "./certd/sys-authority";
|
||||
import syscname from "./certd/sys-cname";
|
||||
import tutorial from "./certd/tutorial";
|
||||
import cron from "./certd/cron";
|
||||
import client from "./certd/client";
|
||||
|
||||
// Note: @ is reserved in locale messages; use {'@'} when needed.
|
||||
export default {
|
||||
@@ -43,4 +44,5 @@ export default {
|
||||
...syscname,
|
||||
...tutorial,
|
||||
...cron,
|
||||
...client,
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ export default {
|
||||
subdomainConfirmContent: "{domain} appears to be a subdomain. Only delegated subdomains and free second-level subdomains need to be maintained here. Otherwise certificate application may fail. Continue?",
|
||||
importFromProvider: "Import from Domain Provider",
|
||||
importFromResolveRecords: "Import from DNS Records",
|
||||
importFromResolveRecordsTooltip: "Import DNS records as monitoring sites",
|
||||
syncExpirationDate: "Sync Domain Expiration Time",
|
||||
syncTaskSubmitted: "Sync task submitted",
|
||||
syncExpirationProgress: "Sync Domain Expiration Progress",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
export default {
|
||||
clientManager: "Client Manager",
|
||||
client: {
|
||||
title: "Client Manager",
|
||||
description: "View online status and managed site count of connected Certd clients. Clients report a heartbeat every 10 minutes and are marked offline if no heartbeat arrives for 30 minutes.",
|
||||
plusRequired: "This feature requires the Pro edition",
|
||||
machineName: "Machine",
|
||||
clientId: "Client ID",
|
||||
version: "Version",
|
||||
os: "OS",
|
||||
status: "Status",
|
||||
online: "Online",
|
||||
offline: "Offline",
|
||||
siteCount: "Sites",
|
||||
httpsSiteCount: "HTTPS Sites",
|
||||
syncedSiteCount: "Synced",
|
||||
failedSiteCount: "Failed",
|
||||
lastHeartbeatAt: "Last Heartbeat",
|
||||
},
|
||||
};
|
||||
@@ -14,6 +14,15 @@ export default {
|
||||
certCount: "Number of Certificates",
|
||||
noCert: "You have no certificates yet",
|
||||
manageCert: "View Certificates",
|
||||
siteMonitorCount: "Number of Monitored Sites",
|
||||
siteAbnormalCount: "Abnormal",
|
||||
siteNormalCount: "Normal",
|
||||
manageSiteMonitor: "Manage Monitored Sites",
|
||||
domainCount: "Number of Domains",
|
||||
domainExpiringCount: "Expiring",
|
||||
domainExpiredCount: "Expired",
|
||||
domainNotExpiredCount: "Not Expired",
|
||||
manageDomain: "Manage Domains",
|
||||
certExpiringCount: "Soon-to-Expire",
|
||||
certExpiredCount: "Expired",
|
||||
certNoExpireCount: "Not Expired",
|
||||
@@ -23,6 +32,16 @@ export default {
|
||||
recentRun: "Recent Run Statistics",
|
||||
runCount: "Run Count",
|
||||
expiringCerts: "Soon-to-Expire Certificates",
|
||||
totalGeneratedCertCount: "Total Certificates Generated",
|
||||
singleDomainCertShortCount: "Single-domain",
|
||||
multiDomainCertShortCount: "Multi-domain",
|
||||
wildcardCertShortCount: "Wildcard",
|
||||
singleDomainCertCount: "Single-domain Certificates Generated",
|
||||
multiDomainCertCount: "Multi-domain Certificates Generated",
|
||||
wildcardCertCount: "Wildcard Certificates Generated",
|
||||
savedAmount: "Estimated savings",
|
||||
savedAmountRule: "Estimated at one quarter of Tencent Cloud's annual certificate prices: single-domain ¥532, multi-domain ¥912, and wildcard ¥2147. Actual savings may vary.",
|
||||
totalPipelineRuns: "Total Pipeline Runs",
|
||||
supportedTasks: "Overview of Supported Deployment Tasks",
|
||||
changeLog: "Change Log",
|
||||
stableRelease: "Stable",
|
||||
|
||||
@@ -53,6 +53,8 @@ export default {
|
||||
suiteSetting: "Suite Settings",
|
||||
orderManager: "Order Management",
|
||||
userSuites: "User Suites",
|
||||
balanceManager: "Balance Management",
|
||||
balanceLog: "Balance Change Records",
|
||||
inviteCommissionSetting: "Incentive Plan Settings",
|
||||
inviteLevel: "Promotion Levels",
|
||||
inviteUserLevel: "User Promotion Levels",
|
||||
|
||||
@@ -3,6 +3,9 @@ export default {
|
||||
scopeOpenApiOnly: "Open API Only",
|
||||
scopeFullAccount: "Full Account Permissions",
|
||||
scopeHelper: "Open API only allows access to open APIs; full account permissions allow access to all APIs",
|
||||
keyRemark: "Key Purpose",
|
||||
keyRemarkHelper: "Describe the purpose of this key for easier identification",
|
||||
keyRemarkMaxChars: "Maximum 512 characters",
|
||||
gen: {
|
||||
text: "API Test",
|
||||
title: "x-certd-token",
|
||||
|
||||
@@ -7,6 +7,8 @@ export default {
|
||||
settingLink: "Site Monitoring Settings",
|
||||
limitInfo: "Basic edition limited to 1, professional and above unlimited, current",
|
||||
checkAll: "Check All",
|
||||
checkBatch: "Check Selected",
|
||||
batchCheckConfirm: "Check the {count} selected sites now?",
|
||||
checkNow: "Check Now",
|
||||
batchDelete: "Batch Delete",
|
||||
confirmTitle: "Confirm",
|
||||
|
||||
@@ -19,6 +19,7 @@ import sysauthority from "./certd/sys-authority";
|
||||
import syscname from "./certd/sys-cname";
|
||||
import tutorial from "./certd/tutorial";
|
||||
import cron from "./certd/cron";
|
||||
import client from "./certd/client";
|
||||
|
||||
// Note: @ is reserved in locale messages; use {'@'} when needed.
|
||||
export default {
|
||||
@@ -43,4 +44,5 @@ export default {
|
||||
...syscname,
|
||||
...tutorial,
|
||||
...cron,
|
||||
...client,
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ export default {
|
||||
subdomainConfirmContent: "检测到{domain}为子域名,只有托管子域名和免费二级子域名才需要在此处维护,否则会导致申请证书失败,请确认是否继续?",
|
||||
importFromProvider: "从域名提供商导入",
|
||||
importFromResolveRecords: "从解析记录导入",
|
||||
importFromResolveRecordsTooltip: "从域名的解析记录中导入并作为监控站点",
|
||||
syncExpirationDate: "同步域名过期时间",
|
||||
syncTaskSubmitted: "同步任务已提交",
|
||||
syncExpirationProgress: "同步域名过期时间进度",
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
export default {
|
||||
clientManager: "客户端管理",
|
||||
client: {
|
||||
title: "客户端管理",
|
||||
description: "查看已接入的 Certd 客户端在线状态与管理的站点数量;客户端每 10 分钟上报一次心跳,超过 30 分钟未上报则标记为离线",
|
||||
plusRequired: "此功能需要专业版",
|
||||
machineName: "机器名",
|
||||
clientId: "客户端ID",
|
||||
version: "版本",
|
||||
os: "系统",
|
||||
status: "在线状态",
|
||||
online: "在线",
|
||||
offline: "离线",
|
||||
siteCount: "站点数",
|
||||
httpsSiteCount: "HTTPS站点",
|
||||
syncedSiteCount: "已同步",
|
||||
failedSiteCount: "异常",
|
||||
lastHeartbeatAt: "上次心跳",
|
||||
},
|
||||
};
|
||||
@@ -17,6 +17,15 @@ export default {
|
||||
certNoExpireCount: "未过期",
|
||||
noCert: "还没有证书",
|
||||
manageCert: "查看证书",
|
||||
siteMonitorCount: "监控站点数量",
|
||||
siteAbnormalCount: "异常",
|
||||
siteNormalCount: "正常",
|
||||
manageSiteMonitor: "管理监控站点",
|
||||
domainCount: "域名数量",
|
||||
domainExpiringCount: "即将到期",
|
||||
domainExpiredCount: "已过期",
|
||||
domainNotExpiredCount: "未到期",
|
||||
manageDomain: "管理域名",
|
||||
|
||||
createNow: "立即创建",
|
||||
managePipeline: "管理流水线",
|
||||
@@ -24,6 +33,16 @@ export default {
|
||||
recentRun: "最近运行统计",
|
||||
runCount: "运行次数",
|
||||
expiringCerts: "最快到期证书",
|
||||
totalGeneratedCertCount: "累计生成证书数量",
|
||||
singleDomainCertShortCount: "单域名",
|
||||
multiDomainCertShortCount: "多域名",
|
||||
wildcardCertShortCount: "泛域名",
|
||||
singleDomainCertCount: "累计单域名证书数量",
|
||||
multiDomainCertCount: "累计多域名证书数量",
|
||||
wildcardCertCount: "累计泛域名证书数量",
|
||||
savedAmount: "已为您累计节省",
|
||||
savedAmountRule: "按腾讯云证书平台一年期单价的 1/4 进行估算。实际节省金额仅供参考。(腾讯云单域名证书 532 元,多域名证书 912 元,泛域名证书 2147 元)",
|
||||
totalPipelineRuns: "累计流水线运行次数",
|
||||
supportedTasks: "已支持的部署任务总览",
|
||||
changeLog: "更新日志",
|
||||
stableRelease: "稳定版",
|
||||
|
||||
@@ -54,6 +54,8 @@ export default {
|
||||
suiteSetting: "套餐设置",
|
||||
orderManager: "订单管理",
|
||||
userSuites: "用户套餐",
|
||||
balanceManager: "余额管理",
|
||||
balanceLog: "余额变更记录",
|
||||
inviteCommissionSetting: "激励计划设置",
|
||||
inviteLevel: "推广等级",
|
||||
inviteUserLevel: "用户推广等级",
|
||||
|
||||
@@ -3,6 +3,9 @@ export default {
|
||||
scopeOpenApiOnly: "仅开放接口",
|
||||
scopeFullAccount: "账户所有权限",
|
||||
scopeHelper: "仅开放接口只可以访问开放接口,账户所有权限可以访问所有接口",
|
||||
keyRemark: "密钥用途",
|
||||
keyRemarkHelper: "说明该密钥的用途,便于区分和查找",
|
||||
keyRemarkMaxChars: "最大512个字符",
|
||||
gen: {
|
||||
text: "接口测试",
|
||||
title: "x-certd-token",
|
||||
|
||||
@@ -7,6 +7,8 @@ export default {
|
||||
settingLink: "站点监控设置",
|
||||
limitInfo: "基础版限制1条,专业版以上无限制,当前",
|
||||
checkAll: "检查全部",
|
||||
checkBatch: "检查选中",
|
||||
batchCheckConfirm: "确认触发检查选中的{count}个站点吗?",
|
||||
checkNow: "立即检查",
|
||||
batchDelete: "批量删除",
|
||||
confirmTitle: "确认",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useProjectStore } from "/@/store/project";
|
||||
import { useProjectStore } from "/@/store/project";
|
||||
import { useSettingStore } from "/@/store/settings";
|
||||
|
||||
export const certdResources = [
|
||||
@@ -128,6 +128,17 @@ export const certdResources = [
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "certd.clientManager",
|
||||
name: "ClientManager",
|
||||
path: "/cert/client",
|
||||
component: "/certd/client/index.vue",
|
||||
meta: {
|
||||
icon: "ion:desktop-outline",
|
||||
auth: true,
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "certd.sysResources.currentProject",
|
||||
name: "ProjectMemberManager",
|
||||
|
||||
@@ -317,6 +317,34 @@ export const sysResources = [
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "certd.sysResources.balanceManager",
|
||||
name: "SysWallet",
|
||||
path: "/sys/suite/wallet",
|
||||
component: "/sys/suite/wallet/index.vue",
|
||||
meta: {
|
||||
show: () => {
|
||||
const settingStore = useSettingStore();
|
||||
return settingStore.isComm;
|
||||
},
|
||||
icon: "ion:wallet-outline",
|
||||
permission: "sys:settings:edit",
|
||||
auth: true,
|
||||
keepAlive: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "certd.sysResources.balanceLog",
|
||||
name: "SysWalletLog",
|
||||
path: "/sys/suite/wallet-log",
|
||||
component: "/sys/suite/wallet/log/index.vue",
|
||||
meta: {
|
||||
isMenu: false,
|
||||
permission: "sys:settings:view",
|
||||
auth: true,
|
||||
keepAlive: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "certd.sysResources.inviteCommissionSetting",
|
||||
name: "SysInviteCommissionSetting",
|
||||
|
||||
@@ -1,13 +1,62 @@
|
||||
import { DynamicType, FormItemProps, useMerge } from "@fast-crud/fast-crud";
|
||||
import { merge, set } from "lodash-es";
|
||||
import { defineStore } from "pinia";
|
||||
import * as api from "./api.plugin";
|
||||
import { DynamicType, FormItemProps, useMerge } from "@fast-crud/fast-crud";
|
||||
import { i18n } from "/src/locales/i18n";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
interface PluginState {
|
||||
group?: PluginGroups;
|
||||
originGroup?: PluginGroups;
|
||||
}
|
||||
|
||||
export function fillPluginDefine(plugin: any) {
|
||||
const columns = plugin.input;
|
||||
for (const key in columns) {
|
||||
const column = columns[key];
|
||||
if (!column.component) {
|
||||
column.component = {
|
||||
name: "a-input",
|
||||
vModel: "value",
|
||||
};
|
||||
} else {
|
||||
if (column.component.vModel == null && column.component.name?.startsWith("a-")) {
|
||||
column.component.vModel = "value";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin.sysSetting?.metadata) {
|
||||
merge(plugin.input, plugin.sysSetting.metadata?.input || {});
|
||||
const keys = Object.keys(plugin.input);
|
||||
for (const key of keys) {
|
||||
//修复旧版本错误引入的key导致报错的问题 【多引入进了一个type=CertApply】
|
||||
const column = plugin.input[key];
|
||||
if (typeof column != "object") {
|
||||
delete plugin.input[key];
|
||||
}
|
||||
}
|
||||
// 应用选项映射
|
||||
for (const key of Object.keys(plugin.input)) {
|
||||
const inputDef = plugin.input[key];
|
||||
if (inputDef.optionsMapping && inputDef.component?.options) {
|
||||
const mapping = inputDef.optionsMapping;
|
||||
for (const opt of inputDef.component.options) {
|
||||
if (mapping[opt.value] !== undefined) {
|
||||
opt.label = mapping[opt.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (plugin.sysSetting?.input) {
|
||||
// // 设置系统默认值, 不能放这里设置
|
||||
// for (const key in plugin.input) {
|
||||
// const sysValue = plugin.sysSetting?.input[key];
|
||||
// if (sysValue != undefined) {
|
||||
// set(plugin, `input.${key}.value`, sysValue);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
export type PluginGroup = {
|
||||
key: string;
|
||||
title: string;
|
||||
@@ -28,6 +77,7 @@ export type PluginDefine = {
|
||||
output: {
|
||||
[key: string]: any;
|
||||
};
|
||||
type?: string;
|
||||
};
|
||||
|
||||
export class PluginGroups {
|
||||
@@ -35,10 +85,9 @@ export class PluginGroups {
|
||||
map!: { [key: string]: PluginDefine };
|
||||
t: any;
|
||||
mergeSetting?: boolean;
|
||||
constructor(groups: { [key: string]: PluginGroup }, opts?: { mergeSetting?: boolean }) {
|
||||
constructor(groups: { [key: string]: PluginGroup }) {
|
||||
this.groups = groups;
|
||||
this.t = i18n.global.t;
|
||||
this.mergeSetting = opts?.mergeSetting ?? false;
|
||||
this.initGroup(groups);
|
||||
this.initMap();
|
||||
}
|
||||
@@ -53,26 +102,9 @@ export class PluginGroups {
|
||||
icon: "material-symbols:border-all-rounded",
|
||||
};
|
||||
for (const key in groups) {
|
||||
if (this.mergeSetting) {
|
||||
for (const plugin of groups[key].plugins) {
|
||||
if (plugin.sysSetting) {
|
||||
merge(plugin.input, plugin.sysSetting.metadata?.input || {});
|
||||
// 应用选项映射
|
||||
for (const key of Object.keys(plugin.input)) {
|
||||
const inputDef = plugin.input[key];
|
||||
if (inputDef.optionsMapping && inputDef.component?.options) {
|
||||
const mapping = inputDef.optionsMapping;
|
||||
for (const opt of inputDef.component.options) {
|
||||
if (mapping[opt.value] !== undefined) {
|
||||
opt.label = mapping[opt.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const plugin of groups[key].plugins) {
|
||||
fillPluginDefine(plugin);
|
||||
}
|
||||
|
||||
all.plugins.push(...groups[key].plugins);
|
||||
}
|
||||
this.groups = {
|
||||
@@ -173,16 +205,12 @@ export const usePluginStore = defineStore({
|
||||
id: "app.plugin",
|
||||
state: (): PluginState => ({
|
||||
group: null,
|
||||
originGroup: null,
|
||||
}),
|
||||
actions: {
|
||||
async reload() {
|
||||
const groups = await api.GetGroups({});
|
||||
const installedGroups = filterInstalledPluginGroups(groups);
|
||||
this.group = new PluginGroups(installedGroups, { mergeSetting: true });
|
||||
this.originGroup = new PluginGroups(cloneDeep(installedGroups));
|
||||
this.group = new PluginGroups(groups);
|
||||
console.log("group", this.group);
|
||||
console.log("originGroup", this.originGroup);
|
||||
},
|
||||
async init() {
|
||||
if (!this.group) {
|
||||
@@ -206,10 +234,6 @@ export const usePluginStore = defineStore({
|
||||
await this.init();
|
||||
return this.group.get(name);
|
||||
},
|
||||
async getPluginDefineFromOrigin(name: string): Promise<PluginDefine> {
|
||||
await this.init();
|
||||
return this.originGroup.get(name);
|
||||
},
|
||||
async getPluginConfig(query: any) {
|
||||
return await api.GetPluginConfig(query);
|
||||
},
|
||||
|
||||
@@ -24,7 +24,8 @@ export const useProjectStore = defineStore("app.project", () => {
|
||||
}
|
||||
const userStore = useUserStore();
|
||||
const userId = userStore.getUserInfo?.id;
|
||||
const lastProjectIdCacheKey = "currentProjectId:" + userId;
|
||||
const settingStore = useSettingStore();
|
||||
const lastProjectIdCacheKey = `currentProjectId:${userId}`;
|
||||
const lastProjectId = LocalStorage.get(lastProjectIdCacheKey);
|
||||
currentProjectId.value = lastProjectId;
|
||||
|
||||
@@ -45,7 +46,6 @@ export const useProjectStore = defineStore("app.project", () => {
|
||||
return null;
|
||||
});
|
||||
|
||||
const settingStore = useSettingStore();
|
||||
const isEnterprise = computed(() => {
|
||||
return settingStore.isEnterprise;
|
||||
});
|
||||
|
||||
@@ -390,6 +390,9 @@ export const useSettingStore = defineStore({
|
||||
this.loadProductInfo();
|
||||
this.inited = true;
|
||||
},
|
||||
getSiteId() {
|
||||
return this.installInfo?.siteId || "";
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -280,6 +280,8 @@ button.ant-btn.isPlus {
|
||||
align-items: center;
|
||||
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
&.font-size-16 {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
@@ -51,10 +51,11 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
rowHandle: {
|
||||
width: 120,
|
||||
fixed: "right",
|
||||
show: false,
|
||||
buttons: {
|
||||
view: { show: false },
|
||||
edit: { show: false },
|
||||
remove: { show: true },
|
||||
remove: { show: false },
|
||||
copy: { show: false },
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<template #header>
|
||||
<div class="title">
|
||||
DNS持久验证记录
|
||||
<span class="red sub" style="color: red">当前仅 Let's Encrypt 测试环境可以申请 DNS 持久验证证书。</span>
|
||||
<span class="red sub" style="color: red">当前仅 Google证书 可以申请 DNS 持久验证。</span>
|
||||
</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"></fs-crud>
|
||||
|
||||
@@ -235,7 +235,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
} else if (value === "dns-persist") {
|
||||
Modal.confirm({
|
||||
title: "请前往DNS持久验证记录页面添加记录",
|
||||
content: "DNS持久验证需要先配置ACME账号和_validation-persist持久TXT记录,续期时不再增删DNS记录;当前仅 Let's Encrypt 测试环境可以申请。",
|
||||
content: "DNS持久验证需要先配置ACME账号和_validation-persist持久TXT记录,续期时不再增删DNS记录;当前仅 Google证书 可以申请。",
|
||||
async onOk() {
|
||||
router.push({
|
||||
path: "/cert/cert/dns-persist",
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { request } from "/src/api/service";
|
||||
|
||||
const apiPrefix = "/client";
|
||||
|
||||
export const clientApi = {
|
||||
async GetList(query: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query,
|
||||
});
|
||||
},
|
||||
|
||||
async GetObj(id: number) {
|
||||
return await request({
|
||||
url: apiPrefix + "/info",
|
||||
method: "post",
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
|
||||
async DelObj(id: number) {
|
||||
return await request({
|
||||
url: apiPrefix + "/delete",
|
||||
method: "post",
|
||||
params: { id },
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,243 @@
|
||||
// @ts-ignore
|
||||
import { AddReq, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
import dayjs from "dayjs";
|
||||
import { useDicts } from "../dicts";
|
||||
import { useProjectStore } from "/@/store/project";
|
||||
import { useI18n } from "/src/locales";
|
||||
import { clientApi } from "./api";
|
||||
|
||||
export default function ({ crudExpose, context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const { t } = useI18n();
|
||||
const api = clientApi;
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
const delRequest = async (req: DelReq) => {
|
||||
const { row } = req;
|
||||
return await api.DelObj(row.id);
|
||||
};
|
||||
const editRequest = async (req: EditReq) => {};
|
||||
const addRequest = async (req: AddReq) => {};
|
||||
|
||||
const { myProjectDict } = useDicts();
|
||||
const projectStore = useProjectStore();
|
||||
|
||||
return {
|
||||
id: "clientCrud",
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
addRequest,
|
||||
editRequest,
|
||||
delRequest,
|
||||
},
|
||||
pagination: {
|
||||
pageSizeOptions: ["10", "20", "50", "100", "200"],
|
||||
},
|
||||
actionbar: {
|
||||
buttons: {
|
||||
add: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
rowHandle: {
|
||||
fixed: "right",
|
||||
width: 120,
|
||||
buttons: {
|
||||
view: {
|
||||
show: false,
|
||||
},
|
||||
edit: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
search: {
|
||||
initialForm: {
|
||||
...projectStore.getSearchForm(),
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
key: "id",
|
||||
type: "number",
|
||||
search: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 70,
|
||||
align: "center",
|
||||
},
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
machineName: {
|
||||
title: t("certd.client.machineName"),
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: "text",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 180,
|
||||
ellipsis: true,
|
||||
},
|
||||
},
|
||||
clientId: {
|
||||
title: t("certd.client.clientId"),
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
type: "text",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 220,
|
||||
ellipsis: true,
|
||||
},
|
||||
},
|
||||
version: {
|
||||
title: t("certd.client.version"),
|
||||
search: {
|
||||
show: false,
|
||||
},
|
||||
type: "text",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 90,
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
os: {
|
||||
title: t("certd.client.os"),
|
||||
search: {
|
||||
show: false,
|
||||
},
|
||||
type: "text",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 100,
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
online: {
|
||||
title: t("certd.client.status"),
|
||||
search: {
|
||||
show: false,
|
||||
},
|
||||
type: "text",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 100,
|
||||
align: "center",
|
||||
cellRender({ value, row }) {
|
||||
const online = value === true;
|
||||
const heartbeat = row.lastHeartbeatAt ? dayjs(row.lastHeartbeatAt).format("YYYY-MM-DD HH:mm:ss") : "";
|
||||
return (
|
||||
<a-tooltip title={heartbeat}>
|
||||
<a-tag color={online ? "green" : "red"}>{online ? t("certd.client.online") : t("certd.client.offline")}</a-tag>
|
||||
</a-tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
},
|
||||
siteCount: {
|
||||
title: t("certd.client.siteCount"),
|
||||
search: {
|
||||
show: false,
|
||||
},
|
||||
type: "number",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 90,
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
httpsSiteCount: {
|
||||
title: t("certd.client.httpsSiteCount"),
|
||||
search: {
|
||||
show: false,
|
||||
},
|
||||
type: "number",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 110,
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
syncedSiteCount: {
|
||||
title: t("certd.client.syncedSiteCount"),
|
||||
search: {
|
||||
show: false,
|
||||
},
|
||||
type: "number",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 90,
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
failedSiteCount: {
|
||||
title: t("certd.client.failedSiteCount"),
|
||||
search: {
|
||||
show: false,
|
||||
},
|
||||
type: "number",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 80,
|
||||
align: "center",
|
||||
},
|
||||
},
|
||||
lastHeartbeatAt: {
|
||||
title: t("certd.client.lastHeartbeatAt"),
|
||||
search: {
|
||||
show: false,
|
||||
},
|
||||
type: "text",
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
column: {
|
||||
width: 170,
|
||||
sorter: true,
|
||||
cellRender({ value }) {
|
||||
if (!value) {
|
||||
return "-";
|
||||
}
|
||||
return dayjs(value).format("YYYY-MM-DD HH:mm:ss");
|
||||
},
|
||||
},
|
||||
},
|
||||
projectId: {
|
||||
title: t("certd.fields.projectName"),
|
||||
type: "dict-select",
|
||||
dict: myProjectDict,
|
||||
form: {
|
||||
show: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title flex items-center">
|
||||
{{ t("certd.client.title") }}
|
||||
<div class="sub flex items-center flex-1">
|
||||
<div>
|
||||
{{ t("certd.client.description") }}
|
||||
</div>
|
||||
<vip-button class="ml-5" mode="button"></vip-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"></fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
import { useSettingStore } from "/@/store/settings";
|
||||
import { useI18n } from "/src/locales";
|
||||
const { t } = useI18n();
|
||||
const settingStore = useSettingStore();
|
||||
defineOptions({
|
||||
name: "ClientManager",
|
||||
});
|
||||
const context: any = {
|
||||
permission: {
|
||||
isProjectPermission: true,
|
||||
},
|
||||
};
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
|
||||
|
||||
useMounted(() => crudExpose.doRefresh());
|
||||
</script>
|
||||
@@ -63,6 +63,13 @@ export const siteInfoApi = {
|
||||
method: "post",
|
||||
});
|
||||
},
|
||||
async CheckBatch(ids: number[]) {
|
||||
return await request({
|
||||
url: apiPrefix + "/checkBatch",
|
||||
method: "post",
|
||||
data: { ids },
|
||||
});
|
||||
},
|
||||
|
||||
async Import(form: any) {
|
||||
return await request({
|
||||
|
||||
@@ -100,6 +100,23 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
});
|
||||
}
|
||||
|
||||
function checkBatch() {
|
||||
if (selectedRowKeys.value.length === 0) {
|
||||
notification.error({ message: t("monitor.selectRecordsFirst") });
|
||||
return;
|
||||
}
|
||||
Modal.confirm({
|
||||
title: t("monitor.confirmTitle"),
|
||||
content: t("monitor.batchCheckConfirm", { count: selectedRowKeys.value.length }),
|
||||
onOk: async () => {
|
||||
await siteInfoApi.CheckBatch(selectedRowKeys.value);
|
||||
notification.success({ message: t("monitor.checkSubmitted") });
|
||||
selectedRowKeys.value = [];
|
||||
await crudExpose.doRefresh();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const GroupTypeSite = "site";
|
||||
const groupDictRef = createGroupDictRef(GroupTypeSite);
|
||||
|
||||
@@ -253,6 +270,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
show: hasActionPermission("write"),
|
||||
title: t("certd.domain.importFromResolveRecords"),
|
||||
text: t("certd.domain.importFromResolveRecords"),
|
||||
tooltip: { title: t("certd.domain.importFromResolveRecordsTooltip") },
|
||||
type: "primary",
|
||||
// needPlus: true,
|
||||
color: "gold",
|
||||
@@ -274,6 +292,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
checkAll();
|
||||
},
|
||||
},
|
||||
checkBatch: {
|
||||
show: true,
|
||||
text: t("monitor.checkBatch"),
|
||||
type: "primary",
|
||||
icon: "ion:play-forward-outline",
|
||||
click() {
|
||||
checkBatch();
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
rowHandle: {
|
||||
|
||||
@@ -13,10 +13,15 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
// 编辑允许修改备注与权限范围。
|
||||
// keyId/keySecret 属于密钥本体,列表中 keySecret 是脱敏串,误提交会把真实密钥写坏,故不提交。
|
||||
const editRequest = async (req: EditReq) => {
|
||||
const { form, row } = req;
|
||||
form.id = row.id;
|
||||
const res = await api.UpdateObj(form);
|
||||
const res = await api.UpdateObj({
|
||||
id: row.id,
|
||||
remark: form.remark,
|
||||
scope: form.scope,
|
||||
});
|
||||
return res;
|
||||
};
|
||||
const delRequest = async (req: DelReq) => {
|
||||
@@ -68,7 +73,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
buttons: {
|
||||
view: { show: true },
|
||||
copy: { show: false },
|
||||
edit: { show: false },
|
||||
edit: { show: true },
|
||||
remove: { show: true },
|
||||
gen: {
|
||||
text: t("certd.gen.text"),
|
||||
@@ -193,6 +198,22 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
sorter: true,
|
||||
},
|
||||
},
|
||||
remark: {
|
||||
title: t("certd.keyRemark"),
|
||||
type: "text",
|
||||
search: {
|
||||
show: true,
|
||||
},
|
||||
column: {
|
||||
width: 200,
|
||||
sorter: true,
|
||||
},
|
||||
form: {
|
||||
show: true,
|
||||
helper: t("certd.keyRemarkHelper"),
|
||||
rules: [{ max: 512, message: t("certd.keyRemarkMaxChars") }],
|
||||
},
|
||||
},
|
||||
projectId: {
|
||||
title: t("certd.fields.projectName"),
|
||||
type: "dict-select",
|
||||
|
||||
@@ -152,6 +152,13 @@ export function useCertPipelineCreator({ formWrapperRef }: { formWrapperRef: Ref
|
||||
});
|
||||
|
||||
const initialForm = req.initialForm || {};
|
||||
const pluginSysConfig = await pluginStore.getPluginConfig({ name: certPlugin.name, type: "builtIn" });
|
||||
if (pluginSysConfig?.sysSetting?.input) {
|
||||
//设置系统默认值,必须放这里设置,保证随时应用最新的系统值
|
||||
for (const key in pluginSysConfig.sysSetting?.input) {
|
||||
initialForm.input[key] = pluginSysConfig.sysSetting?.input[key];
|
||||
}
|
||||
}
|
||||
initialForm.type = certPlugin.name;
|
||||
const applyTemplates = reactive<any[]>([]);
|
||||
const selectedTemplateId = ref<number | null>(null);
|
||||
@@ -556,12 +563,12 @@ export function useCertPipelineCreator({ formWrapperRef }: { formWrapperRef: Ref
|
||||
|
||||
//设置系统初始值
|
||||
const initialForm: any = { input: {} };
|
||||
const pluginSysConfig = await pluginStore.getPluginConfig({ name: req.pluginName, type: "builtIn" });
|
||||
if (pluginSysConfig.sysSetting?.input) {
|
||||
for (const key in pluginSysConfig.sysSetting?.input) {
|
||||
initialForm.input[key] = pluginSysConfig.sysSetting?.input[key];
|
||||
}
|
||||
}
|
||||
// const pluginSysConfig = await pluginStore.getPluginConfig({ name: req.pluginName, type: "builtIn" });
|
||||
// if (pluginSysConfig.sysSetting?.input) {
|
||||
// for (const key in pluginSysConfig.sysSetting?.input) {
|
||||
// initialForm.input[key] = pluginSysConfig.sysSetting?.input[key];
|
||||
// }
|
||||
// }
|
||||
async function doSubmit({ form }: any) {
|
||||
// const certDetail = readCertDetail(form.cert.crt);
|
||||
// 添加certd pipeline
|
||||
|
||||
+6
-3
@@ -263,8 +263,8 @@ function useStepForm() {
|
||||
currentStep.input[key] = column.default ?? column.value;
|
||||
}
|
||||
}
|
||||
//设置系统初始值
|
||||
const pluginSysConfig = await pluginStore.getPluginConfig({ name: pluginDefine.name, type: "builtIn" });
|
||||
//设置系统初始值,必须放在这里设置,保证最新的系统值被清空后,这里也能跟着清空
|
||||
const pluginSysConfig = await pluginStore.getPluginConfig({ name: pluginDefine.name, type: pluginDefine.type });
|
||||
if (pluginSysConfig.sysSetting?.input) {
|
||||
for (const key in pluginSysConfig.sysSetting?.input) {
|
||||
currentStep.input[key] = pluginSysConfig.sysSetting?.input[key];
|
||||
@@ -280,7 +280,10 @@ function useStepForm() {
|
||||
return;
|
||||
}
|
||||
|
||||
callback.value("save", currentStep);
|
||||
// currentStep 是复用同一份 reactive 对象,直接传给回调会被 task.steps 保存同一引用,
|
||||
// 后续再次 stepOpen 会 delete 掉其属性,导致已保存的步骤(如第一个步骤)被覆盖。
|
||||
// 这里深拷贝一份再传出,保证每个步骤是独立对象。
|
||||
callback.value("save", cloneDeep(currentStep));
|
||||
stepDrawerClose();
|
||||
};
|
||||
|
||||
|
||||
@@ -53,9 +53,14 @@
|
||||
|
||||
<div class="flex-o mt-5">
|
||||
<span class="label">{{ $t("certd.order.paymentMethod") }}:</span>
|
||||
<div v-if="durationSelected.price === 0 || expectedThirdPartyAmount === 0">{{ $t("certd.order.free") }}</div>
|
||||
<div v-if="durationSelected.price === 0 || expectedThirdPartyAmount === 0">0元</div>
|
||||
<div v-else-if="noPaymentMethod">当前仅支持余额支付</div>
|
||||
<fs-dict-select v-else v-model:value="formRef.payType" :dict="paymentsDictRef" style="width: 200px"> </fs-dict-select>
|
||||
</div>
|
||||
|
||||
<div v-if="showBalanceInsufficientTip" class="flex-o mt-5">
|
||||
<span class="color-red">余额不足,请联系管理员,充值或设置套餐</span>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
@@ -88,12 +93,17 @@ async function open(opts: OrderModalOpenReq) {
|
||||
formRef.value.productId = opts.product.id;
|
||||
formRef.value.duration = opts.duration;
|
||||
formRef.value.num = opts.num ?? 1;
|
||||
formRef.value.useRebateBalance = false;
|
||||
formRef.value.useRebateBalance = true;
|
||||
try {
|
||||
wallet.value = await GetWalletSummary();
|
||||
} catch (e) {
|
||||
wallet.value = { availableAmount: 0 };
|
||||
}
|
||||
try {
|
||||
await paymentsDictRef.loadDict();
|
||||
} catch (e) {
|
||||
// 支付方式加载失败时按无可用支付方式处理
|
||||
}
|
||||
}
|
||||
const paymentsDictRef = dict({
|
||||
async getData() {
|
||||
@@ -119,6 +129,23 @@ const expectedThirdPartyAmount = computed(() => {
|
||||
return Math.max(0, (durationSelected.value?.price || 0) - expectedRebateAmount.value);
|
||||
});
|
||||
|
||||
const noPaymentMethod = computed(() => {
|
||||
return paymentsDictRef.data.length === 0;
|
||||
});
|
||||
|
||||
const balanceInsufficient = computed(() => {
|
||||
const price = durationSelected.value?.price || 0;
|
||||
if (price <= 0) {
|
||||
return false;
|
||||
}
|
||||
const availableAmount = wallet.value?.availableAmount || 0;
|
||||
return availableAmount < price;
|
||||
});
|
||||
|
||||
const showBalanceInsufficientTip = computed(() => {
|
||||
return noPaymentMethod.value && balanceInsufficient.value;
|
||||
});
|
||||
|
||||
function amountToYuan(amount: number) {
|
||||
return util.amount.toYuan(amount || 0);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ import { CanvasRenderer } from "echarts/renderers";
|
||||
import { PieChart } from "echarts/charts";
|
||||
import { TitleComponent, TooltipComponent, LegendComponent, GridComponent } from "echarts/components";
|
||||
import VChart, { THEME_KEY } from "vue-echarts";
|
||||
import { ref, provide, defineProps } from "vue";
|
||||
import { computed, provide, defineProps } from "vue";
|
||||
import { usePreferences } from "/@/vben/preferences";
|
||||
import { ChartItem } from "./d";
|
||||
|
||||
use([CanvasRenderer, PieChart, TitleComponent, TooltipComponent, GridComponent, LegendComponent]);
|
||||
@@ -18,16 +19,26 @@ provide(THEME_KEY, "");
|
||||
const props = defineProps<{
|
||||
data: ChartItem[];
|
||||
}>();
|
||||
const { isDark } = usePreferences();
|
||||
|
||||
const option = ref({
|
||||
const option = computed(() => ({
|
||||
color: ["#91cc75", "#73c0de", "#ee6666", "#fac858", "#5470c6", "#3ba272", "#fc8452", "#9a60b4", "#ea7ccc", "#5470c6"],
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
backgroundColor: isDark.value ? "rgba(35, 38, 45, 0.96)" : "rgba(255, 255, 255, 0.96)",
|
||||
borderColor: isDark.value ? "rgba(255, 255, 255, 0.2)" : "#d9d9d9",
|
||||
textStyle: {
|
||||
color: isDark.value ? "rgba(242, 242, 242, 0.92)" : "#333",
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
orient: "vertical",
|
||||
bottom: "5%",
|
||||
left: "left",
|
||||
textStyle: {
|
||||
color: isDark.value ? "rgba(242, 242, 242, 0.85)" : "#555",
|
||||
},
|
||||
inactiveColor: isDark.value ? "rgba(242, 242, 242, 0.35)" : "#ccc",
|
||||
},
|
||||
grid: {
|
||||
top: "20px",
|
||||
@@ -65,7 +76,7 @@ const option = ref({
|
||||
data: props.data,
|
||||
},
|
||||
],
|
||||
});
|
||||
}));
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<SimpleSteps></SimpleSteps>
|
||||
</div>
|
||||
</div>
|
||||
<div class="warning">
|
||||
<div v-if="noticeList.length > 0" class="warning">
|
||||
<notice-bar :list="noticeList"></notice-bar>
|
||||
</div>
|
||||
|
||||
@@ -84,11 +84,22 @@
|
||||
</template>
|
||||
</statistic-card>
|
||||
</a-col>
|
||||
<!-- <a-col :md="6" :xs="24">
|
||||
|
||||
<a-col :md="6" :xs="24">
|
||||
<statistic-card :title="t('certd.dashboard.pipelineStatus')" :footer="false">
|
||||
<pie-count v-if="count.pipelineStatusCount" :data="count.pipelineStatusCount"></pie-count>
|
||||
<pie-count v-if="count.pipelineStatusCount" :data="count.pipelineStatusCount" />
|
||||
</statistic-card>
|
||||
</a-col> -->
|
||||
</a-col>
|
||||
<a-col :md="6" :xs="24">
|
||||
<statistic-card icon="fluent-color:data-trending-24" :title="t('certd.dashboard.recentRun')" :footer="false">
|
||||
<day-count v-if="count.historyCountPerDay" :data="count.historyCountPerDay" :title="t('certd.dashboard.runCount')"></day-count>
|
||||
</statistic-card>
|
||||
</a-col>
|
||||
<a-col :md="6" :xs="24">
|
||||
<statistic-card icon="fluent-color:alert-urgent-24" :title="t('certd.dashboard.expiringCerts')">
|
||||
<expiring-list v-if="count.expiringList" :data="count.expiringList"></expiring-list>
|
||||
</statistic-card>
|
||||
</a-col>
|
||||
<a-col :md="6" :xs="24">
|
||||
<statistic-card icon="fluent-color:certificate-24" :title="t('certd.dashboard.certCount')" :count="count.certCount" link="/cert/monitor/cert" :sub-counts="count.certStatusCount">
|
||||
<template v-if="count.certCount === 0" #default>
|
||||
@@ -102,13 +113,29 @@
|
||||
</statistic-card>
|
||||
</a-col>
|
||||
<a-col :md="6" :xs="24">
|
||||
<statistic-card icon="fluent-color:data-trending-24" :title="t('certd.dashboard.recentRun')" :footer="false">
|
||||
<day-count v-if="count.historyCountPerDay" :data="count.historyCountPerDay" :title="t('certd.dashboard.runCount')"></day-count>
|
||||
<statistic-card
|
||||
icon="fluent-color:certificate-24"
|
||||
:title="t('certd.dashboard.totalGeneratedCertCount')"
|
||||
:count="count.totalGeneratedCertCount"
|
||||
:sub-counts="count.generatedCertTypeCounts"
|
||||
:helper-text="t('certd.dashboard.savedAmount')"
|
||||
:helper-value="`¥${formatSavedAmount(count.totalSavedAmount)}`"
|
||||
:helper-tooltip="t('certd.dashboard.savedAmountRule')"
|
||||
:footer="false"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col :md="6" :xs="24">
|
||||
<statistic-card icon="fluent-color:link-multiple-24" :title="t('certd.dashboard.siteMonitorCount')" :count="count.siteCount" link="/cert/monitor/site" :sub-counts="count.siteStatusCount">
|
||||
<template #footer>
|
||||
<router-link to="/cert/monitor/site" class="flex"> <fs-icon icon="ion:settings-outline" class="mr-5 fs-16" /> {{ t("certd.dashboard.manageSiteMonitor") }} </router-link>
|
||||
</template>
|
||||
</statistic-card>
|
||||
</a-col>
|
||||
<a-col :md="6" :xs="24">
|
||||
<statistic-card icon="fluent-color:alert-urgent-24" :title="t('certd.dashboard.expiringCerts')">
|
||||
<expiring-list v-if="count.expiringList" :data="count.expiringList"></expiring-list>
|
||||
<statistic-card icon="fluent-color:globe-24" :title="t('certd.dashboard.domainCount')" :count="count.domainCount" link="/cert/cert/domain" :sub-counts="count.domainStatusCount">
|
||||
<template #footer>
|
||||
<router-link to="/cert/cert/domain" class="flex"> <fs-icon icon="ion:settings-outline" class="mr-5 fs-16" /> {{ t("certd.dashboard.manageDomain") }} </router-link>
|
||||
</template>
|
||||
</statistic-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@@ -121,6 +148,7 @@
|
||||
<fs-icon icon="fluent-color:puzzle-piece-24" class="mr-5 fs-28" />
|
||||
<div class="mr-5">{{ t("certd.dashboard.supportedTasks") }}</div>
|
||||
<a-tag color="green">{{ pluginGroups.groups.all.plugins.length }}</a-tag>
|
||||
<div class="helper">列表仅做展示,请创建流水线来使用它们</div>
|
||||
</div>
|
||||
</template>
|
||||
<a-row :gutter="10">
|
||||
@@ -159,6 +187,7 @@ import { useRouter } from "vue-router";
|
||||
import * as api from "./api";
|
||||
import DayCount from "./charts/day-count.vue";
|
||||
import ExpiringList from "./charts/expiring-list.vue";
|
||||
import PieCount from "./charts/pie-count.vue";
|
||||
import NoticeBar from "./notice-bar.vue";
|
||||
import SuiteCard from "./suite-card.vue";
|
||||
import TutorialButton from "/@/components/tutorial/index.vue";
|
||||
@@ -260,6 +289,10 @@ function goPipeline() {
|
||||
}
|
||||
|
||||
const count: any = ref({});
|
||||
function formatSavedAmount(amountValue: number) {
|
||||
const amount = Number(amountValue) || 0;
|
||||
return Number.isInteger(amount) ? amount.toString() : amount.toFixed(2);
|
||||
}
|
||||
function transformStatusCount() {
|
||||
const data = count.value.pipelineStatusCount;
|
||||
const sorted = [
|
||||
@@ -300,6 +333,33 @@ function transformStatusCount() {
|
||||
{ name: t("certd.dashboard.certNoExpireCount"), value: certCount.notExpired, color: "green", link: { path: "/cert/monitor/cert", query: { expireStatus: "noExpired" } } },
|
||||
];
|
||||
count.value.certCount = certCount.total;
|
||||
|
||||
const siteCount = count.value.siteCount;
|
||||
count.value.siteStatusCount = [
|
||||
{ name: t("certd.dashboard.siteAbnormalCount"), value: siteCount.abnormal, color: "red", checkIcon: "mingcute:warning-fill:#f44336", link: { path: "/cert/monitor/site", query: { checkStatus: "error" } } },
|
||||
{ name: t("certd.dashboard.siteNormalCount"), value: siteCount.normal, color: "green" },
|
||||
];
|
||||
count.value.siteCount = siteCount.total;
|
||||
|
||||
const domainCount = count.value.domainCount;
|
||||
count.value.domainStatusCount = [
|
||||
{ name: t("certd.dashboard.domainExpiredCount"), value: domainCount.expired, color: "red", checkIcon: "mingcute:warning-fill:#f44336", link: { path: "/cert/cert/domain", query: { expirationStatus: "expired" } } },
|
||||
{ name: t("certd.dashboard.domainExpiringCount"), value: domainCount.expiring, color: "yellow", checkIcon: "mingcute:alert-fill:#ff9800" },
|
||||
{ name: t("certd.dashboard.domainNotExpiredCount"), value: domainCount.notExpired, color: "green" },
|
||||
];
|
||||
count.value.domainCount = domainCount.total;
|
||||
|
||||
const genCertCount = count.value.genCertCount || {};
|
||||
const singleDomainCertCount = Number(genCertCount.singleDomainCertCount) || 0;
|
||||
const multiDomainCertCount = Number(genCertCount.multiDomainCertCount) || 0;
|
||||
const wildcardCertCount = Number(genCertCount.wildcardCertCount) || 0;
|
||||
count.value.totalGeneratedCertCount = singleDomainCertCount + multiDomainCertCount + wildcardCertCount;
|
||||
count.value.generatedCertTypeCounts = [
|
||||
{ name: t("certd.dashboard.singleDomainCertShortCount"), value: singleDomainCertCount, color: "blue" },
|
||||
{ name: t("certd.dashboard.multiDomainCertShortCount"), value: multiDomainCertCount, color: "blue" },
|
||||
{ name: t("certd.dashboard.wildcardCertShortCount"), value: wildcardCertCount, color: "blue" },
|
||||
];
|
||||
count.value.totalSavedAmount = singleDomainCertCount * (532 / 4) + multiDomainCertCount * (912 / 4) + wildcardCertCount * (2147 / 4);
|
||||
}
|
||||
async function loadCount() {
|
||||
count.value = await GetStatisticCount();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="statistic-card">
|
||||
<a-card>
|
||||
<a-card :class="cardTone">
|
||||
<div class="data-item">
|
||||
<div class="header">
|
||||
<div class="title">
|
||||
@@ -11,9 +11,9 @@
|
||||
</div>
|
||||
<div class="content">
|
||||
<div v-if="!slots.default" class="statistic">
|
||||
<div v-if="count !== 0" class="value flex items-center w-full">
|
||||
<div class="value flex items-center w-full">
|
||||
<div class="total flex-center flex-1 flex-col pointer" @click="goDetail(link)">
|
||||
<span>{{ count }}</span>
|
||||
<span>{{ count ?? 0 }}</span>
|
||||
<span class="sub-title">{{ title }}</span>
|
||||
</div>
|
||||
<a-divider type="vertical h-10"></a-divider>
|
||||
@@ -32,7 +32,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a-empty v-else></a-empty>
|
||||
<div v-if="helperText" class="helper-text mt-10">
|
||||
<span>{{ helperText }}</span>
|
||||
<strong v-if="helperValue" class="helper-value">{{ helperValue }}</strong>
|
||||
<a-tooltip v-if="helperTooltip" :title="helperTooltip">
|
||||
<fs-icon icon="mingcute:information-line" class="helper-icon" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
@@ -45,6 +51,7 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { FsIcon } from "@fast-crud/fast-crud";
|
||||
import { computed } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
const props = defineProps<{
|
||||
icon: string;
|
||||
@@ -59,9 +66,21 @@ const props = defineProps<{
|
||||
title?: string;
|
||||
link?: any;
|
||||
}[];
|
||||
helperText?: string;
|
||||
helperValue?: string;
|
||||
helperTooltip?: string;
|
||||
}>();
|
||||
const slots = defineSlots();
|
||||
const router = useRouter();
|
||||
const cardTone = computed(() => {
|
||||
if (props.subCounts?.some(item => item.color === "red" && item.value > 0)) {
|
||||
return "has-danger";
|
||||
}
|
||||
if (props.subCounts?.some(item => item.color === "yellow" && item.value > 0)) {
|
||||
return "has-warning";
|
||||
}
|
||||
return "";
|
||||
});
|
||||
function goDetail(link: any) {
|
||||
if (!link) {
|
||||
return;
|
||||
@@ -80,9 +99,35 @@ function goDetail(link: any) {
|
||||
color: rgba(242, 242, 242, 0.85) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.statistic-card {
|
||||
.ant-card.has-danger {
|
||||
background: linear-gradient(135deg, rgba(92, 35, 35, 0.72), rgba(28, 28, 28, 0.96) 62%);
|
||||
}
|
||||
|
||||
.ant-card.has-warning {
|
||||
background: linear-gradient(135deg, rgba(91, 72, 25, 0.68), rgba(28, 28, 28, 0.96) 62%);
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
color: rgba(242, 242, 242, 0.72);
|
||||
|
||||
.helper-value {
|
||||
color: #8bd7a0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.statistic-card {
|
||||
margin-bottom: 10px;
|
||||
|
||||
.ant-card.has-danger {
|
||||
background: linear-gradient(135deg, rgba(255, 241, 240, 0.9), #fff 58%);
|
||||
}
|
||||
|
||||
.ant-card.has-warning {
|
||||
background: linear-gradient(135deg, rgba(255, 251, 230, 0.9), #fff 58%);
|
||||
}
|
||||
.icon-text {
|
||||
display: inline-flex;
|
||||
justify-content: left;
|
||||
@@ -143,6 +188,7 @@ function goDetail(link: any) {
|
||||
font-weight: 700;
|
||||
.total {
|
||||
color: hsl(var(--primary));
|
||||
|
||||
.sub-title {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
@@ -180,6 +226,31 @@ function goDetail(link: any) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.helper-text {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
margin-top: 6px;
|
||||
color: #626262;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 20px;
|
||||
|
||||
.helper-value {
|
||||
color: #52a26a;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.helper-icon {
|
||||
cursor: help;
|
||||
font-size: 14px;
|
||||
margin-left: 2px;
|
||||
vertical-align: -2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { PluginDefine } from "@certd/pipeline";
|
||||
import { request } from "/src/api/service";
|
||||
|
||||
const apiPrefix = "/sys/plugin";
|
||||
@@ -108,6 +109,7 @@ export type OnlinePluginBean = {
|
||||
desc?: string;
|
||||
latest?: string;
|
||||
status?: string;
|
||||
email?: string;
|
||||
downloadCount?: number;
|
||||
score?: number;
|
||||
aiCheckStatus?: string;
|
||||
@@ -218,14 +220,8 @@ export async function OnlinePluginPublishInfo(body: { id: number }): Promise<{
|
||||
|
||||
export type OnlinePluginAuthorBean = {
|
||||
id?: number;
|
||||
appId?: number;
|
||||
appOwnerId?: number;
|
||||
developerId?: number;
|
||||
name?: string;
|
||||
displayName?: string;
|
||||
avatar?: string;
|
||||
desc?: string;
|
||||
status?: string;
|
||||
email?: string;
|
||||
};
|
||||
|
||||
export async function OnlinePluginAuthorGet(options?: { showErrorNotify?: boolean }): Promise<{ registered?: boolean; author?: OnlinePluginAuthorBean }> {
|
||||
@@ -236,18 +232,22 @@ export async function OnlinePluginAuthorGet(options?: { showErrorNotify?: boolea
|
||||
});
|
||||
}
|
||||
|
||||
export async function OnlinePluginAuthorAdd(body: { name: string; displayName?: string; avatar?: string; desc?: string }): Promise<OnlinePluginAuthorBean> {
|
||||
export async function OnlinePluginAuthorAdd(body: { name: string; email: string }): Promise<OnlinePluginAuthorBean> {
|
||||
return await request({
|
||||
url: apiPrefix + "/online/author/add",
|
||||
method: "post",
|
||||
data: body,
|
||||
});
|
||||
}
|
||||
export async function OnlinePluginAuthorUpdate(body: { email: string }): Promise<OnlinePluginAuthorBean> {
|
||||
return await request({ url: apiPrefix + "/online/author/update", method: "post", data: body });
|
||||
}
|
||||
|
||||
export type PluginConfigBean = {
|
||||
name: string;
|
||||
disabled: boolean;
|
||||
sysSetting: {
|
||||
metadata?: Record<string, any>;
|
||||
input?: Record<string, any>;
|
||||
};
|
||||
};
|
||||
@@ -285,7 +285,7 @@ export async function SaveCommPluginConfigs(data: CommPluginConfig): Promise<voi
|
||||
});
|
||||
}
|
||||
|
||||
export async function savePluginSetting(req: { name: string; sysSetting: any }): Promise<void> {
|
||||
export async function savePluginSetting(req: { name: string; sysSetting: any; type: string }): Promise<void> {
|
||||
return await request({
|
||||
url: apiPrefix + "/saveSetting",
|
||||
method: "post",
|
||||
@@ -293,11 +293,11 @@ export async function savePluginSetting(req: { name: string; sysSetting: any }):
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetPluginByName(name: string): Promise<PluginConfigBean> {
|
||||
export async function getPluginSetting(name: string, type: string = "builtIn"): Promise<PluginConfigBean> {
|
||||
return await request({
|
||||
url: apiPrefix + "/getPluginByName",
|
||||
url: apiPrefix + "/getSetting",
|
||||
method: "post",
|
||||
data: { name },
|
||||
data: { name, type },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -307,3 +307,11 @@ export async function ClearRuntimeDeps(): Promise<void> {
|
||||
method: "post",
|
||||
});
|
||||
}
|
||||
|
||||
export async function getPluginDefine(name: string): Promise<PluginDefine> {
|
||||
return await request({
|
||||
url: apiPrefix + "/getPluginDefine",
|
||||
method: "post",
|
||||
data: { name },
|
||||
});
|
||||
}
|
||||
|
||||
+60
-15
@@ -21,7 +21,17 @@
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="step === 1"
|
||||
><label class="plugin-ai-dev__label">开发需求</label><a-textarea v-model:value="requirement" :rows="6" /><a-button
|
||||
><div v-if="mode === 'new'" class="plugin-ai-dev__field">
|
||||
<label>插件作者</label>
|
||||
<PluginAuthorField v-model="author" editable />
|
||||
<div class="plugin-ai-dev__hint">自动读取绑定账号已注册的插件作者,也可以手动输入作者名称。</div>
|
||||
</div>
|
||||
<div v-else class="plugin-ai-dev__field">
|
||||
<label>插件作者</label>
|
||||
<a-input :value="author" disabled />
|
||||
<div class="plugin-ai-dev__hint">修改已有插件时沿用该插件当前作者,作者变化会导致插件身份变化。</div>
|
||||
</div>
|
||||
<label class="plugin-ai-dev__label">开发需求</label><a-textarea v-model:value="requirement" :rows="6" /><a-button
|
||||
class="plugin-ai-dev__generate"
|
||||
type="primary"
|
||||
:loading="creating"
|
||||
@@ -120,6 +130,7 @@ import * as api from "../api";
|
||||
import * as pipelineApi from "/@/views/certd/pipeline/api";
|
||||
import * as historyApi from "/@/views/certd/pipeline/api.history";
|
||||
import DependPluginsInput from "./depend-plugins-input.vue";
|
||||
import PluginAuthorField from "./plugin-author-field.vue";
|
||||
import PluginEditDialogBody from "./plugin-edit-dialog-body.vue";
|
||||
import PipelineEditDialog from "/@/views/certd/pipeline/components/pipeline-edit-dialog.vue";
|
||||
import { useFormDialog } from "/@/use/use-dialog";
|
||||
@@ -135,6 +146,7 @@ const pluginTypeOptions = [
|
||||
{ value: "notification", label: "通知插件", desc: "通过消息、邮件等方式发送通知" },
|
||||
// { value: "addon", label: "Addon", desc: "扩展流水线的通用辅助能力" },
|
||||
];
|
||||
const author = ref("");
|
||||
const requirement = ref(
|
||||
"例如:开发一个部署证书到阿里云DCDN的插件\nAPI接口请参考:https://help.aliyun.com/zh/edge-security-acceleration/dcdn/developer-reference/api-dcdn-2018-01-15-overview。\n要求: \n1、能够引用上传到阿里云CAS步骤输出的证书。\n2、支持在UI上选择账户下某区域下的DCDN的域名列表。\n3、支持一次性选择多个域名进行部署"
|
||||
);
|
||||
@@ -183,7 +195,33 @@ async function createPrompt() {
|
||||
try {
|
||||
const token = await api.GetScopedAccessToken(["sys/ai"]);
|
||||
accessToken.value = token.token;
|
||||
prompt.value = `你是 Certd 在线插件开发 Agent。\n\n开发模式:${mode.value === "new" ? "开发新插件" : "修改已有插件"}\n插件类型:${pluginType.value || "按已有插件类型"}\n插件 ID:${pluginPath.value.at(-1) || "无"}\n用户需求:\n${requirement.value.trim()}\n\n如果用户需求描述与他选择的插件类型有冲突,你需要跟用户确认是否选错插件类型。 \nCertd 地址:${window.location.origin}\n受限 AccessToken(6小时有效):${token.token}\n\n请先读取 .trae/skills/certd-online-plugin-dev/SKILL.md,按插件类型开发并提交版本。仅调用 /scoped/sys/ai/plugin/ 前缀接口,完成后报告提交结果,不自动发布。\n如果当前工作目录不是 Certd 项目,或缺少 certd-online-plugin-dev Skill,先拉取 Certd 仓库代码并切换到仓库内工作(git clone https://atomgit.com/certd/certd --depth 1 )`;
|
||||
const authorName = author.value.trim();
|
||||
let authorText = authorName;
|
||||
if (!authorText && mode.value === "edit") {
|
||||
// 修改已有插件时作者保持原样,不能凭空补一个作者出来
|
||||
authorText = "无(保持原插件的 author 字段不变,不要新增作者)";
|
||||
}
|
||||
if (!authorText) {
|
||||
authorText = "未指定(请先向用户确认作者名称,不要自行编造作者名)";
|
||||
}
|
||||
prompt.value = `
|
||||
你是 Certd 在线插件开发 Agent。
|
||||
|
||||
开发模式:${mode.value === "new" ? "开发新插件" : "修改已有插件"}
|
||||
插件类型:${pluginType.value || "按已有插件类型"}
|
||||
插件 ID:${pluginPath.value.at(-1) || "无"}
|
||||
插件作者:${authorText}
|
||||
用户需求:
|
||||
${requirement.value.trim()}
|
||||
|
||||
如果用户需求描述与他选择的插件类型有冲突,你需要跟用户确认是否选错插件类型。
|
||||
Certd 地址:${window.location.origin}
|
||||
受限 AccessToken(6小时有效):${token.token}
|
||||
|
||||
请先读取 .trae/skills/certd-online-plugin-dev/SKILL.md,按插件类型开发并提交版本。仅调用 /scoped/sys/ai/plugin/ 前缀接口,完成后报告提交结果,不自动发布。
|
||||
如果当前工作目录不是 Certd 项目,或缺少 certd-online-plugin-dev Skill,先拉取 Certd 仓库代码并切换到仓库内工作(git clone https://atomgit.com/certd/certd --depth 1 )
|
||||
本次开发的在线插件无需写单元测试。
|
||||
`;
|
||||
notification.success({ message: "启动提示词已生成" });
|
||||
} finally {
|
||||
creating.value = false;
|
||||
@@ -259,24 +297,31 @@ async function loadRecentPlugins(force = false) {
|
||||
async function loadSelectedPlugin() {
|
||||
const selectedValue = pluginPath.value.at(-1);
|
||||
if (!selectedValue) {
|
||||
recentPlugins.value = [];
|
||||
setSelectedPlugin(null);
|
||||
return;
|
||||
}
|
||||
if (props.pluginId && selectedValue === String(props.pluginId)) {
|
||||
const selected = await api.GetObj(props.pluginId);
|
||||
recentPlugins.value = selected ? [selected] : [];
|
||||
setSelectedPlugin(selected);
|
||||
return;
|
||||
}
|
||||
let selected = await api.GetObj(selectedValue);
|
||||
if (selected) {
|
||||
recentPlugins.value = [selected];
|
||||
return;
|
||||
if (!selected) {
|
||||
const fullName = selectedValue.includes(":") ? selectedValue.slice(selectedValue.indexOf(":") + 1) : selectedValue;
|
||||
const result: any = await api.FindPlugins({ includeBuiltIn: true, includeStore: true, keyword: fullName });
|
||||
const records: any[] = Array.isArray(result) ? result : result?.records || [];
|
||||
selected = records.find(item => String(item.fullName || item.name) === fullName) || records[0];
|
||||
}
|
||||
setSelectedPlugin(selected);
|
||||
}
|
||||
|
||||
function setSelectedPlugin(plugin: any) {
|
||||
recentPlugins.value = plugin ? [plugin] : [];
|
||||
const pluginAuthor = String(plugin?.author || "").trim();
|
||||
if (pluginAuthor) {
|
||||
// 修改已有插件时,作者默认沿用该插件当前作者,避免插件身份被改错
|
||||
author.value = pluginAuthor;
|
||||
}
|
||||
const fullName = selectedValue.includes(":") ? selectedValue.slice(selectedValue.indexOf(":") + 1) : selectedValue;
|
||||
const result: any = await api.FindPlugins({ includeBuiltIn: true, includeStore: true, keyword: fullName });
|
||||
const records: any[] = Array.isArray(result) ? result : result?.records || [];
|
||||
selected = records.find(item => String(item.fullName || item.name) === fullName) || records[0];
|
||||
recentPlugins.value = selected ? [selected] : [];
|
||||
}
|
||||
|
||||
function formatTime(value: any) {
|
||||
@@ -361,9 +406,9 @@ watch(mode, value => {
|
||||
<style lang="less">
|
||||
.plugin-ai-dev {
|
||||
display: flex;
|
||||
height: 60vh;
|
||||
height: 65vh;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
gap: 10px;
|
||||
|
||||
&__steps {
|
||||
flex: none;
|
||||
@@ -373,7 +418,7 @@ watch(mode, value => {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
gap: 18px;
|
||||
gap: 10px;
|
||||
padding: 8px 20px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<template>
|
||||
<div class="plugin-author-field">
|
||||
<a-input :value="modelValue" placeholder="请输入作者名称" @update:value="emit('update:modelValue', $event)" />
|
||||
<a-button type="link" size="small" :loading="loading" @click="registerAuthor">注册作者</a-button>
|
||||
<a-input :value="modelValue" placeholder="请输入作者名称" :disabled="inputDisabled" @update:value="emit('update:modelValue', $event)" />
|
||||
<a-button type="link" size="small" :loading="loading" @click="handleAuthorAction">
|
||||
{{ authorRegistered ? "修改邮箱" : "注册作者" }}
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -12,10 +14,22 @@ import { usePluginPublish } from "../use-publish";
|
||||
|
||||
defineOptions({ name: "PluginAuthorField" });
|
||||
|
||||
const props = defineProps<{ modelValue?: string }>();
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
modelValue?: string;
|
||||
// 允许手动输入作者名称,默认使用后端已注册作者且不可修改
|
||||
editable?: boolean;
|
||||
}>(),
|
||||
{
|
||||
editable: false,
|
||||
}
|
||||
);
|
||||
const emit = defineEmits<{ (event: "update:modelValue", value: string): void }>();
|
||||
const loading = ref(false);
|
||||
const { registerPluginAuthor } = usePluginPublish();
|
||||
const { registerPluginAuthor, updatePluginAuthorEmail } = usePluginPublish();
|
||||
const registeredAuthor = ref<api.OnlinePluginAuthorBean>();
|
||||
const authorRegistered = computed(() => !!registeredAuthor.value?.id);
|
||||
const inputDisabled = computed(() => authorRegistered.value && !props.editable);
|
||||
const isLocalAuthor = computed(() => {
|
||||
return (
|
||||
String(props.modelValue || "")
|
||||
@@ -28,7 +42,8 @@ async function loadAuthor() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await api.OnlinePluginAuthorGet({ showErrorNotify: false });
|
||||
if ((!props.modelValue || isLocalAuthor.value) && result.author?.name) {
|
||||
if (result.registered && result.author?.id && result.author.name) {
|
||||
registeredAuthor.value = result.author;
|
||||
emit("update:modelValue", result.author.name);
|
||||
}
|
||||
} catch {
|
||||
@@ -38,11 +53,19 @@ async function loadAuthor() {
|
||||
}
|
||||
}
|
||||
|
||||
async function registerAuthor() {
|
||||
async function handleAuthorAction() {
|
||||
if (registeredAuthor.value) {
|
||||
const author = await updatePluginAuthorEmail(registeredAuthor.value);
|
||||
if (author?.id) {
|
||||
registeredAuthor.value = author;
|
||||
}
|
||||
return;
|
||||
}
|
||||
const author = await registerPluginAuthor();
|
||||
if (!author?.name) {
|
||||
return;
|
||||
}
|
||||
registeredAuthor.value = author;
|
||||
emit("update:modelValue", author.name);
|
||||
}
|
||||
|
||||
|
||||
@@ -105,6 +105,9 @@ async function loadPlugin() {
|
||||
if (baseForm.extra) {
|
||||
baseForm.extra = yaml.load(baseForm.extra);
|
||||
}
|
||||
if (baseForm.dependPlugins) {
|
||||
baseForm.dependPlugins = yaml.load(baseForm.dependPlugins);
|
||||
}
|
||||
delete baseForm.metadata;
|
||||
delete baseForm.content;
|
||||
baseFormRef.value.setFormData(baseForm);
|
||||
@@ -136,6 +139,9 @@ function buildSubmitForm(versionOverride?: string) {
|
||||
if (form.extra) {
|
||||
form.extra = yaml.dump(form.extra);
|
||||
}
|
||||
if (form.dependPlugins) {
|
||||
form.dependPlugins = yaml.dump(form.dependPlugins);
|
||||
}
|
||||
return form;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="plugin-card__actions">
|
||||
<div v-if="pluginCardState.showEditButton || pluginCardState.showConfigButton" class="plugin-card__tools">
|
||||
<div v-if="pluginCardState.showEditButton || pluginCardState.showConfigButton || pluginCardState.canExportPlugin" class="plugin-card__tools">
|
||||
<a-tooltip v-if="pluginCardState.showEditButton" title="编辑">
|
||||
<a-button class="plugin-card__tool" type="text" size="small" @click.stop="editPlugin">
|
||||
<template #icon>
|
||||
@@ -37,10 +37,10 @@
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-if="copyHandler && pluginCardState.canCopyPlugin" title="复制">
|
||||
<a-button class="plugin-card__tool" type="text" size="small" @click.stop="copyPlugin">
|
||||
<a-tooltip v-if="pluginCardState.canExportPlugin" :title="t('certd.export')">
|
||||
<a-button class="plugin-card__tool" type="text" size="small" :loading="isActionLoading('export')" @click.stop="exportPlugin">
|
||||
<template #icon>
|
||||
<fs-icon icon="ion:copy-outline" />
|
||||
<fs-icon icon="ion:cloud-download-outline" />
|
||||
</template>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
@@ -89,7 +89,7 @@
|
||||
|
||||
<div class="plugin-card__meta">
|
||||
<template v-if="source === 'local'">
|
||||
<a-tooltip :title="toggleTitle">
|
||||
<a-tooltip v-if="plugin.showDisabled" :title="toggleTitle">
|
||||
<a-tag class="plugin-status-tag" :class="{ 'is-loading': isActionLoading('toggle') }" :color="plugin.disabled ? 'default' : 'green'" @click.stop="togglePlugin">
|
||||
{{ plugin.disabled ? t("certd.onlinePluginDisabled") : t("certd.onlinePluginEnabled") }}
|
||||
</a-tag>
|
||||
@@ -99,7 +99,7 @@
|
||||
<a-tag v-if="plugin.group">{{ plugin.group }}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="!simple">
|
||||
<a-tooltip v-if="plugin.installed && plugin.id" :title="toggleTitle">
|
||||
<a-tooltip v-if="plugin.installed && plugin.id && plugin.showDisabled" :title="toggleTitle">
|
||||
<a-tag class="plugin-status-tag" :class="{ 'is-loading': isActionLoading('toggle') }" :color="plugin.disabled ? 'default' : 'green'" @click.stop="togglePlugin">
|
||||
{{ plugin.disabled ? t("certd.onlinePluginDisabled") : t("certd.onlinePluginEnabled") }}
|
||||
</a-tag>
|
||||
@@ -147,6 +147,7 @@ import PluginEditDialogBody from "./plugin-edit-dialog-body.vue";
|
||||
import { usePluginStore } from "/@/store/plugin";
|
||||
import { useFormDialog } from "/@/use/use-dialog";
|
||||
import { useI18n } from "/src/locales";
|
||||
import { useSettingStore } from "/@/store/settings";
|
||||
|
||||
defineOptions({
|
||||
name: "PluginItemCard",
|
||||
@@ -160,11 +161,9 @@ const props = withDefaults(
|
||||
simple?: boolean;
|
||||
showConfig?: boolean;
|
||||
editable?: boolean;
|
||||
copyHandler?: (plugin: any) => void | Promise<void>;
|
||||
}>(),
|
||||
{
|
||||
source: "market",
|
||||
copyHandler: undefined,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -183,8 +182,9 @@ const { resolveOnlinePluginDependencies, installOnlinePluginChain, openDependenc
|
||||
const actionLoading = ref<PluginCardAction | "">("");
|
||||
const editDialogBodyRef = ref();
|
||||
|
||||
type PluginCardAction = "edit" | "copy" | "publish" | "config" | "install" | "uninstall" | "remove" | "toggle";
|
||||
type PluginCardAction = "edit" | "export" | "publish" | "config" | "install" | "uninstall" | "remove" | "toggle";
|
||||
|
||||
const settingStore = useSettingStore();
|
||||
const pluginCardState = computed(() => {
|
||||
const isLocal = props.source === "local";
|
||||
const isCloudPlugin = Number(props.plugin.developerId) > 0;
|
||||
@@ -192,7 +192,6 @@ const pluginCardState = computed(() => {
|
||||
const isAvailableLocally = isLocal || !!props.plugin.installed;
|
||||
const canEditPlugin = props.editable === true;
|
||||
const canManagePlugin = !isBuiltInPlugin && canEditPlugin && isAvailableLocally;
|
||||
|
||||
return {
|
||||
editPluginId: props.plugin.id,
|
||||
isLocal,
|
||||
@@ -200,11 +199,12 @@ const pluginCardState = computed(() => {
|
||||
canEditPlugin,
|
||||
isAvailableLocally,
|
||||
canManagePlugin,
|
||||
canCopyPlugin: canManagePlugin,
|
||||
canExportPlugin: canManagePlugin,
|
||||
showEditButton: canManagePlugin,
|
||||
showConfigButton: !!props.showConfig,
|
||||
showConfigButton: !!props.showConfig && isAvailableLocally && settingStore.isComm,
|
||||
isInstalled: isCloudPlugin && !!props.plugin.installed,
|
||||
isDisabled: !!props.plugin.disabled,
|
||||
showDisabled: settingStore.isComm,
|
||||
requiresVip: props.plugin.vip === "plus" || props.plugin.needPlus === true,
|
||||
canRemovePlugin: isLocal && canManagePlugin,
|
||||
isBuiltInPlugin,
|
||||
@@ -354,16 +354,26 @@ async function editPlugin() {
|
||||
});
|
||||
}
|
||||
|
||||
async function copyPlugin() {
|
||||
if (!props.copyHandler) {
|
||||
async function exportPlugin() {
|
||||
if (!pluginCardState.value.canExportPlugin || !props.plugin.id) {
|
||||
return;
|
||||
}
|
||||
await runAction(
|
||||
"copy",
|
||||
"export",
|
||||
async () => {
|
||||
await props.copyHandler?.(props.plugin);
|
||||
const content = await api.ExportPlugin(props.plugin.id);
|
||||
if (!content) {
|
||||
return;
|
||||
}
|
||||
const blob = new Blob([content], { type: "text/yaml;charset=utf-8" });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = `${props.plugin.name || props.plugin.fullName || "plugin"}.yaml`;
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
},
|
||||
{ emitChanged: true }
|
||||
{ emitChanged: false }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1052,5 +1062,4 @@ function handleVersionClick() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<script lang="ts" setup>
|
||||
import AccessSelector from "/@/views/certd/access/access-selector/index.vue";
|
||||
import { reactive, ref } from "vue";
|
||||
import { CommPluginConfig, GetCommPluginConfigs, SaveCommPluginConfigs, GetPluginByName } from "/@/views/sys/plugin/api";
|
||||
import { CommPluginConfig, GetCommPluginConfigs, SaveCommPluginConfigs, getPluginSetting } from "/@/views/sys/plugin/api";
|
||||
import { merge } from "lodash-es";
|
||||
import { notification } from "ant-design-vue";
|
||||
import { usePluginConfig } from "./use-config";
|
||||
@@ -113,7 +113,8 @@ const onFinishFailed = (errorInfo: any) => {
|
||||
const { openConfigDialog } = usePluginConfig();
|
||||
|
||||
async function doPluginConfig() {
|
||||
const certApplyInfo = await GetPluginByName("CertApply");
|
||||
const certApplyInfo: any = await getPluginSetting("CertApply");
|
||||
certApplyInfo.pluginType = "plugin";
|
||||
await openConfigDialog({
|
||||
row: certApplyInfo,
|
||||
onSuccess: async () => {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user