mirror of
https://github.com/certd/certd.git
synced 2026-10-14 09:55:57 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,51 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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,78 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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. 面板
|
||||
|
||||
| 序号 | 名称 | 说明 |
|
||||
|
||||
+1
-1
@@ -9,5 +9,5 @@
|
||||
}
|
||||
},
|
||||
"npmClient": "pnpm",
|
||||
"version": "1.44.0"
|
||||
"version": "1.44.4"
|
||||
}
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
"dependencies": {
|
||||
"copyfiles": "^2.4.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"root": "link:",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"workspaces": [
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.44.4",
|
||||
"type": "module",
|
||||
"module": "./dist/index.js",
|
||||
"main": "./dist/index.js",
|
||||
@@ -18,7 +18,7 @@
|
||||
"types"
|
||||
],
|
||||
"dependencies": {
|
||||
"@certd/basic": "^1.44.0",
|
||||
"@certd/basic": "^1.44.4",
|
||||
"@peculiar/x509": "^1.11.0",
|
||||
"asn1js": "^3.0.5",
|
||||
"axios": "^1.9.0",
|
||||
@@ -58,7 +58,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 +76,5 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "cc60cd4cd1d914110be65284bcf16cd3c5a63b8d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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
|
||||
19:17
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@certd/basic",
|
||||
"private": false,
|
||||
"version": "1.44.0",
|
||||
"version": "1.44.4",
|
||||
"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": "cc60cd4cd1d914110be65284bcf16cd3c5a63b8d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.44.4",
|
||||
"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.44.4",
|
||||
"@certd/plus-core": "^1.44.4",
|
||||
"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": "cc60cd4cd1d914110be65284bcf16cd3c5a63b8d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.44.4",
|
||||
"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": "cc60cd4cd1d914110be65284bcf16cd3c5a63b8d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.44.4",
|
||||
"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": "cc60cd4cd1d914110be65284bcf16cd3c5a63b8d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.44.4",
|
||||
"description": "jdcloud openApi sdk",
|
||||
"main": "./dist/bundle.js",
|
||||
"module": "./dist/bundle.js",
|
||||
@@ -63,5 +63,5 @@
|
||||
"fetch"
|
||||
]
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "cc60cd4cd1d914110be65284bcf16cd3c5a63b8d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.44.4",
|
||||
"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.44.4",
|
||||
"@kubernetes/client-node": "0.21.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -38,5 +38,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "cc60cd4cd1d914110be65284bcf16cd3c5a63b8d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,24 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.44.4",
|
||||
"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.44.4",
|
||||
"@certd/basic": "^1.44.4",
|
||||
"@certd/pipeline": "^1.44.4",
|
||||
"@certd/plugin-lib": "^1.44.4",
|
||||
"@certd/plus-core": "^1.44.4",
|
||||
"@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": "cc60cd4cd1d914110be65284bcf16cd3c5a63b8d"
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.44.4",
|
||||
"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": "cc60cd4cd1d914110be65284bcf16cd3c5a63b8d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.44.4",
|
||||
"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.44.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.3.12",
|
||||
@@ -38,5 +38,5 @@
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.4.2"
|
||||
},
|
||||
"gitHead": "5558376baee9e3568b00a8355a23898427efadf8"
|
||||
"gitHead": "cc60cd4cd1d914110be65284bcf16cd3c5a63b8d"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.44.4",
|
||||
"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.44.4",
|
||||
"@certd/basic": "^1.44.4",
|
||||
"@certd/pipeline": "^1.44.4",
|
||||
"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": "cc60cd4cd1d914110be65284bcf16cd3c5a63b8d"
|
||||
}
|
||||
|
||||
@@ -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,42 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [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.44.4",
|
||||
"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.44.4",
|
||||
"@certd/pipeline": "^1.44.4",
|
||||
"@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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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: "用户推广等级",
|
||||
|
||||
@@ -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">当前仅 Let's Encrypt 测试环境 可以申请 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记录;当前仅 Let's Encrypt 测试环境 可以申请。",
|
||||
async onOk() {
|
||||
router.push({
|
||||
path: "/cert/cert/dns-persist",
|
||||
|
||||
@@ -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 },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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="authorRegistered" @update:value="emit('update:modelValue', $event)" />
|
||||
<a-button type="link" size="small" :loading="loading" @click="handleAuthorAction">
|
||||
{{ authorRegistered ? "修改邮箱" : "注册作者" }}
|
||||
</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -15,7 +17,9 @@ defineOptions({ name: "PluginAuthorField" });
|
||||
const props = defineProps<{ modelValue?: string }>();
|
||||
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 isLocalAuthor = computed(() => {
|
||||
return (
|
||||
String(props.modelValue || "")
|
||||
@@ -28,7 +32,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 +43,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 () => {
|
||||
|
||||
@@ -45,10 +45,10 @@
|
||||
<script setup lang="tsx">
|
||||
import { dict, FsRender } from "@fast-crud/fast-crud";
|
||||
import { cloneDeep, merge, unset } from "lodash-es";
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { computed, onMounted, provide, reactive, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import Rollbackable from "./rollbackable.vue";
|
||||
import { usePluginStore } from "/@/store/plugin";
|
||||
import { fillPluginDefine, usePluginStore } from "/@/store/plugin";
|
||||
import * as api from "./api";
|
||||
// @ts-ignore js-yaml 没有在当前前端包中提供类型声明。
|
||||
import yaml from "js-yaml";
|
||||
@@ -106,7 +106,7 @@ const editableKeys = ref([
|
||||
},
|
||||
editRender(item: any) {
|
||||
return () => {
|
||||
return <fs-component-render {...item.component} vModel:modelValue={configForm[item.key]["value"]} scope={getScope()} />;
|
||||
return <fs-component-render {...item.component} vModel:modelValue={configForm[item.key].value} scope={getScope()} />;
|
||||
};
|
||||
},
|
||||
},
|
||||
@@ -185,9 +185,11 @@ const optionsMappingKey = {
|
||||
},
|
||||
editRender(item: any) {
|
||||
return () => {
|
||||
const options = item.component?.options || [];
|
||||
if (options.length === 0) {
|
||||
return <span class="text-gray-400">该组件没有预设选项</span>;
|
||||
const options = reactive(cloneDeep(item.component?.options || []));
|
||||
|
||||
const showAdd = options.length === 0;
|
||||
function addOption(opt: any) {
|
||||
options.push(opt);
|
||||
}
|
||||
|
||||
const onLabelChange = (optValue: string, newLabel: string) => {
|
||||
@@ -233,6 +235,13 @@ const optionsMappingKey = {
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
{showAdd && (
|
||||
<div class="mt-2">
|
||||
<a-button type="primary" size={() => addOption({ value: "", label: "" })}>
|
||||
添加选项
|
||||
</a-button>
|
||||
</div>
|
||||
)}
|
||||
<div class="helper mt-1">只需填写需要自定义的选项,留空则使用原始显示内容</div>
|
||||
</div>
|
||||
);
|
||||
@@ -267,38 +276,18 @@ function clearFormValue(key: string) {
|
||||
console.log(key, configForm);
|
||||
}
|
||||
|
||||
function getPluginOriginName() {
|
||||
if (props.plugin.fullName) {
|
||||
return props.plugin.fullName;
|
||||
}
|
||||
if (props.plugin.author && props.plugin.name) {
|
||||
return `${props.plugin.author}/${props.plugin.name}`;
|
||||
}
|
||||
return props.plugin.name;
|
||||
}
|
||||
|
||||
async function loadPluginSetting() {
|
||||
const originName = getPluginOriginName();
|
||||
currentPlugin.value = await pluginStore.getPluginDefineFromOrigin(originName);
|
||||
if (!currentPlugin.value?.input && originName !== props.plugin.name) {
|
||||
currentPlugin.value = await pluginStore.getPluginDefineFromOrigin(props.plugin.name);
|
||||
}
|
||||
if (!currentPlugin.value?.input && props.plugin.id) {
|
||||
const plugin = await api.GetObj(props.plugin.id);
|
||||
const metadata = plugin.metadata ? yaml.load(plugin.metadata) : {};
|
||||
currentPlugin.value = {
|
||||
...plugin,
|
||||
...(metadata || {}),
|
||||
};
|
||||
}
|
||||
const pluginName = props.plugin.fullName || props.plugin.name; // 这里必须两种进行兼容
|
||||
currentPlugin.value = await api.getPluginDefine(pluginName);
|
||||
fillPluginDefine(currentPlugin.value);
|
||||
const input = currentPlugin.value?.input || {};
|
||||
for (const key in input) {
|
||||
configForm[key] = {};
|
||||
}
|
||||
const setting = currentPlugin.value?.sysSetting || props.plugin.sysSetting;
|
||||
if (setting) {
|
||||
const settingJson = JSON.parse(setting);
|
||||
merge(configForm, settingJson.metadata?.input || {});
|
||||
|
||||
const pluginConfig = await api.getPluginSetting(pluginName, currentPlugin.value.type);
|
||||
if (pluginConfig?.sysSetting) {
|
||||
merge(configForm, pluginConfig.sysSetting?.metadata?.input || {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,6 +298,16 @@ onMounted(async () => {
|
||||
defineExpose({
|
||||
getForm,
|
||||
});
|
||||
|
||||
provide("getCurrentPluginDefine", () => {
|
||||
return { value: props.plugin };
|
||||
});
|
||||
provide("get:scope", () => {
|
||||
return getScope();
|
||||
});
|
||||
provide("get:plugin:type", () => {
|
||||
return props.plugin.pluginType;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
@@ -383,7 +383,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
{ label: t("certd.dns"), value: "dnsProvider" },
|
||||
{ label: t("certd.deployPlugin"), value: "deploy" },
|
||||
{ label: "通知", value: "notification" },
|
||||
{ label: "Addon", value: "addon" },
|
||||
// { label: "Addon", value: "addon" },
|
||||
],
|
||||
}),
|
||||
column: {
|
||||
@@ -463,6 +463,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
name: PluginAuthorField,
|
||||
vModel: "modelValue",
|
||||
},
|
||||
helper: "为保证插件名称引用一致性,作者一旦注册不允许修改",
|
||||
rules: [{ required: true, message: "请先注册并选择插件作者" }],
|
||||
},
|
||||
column: {
|
||||
@@ -613,6 +614,16 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
|
||||
column: {
|
||||
show: false,
|
||||
},
|
||||
valueBuilder({ row }) {
|
||||
if (typeof row.dependPlugins === "string") {
|
||||
row.dependPlugins = yaml.load(row.dependPlugins);
|
||||
}
|
||||
},
|
||||
valueResolve({ row }) {
|
||||
if (row.dependPlugins && typeof row.dependPlugins === "object") {
|
||||
row.dependPlugins = yaml.dump(row.dependPlugins);
|
||||
}
|
||||
},
|
||||
},
|
||||
"extra.dependPackages": {
|
||||
title: t("certd.thirdPartyDependencies"),
|
||||
|
||||
@@ -9,17 +9,7 @@
|
||||
<fs-crud ref="crudRef" class="plugin-card-crud" v-bind="crudBinding">
|
||||
<a-empty v-if="pluginList.length === 0" class="plugin-card-empty" />
|
||||
<div v-else class="plugin-card-grid">
|
||||
<PluginItemCard
|
||||
v-for="item of pluginList"
|
||||
:key="item.fullName"
|
||||
:source="getPluginCardSource(item)"
|
||||
:plugin="item"
|
||||
show-config
|
||||
:editable="item.editable"
|
||||
:copy-handler="copyPlugin"
|
||||
@changed="handlePluginChanged"
|
||||
@click="openPluginDetail"
|
||||
/>
|
||||
<PluginItemCard v-for="item of pluginList" :key="item.fullName" :source="getPluginCardSource(item)" :plugin="item" show-config :editable="item.editable" @changed="handlePluginChanged" @click="openPluginDetail" />
|
||||
</div>
|
||||
</fs-crud>
|
||||
</fs-page>
|
||||
@@ -80,30 +70,8 @@ async function handleDetailInstalled() {
|
||||
crudExpose.doRefresh();
|
||||
}
|
||||
|
||||
async function copyPlugin(row: any) {
|
||||
const copyRow = { ...row };
|
||||
delete copyRow.fullName;
|
||||
delete copyRow.id;
|
||||
delete copyRow.developerId;
|
||||
delete copyRow.appId;
|
||||
delete copyRow.latest;
|
||||
delete copyRow.status;
|
||||
delete copyRow.downloadCount;
|
||||
delete copyRow.score;
|
||||
await crudExpose.openCopy(
|
||||
{
|
||||
row: copyRow,
|
||||
},
|
||||
{
|
||||
async onSuccess() {
|
||||
crudExpose.doRefresh();
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function handlePluginChanged(payload: { action: string }) {
|
||||
if (payload.action === "install" || payload.action === "uninstall" || payload.action === "remove" || payload.action === "copy") {
|
||||
if (payload.action === "install" || payload.action === "uninstall" || payload.action === "remove") {
|
||||
await pluginStore.reload();
|
||||
}
|
||||
crudExpose.doRefresh();
|
||||
|
||||
@@ -49,12 +49,13 @@ export function usePluginConfig() {
|
||||
}
|
||||
}
|
||||
const res = await api.savePluginSetting({
|
||||
name: row.name,
|
||||
name: row.fullName || row.name,
|
||||
sysSetting: {
|
||||
metadata: {
|
||||
input: newForm,
|
||||
},
|
||||
},
|
||||
type: row.type,
|
||||
});
|
||||
await pluginStore.clear();
|
||||
return res;
|
||||
|
||||
@@ -14,6 +14,9 @@ type PublishManagerResult =
|
||||
| {
|
||||
action: "registered";
|
||||
}
|
||||
| {
|
||||
action: "updated";
|
||||
}
|
||||
| {
|
||||
action: "cancel";
|
||||
};
|
||||
@@ -61,7 +64,7 @@ export function usePluginPublish() {
|
||||
initialForm: {
|
||||
name: "",
|
||||
nameConfirm: "",
|
||||
desc: "",
|
||||
email: "",
|
||||
},
|
||||
async onSubmit(form: any) {
|
||||
if (form.name?.trim() !== form.nameConfirm?.trim()) {
|
||||
@@ -72,8 +75,7 @@ export function usePluginPublish() {
|
||||
}
|
||||
const author = await api.OnlinePluginAuthorAdd({
|
||||
name: form.name,
|
||||
displayName: form.displayName,
|
||||
desc: form.desc,
|
||||
email: form.email,
|
||||
});
|
||||
resolveAuthor(author);
|
||||
},
|
||||
@@ -111,6 +113,51 @@ export function usePluginPublish() {
|
||||
],
|
||||
},
|
||||
},
|
||||
email: {
|
||||
title: "作者邮箱",
|
||||
type: "text",
|
||||
form: {
|
||||
col: { span: 24 },
|
||||
rules: [
|
||||
{ required: true, message: "请输入作者邮箱" },
|
||||
{ type: "email", message: "请输入有效的作者邮箱" },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function updatePluginAuthorEmail(author?: api.OnlinePluginAuthorBean) {
|
||||
return new Promise<api.OnlinePluginAuthorBean | undefined>(async resolve => {
|
||||
let resolved = false;
|
||||
const resolveAuthor = (result?: api.OnlinePluginAuthorBean) => {
|
||||
if (!resolved) {
|
||||
resolved = true;
|
||||
resolve(result);
|
||||
}
|
||||
};
|
||||
await openFormDialog({
|
||||
title: "修改作者邮箱",
|
||||
wrapper: { width: 560, onClosed: () => resolveAuthor() },
|
||||
initialForm: { email: author?.email || "" },
|
||||
async onSubmit(form: any) {
|
||||
const result = await api.OnlinePluginAuthorUpdate({ email: form.email });
|
||||
resolveAuthor(result);
|
||||
},
|
||||
columns: {
|
||||
email: {
|
||||
title: "作者邮箱",
|
||||
type: "text",
|
||||
form: {
|
||||
col: { span: 24 },
|
||||
rules: [
|
||||
{ required: true, message: "请输入作者邮箱" },
|
||||
{ type: "email", message: "请输入有效的作者邮箱" },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -326,7 +373,26 @@ export function usePluginPublish() {
|
||||
|
||||
function renderAuthor() {
|
||||
if (info.authorRegistered) {
|
||||
return author?.displayName || author?.name || "-";
|
||||
return (
|
||||
<span class="sys-plugin-publish__author-register">
|
||||
<span>{author?.name || "-"}</span>
|
||||
<a-button
|
||||
size="small"
|
||||
type="link"
|
||||
onClick={async (event: any) => {
|
||||
event.stopPropagation();
|
||||
const updatedAuthor = await updatePluginAuthorEmail(author);
|
||||
if (!updatedAuthor?.id) {
|
||||
return;
|
||||
}
|
||||
resolveResult({ action: "updated" });
|
||||
formWrapper?.close?.();
|
||||
}}
|
||||
>
|
||||
修改邮箱
|
||||
</a-button>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span class="sys-plugin-publish__author-register">
|
||||
@@ -490,6 +556,10 @@ export function usePluginPublish() {
|
||||
await publishLocalPlugin(row, options);
|
||||
return;
|
||||
}
|
||||
if (action.action === "updated") {
|
||||
await publishLocalPlugin(row, options);
|
||||
return;
|
||||
}
|
||||
if (action.action !== "publish") {
|
||||
return;
|
||||
}
|
||||
@@ -521,10 +591,10 @@ export function usePluginPublish() {
|
||||
publishingPluginId.value = "";
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isPublishingPlugin,
|
||||
publishLocalPlugin,
|
||||
registerPluginAuthor,
|
||||
updatePluginAuthorEmail,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<template #header>
|
||||
<div class="title">
|
||||
套餐设置
|
||||
<span class="sub"> 需要<router-link :to="{ path: '/sys/settings', query: { tab: 'payment' } }">开启至少一种支付方式</router-link></span>
|
||||
<span class="sub"> 建议<router-link :to="{ path: '/sys/settings', query: { tab: 'payment' } }">开启至少一种支付方式</router-link></span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
<a-form-item label=" " :colon="false">
|
||||
<loading-button type="primary" html-type="button" :click="onClick">保存</loading-button>
|
||||
<div class="helper">需要 <router-link :to="{ path: '/sys/settings', query: { tab: 'payment' } }">开启至少一种支付方式</router-link></div>
|
||||
<div class="helper">建议 <router-link :to="{ path: '/sys/settings', query: { tab: 'payment' } }">开启至少一种支付方式</router-link></div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { request } from "/src/api/service";
|
||||
|
||||
const apiPrefix = "/sys/wallet";
|
||||
|
||||
export async function GetList(query: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/page",
|
||||
method: "post",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
export async function Recharge(data: { userId: number; amount: number; remark?: string }) {
|
||||
return await request({
|
||||
url: apiPrefix + "/recharge",
|
||||
method: "post",
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetWalletLogs(query: any) {
|
||||
return await request({
|
||||
url: apiPrefix + "/log/page",
|
||||
method: "post",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
export async function GetSimpleUserByIds(ids: number[]) {
|
||||
return await request({
|
||||
url: "/sys/authority/user/getSimpleUserByIds",
|
||||
method: "post",
|
||||
data: { ids },
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
import { CreateCrudOptionsProps, CreateCrudOptionsRet, dict, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
import { notification } from "ant-design-vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { util } from "/@/utils";
|
||||
import { useFormDialog } from "/@/use/use-dialog";
|
||||
import createCrudOptionsUser from "/@/views/sys/authority/user/crud";
|
||||
import PriceInput from "/@/views/sys/suite/product/price-input.vue";
|
||||
import * as api from "./api";
|
||||
|
||||
export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOptionsRet {
|
||||
const { openFormDialog } = useFormDialog();
|
||||
const router = useRouter();
|
||||
|
||||
const userDict = dict({
|
||||
async getNodesByValues(ids: number[]) {
|
||||
return await api.GetSimpleUserByIds(ids);
|
||||
},
|
||||
value: "id",
|
||||
label: "nickName",
|
||||
});
|
||||
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetList(query);
|
||||
};
|
||||
|
||||
function moneyColumn(title: string) {
|
||||
return {
|
||||
title,
|
||||
type: "number",
|
||||
column: {
|
||||
width: 120,
|
||||
component: {
|
||||
name: PriceInput,
|
||||
vModel: "modelValue",
|
||||
edit: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async function openRecharge(userId?: number) {
|
||||
await openFormDialog({
|
||||
title: "用户余额充值",
|
||||
wrapper: { width: 520 },
|
||||
initialForm: {
|
||||
userId: userId ?? null,
|
||||
amountYuan: null,
|
||||
remark: "",
|
||||
},
|
||||
columns: {
|
||||
userId: {
|
||||
title: "用户",
|
||||
type: "table-select",
|
||||
dict: userDict,
|
||||
form: {
|
||||
col: { span: 24 },
|
||||
rules: [{ required: true, message: "请选择用户" }],
|
||||
component: {
|
||||
disabled: userId != null,
|
||||
crossPage: true,
|
||||
multiple: false,
|
||||
select: {
|
||||
placeholder: "请选择用户",
|
||||
},
|
||||
createCrudOptions: createCrudOptionsUser,
|
||||
},
|
||||
},
|
||||
},
|
||||
amountYuan: {
|
||||
title: "充值金额",
|
||||
type: "number",
|
||||
form: {
|
||||
col: { span: 24 },
|
||||
rules: [{ required: true, message: "请输入充值金额" }],
|
||||
component: {
|
||||
name: "a-input-number",
|
||||
vModel: "value",
|
||||
min: 0,
|
||||
precision: 2,
|
||||
addonAfter: "元",
|
||||
style: { width: "100%" },
|
||||
},
|
||||
},
|
||||
},
|
||||
remark: {
|
||||
title: "备注",
|
||||
type: "text",
|
||||
form: {
|
||||
col: { span: 24 },
|
||||
helper: "选填",
|
||||
},
|
||||
},
|
||||
},
|
||||
async onSubmit(form: any) {
|
||||
await api.Recharge({
|
||||
userId: form.userId,
|
||||
amount: util.amount.toCent(form.amountYuan || 0),
|
||||
remark: form.remark,
|
||||
});
|
||||
await crudExpose.doRefresh();
|
||||
notification.success({ message: "充值成功" });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: {
|
||||
pageRequest,
|
||||
},
|
||||
toolbar: { show: false },
|
||||
actionbar: {
|
||||
buttons: {
|
||||
add: { show: false },
|
||||
recharge: {
|
||||
text: "余额充值",
|
||||
type: "primary",
|
||||
click: () => openRecharge(),
|
||||
},
|
||||
log: {
|
||||
text: "余额变更记录",
|
||||
type: "primary",
|
||||
click: () => router.push("/sys/suite/wallet-log"),
|
||||
},
|
||||
},
|
||||
},
|
||||
rowHandle: {
|
||||
width: 220,
|
||||
fixed: "right",
|
||||
buttons: {
|
||||
view: { show: false },
|
||||
edit: { show: false },
|
||||
copy: { show: false },
|
||||
remove: { show: false },
|
||||
walletLog: {
|
||||
text: "余额变更记录",
|
||||
type: "link",
|
||||
click: ({ row }) => router.push({ path: "/sys/suite/wallet-log", query: { userId: row.userId } }),
|
||||
},
|
||||
recharge: {
|
||||
text: "充值",
|
||||
type: "link",
|
||||
click: ({ row }) => openRecharge(row.userId),
|
||||
},
|
||||
},
|
||||
},
|
||||
columns: {
|
||||
id: {
|
||||
title: "ID",
|
||||
type: "number",
|
||||
column: { width: 80 },
|
||||
form: { show: false },
|
||||
},
|
||||
userId: {
|
||||
title: "用户",
|
||||
type: "table-select",
|
||||
dict: userDict,
|
||||
search: { show: true, col: { span: 3 } },
|
||||
column: { width: 160 },
|
||||
form: {
|
||||
show: false,
|
||||
component: {
|
||||
crossPage: true,
|
||||
multiple: false,
|
||||
select: {
|
||||
placeholder: "点击选择用户",
|
||||
},
|
||||
createCrudOptions: createCrudOptionsUser,
|
||||
},
|
||||
},
|
||||
},
|
||||
availableAmount: moneyColumn("可用余额"),
|
||||
totalAmount: moneyColumn("总余额"),
|
||||
frozenAmount: moneyColumn("冻结余额"),
|
||||
totalIncomeAmount: moneyColumn("累计收入"),
|
||||
totalConsumedAmount: moneyColumn("累计消费"),
|
||||
totalWithdrawAmount: moneyColumn("累计提现"),
|
||||
createTime: {
|
||||
title: "创建时间",
|
||||
type: "datetime",
|
||||
form: { show: false },
|
||||
column: { sorter: true, width: 160 },
|
||||
},
|
||||
updateTime: {
|
||||
title: "更新时间",
|
||||
type: "datetime",
|
||||
form: { show: false },
|
||||
column: { width: 160 },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">
|
||||
余额管理
|
||||
<span class="sub">管理用户余额,可给用户充值</span>
|
||||
</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";
|
||||
|
||||
defineOptions({
|
||||
name: "SysWallet",
|
||||
});
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
useMounted(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,100 @@
|
||||
import { CreateCrudOptionsRet, dict, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud";
|
||||
import { useRoute } from "vue-router";
|
||||
import { util } from "/@/utils";
|
||||
import createCrudOptionsUser from "/@/views/sys/authority/user/crud";
|
||||
import * as api from "../api";
|
||||
|
||||
function moneyText(amount: number) {
|
||||
const yuan = util.amount.toYuan(Math.abs(amount || 0));
|
||||
if (amount < 0) {
|
||||
return `-¥${yuan}`;
|
||||
}
|
||||
return `¥${yuan}`;
|
||||
}
|
||||
|
||||
export default function (): CreateCrudOptionsRet {
|
||||
const route = useRoute();
|
||||
const userIdFromQuery = route.query.userId ? Number(route.query.userId) : undefined;
|
||||
|
||||
const userDict = dict({
|
||||
async getNodesByValues(ids: number[]) {
|
||||
return await api.GetSimpleUserByIds(ids);
|
||||
},
|
||||
value: "id",
|
||||
label: "nickName",
|
||||
});
|
||||
|
||||
const pageRequest = async (query: UserPageQuery): Promise<UserPageRes> => {
|
||||
return await api.GetWalletLogs(query);
|
||||
};
|
||||
|
||||
return {
|
||||
crudOptions: {
|
||||
request: { pageRequest },
|
||||
search: {
|
||||
initialForm: { userId: userIdFromQuery },
|
||||
},
|
||||
actionbar: { show: false },
|
||||
toolbar: { show: false },
|
||||
rowHandle: { show: false },
|
||||
columns: {
|
||||
createTime: { title: "时间", type: "datetime", column: { width: 180 } },
|
||||
userId: {
|
||||
title: "用户",
|
||||
type: "table-select",
|
||||
dict: userDict,
|
||||
search: { show: true, col: { span: 3 } },
|
||||
column: { width: 160 },
|
||||
form: {
|
||||
show: false,
|
||||
component: {
|
||||
crossPage: true,
|
||||
multiple: false,
|
||||
select: { placeholder: "点击选择用户" },
|
||||
createCrudOptions: createCrudOptionsUser,
|
||||
},
|
||||
},
|
||||
},
|
||||
type: {
|
||||
title: "类型",
|
||||
type: "dict-select",
|
||||
dict: dict({
|
||||
data: [
|
||||
{ label: "收益入账", value: "income", color: "success" },
|
||||
{ label: "余额抵扣", value: "consume", color: "default" },
|
||||
{ label: "提现扣减", value: "withdraw", color: "warning" },
|
||||
],
|
||||
}),
|
||||
search: { show: true, col: { span: 3 } },
|
||||
column: { width: 120 },
|
||||
},
|
||||
amount: {
|
||||
title: "变动金额",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 120,
|
||||
cellRender({ value }) {
|
||||
const amount = Number(value || 0);
|
||||
return <span class={amount < 0 ? "text-green-500" : "text-red-500"}>{moneyText(amount)}</span>;
|
||||
},
|
||||
},
|
||||
},
|
||||
balanceAfter: {
|
||||
title: "变动后余额",
|
||||
type: "number",
|
||||
column: {
|
||||
width: 130,
|
||||
cellRender({ value }) {
|
||||
return <span class="text-red-500">{moneyText(Number(value || 0))}</span>;
|
||||
},
|
||||
},
|
||||
},
|
||||
remark: {
|
||||
title: "备注",
|
||||
type: "text",
|
||||
column: { minWidth: 220 },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<fs-page>
|
||||
<template #header>
|
||||
<div class="title">
|
||||
{{ pageTitle }}
|
||||
<span class="sub">{{ pageSub }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<fs-crud ref="crudRef" v-bind="crudBinding"> </fs-crud>
|
||||
</fs-page>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useFs } from "@fast-crud/fast-crud";
|
||||
import createCrudOptions from "./crud";
|
||||
import { useMounted } from "/@/use/use-mounted";
|
||||
|
||||
defineOptions({
|
||||
name: "SysWalletLog",
|
||||
});
|
||||
|
||||
const route = useRoute();
|
||||
const isUserLog = computed(() => route.query.userId != null);
|
||||
const pageTitle = computed(() => {
|
||||
if (isUserLog.value) {
|
||||
return "用户余额变更记录";
|
||||
}
|
||||
return "余额变更记录";
|
||||
});
|
||||
const pageSub = computed(() => {
|
||||
if (isUserLog.value) {
|
||||
return "查看该用户的余额变更明细";
|
||||
}
|
||||
return "查看所有用户的余额变更明细";
|
||||
});
|
||||
|
||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
|
||||
|
||||
// 页面打开后获取列表数据
|
||||
useMounted(async () => {
|
||||
await crudExpose.doRefresh();
|
||||
});
|
||||
</script>
|
||||
@@ -3,6 +3,45 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.44.4](https://github.com/certd/certd/compare/v1.44.3...v1.44.4) (2026-09-12)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* 新增余额管理,管理员充值功能 ([bded368](https://github.com/certd/certd/commit/bded368cbb8974b6937ae260e448c25fae5c33ad))
|
||||
* **acme-account-access:** AcmeAccount授权允许修改eab,重新生成account ([6a649aa](https://github.com/certd/certd/commit/6a649aaca083ccd3b517f7d676a58220aa33b37d))
|
||||
|
||||
## [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
|
||||
|
||||
@@ -14,6 +14,7 @@ input:
|
||||
action: onCaTypeList
|
||||
single: true
|
||||
mode: tags
|
||||
emitImmediate: true
|
||||
required: true
|
||||
mergeScript: |2-
|
||||
|
||||
@@ -42,7 +43,7 @@ input:
|
||||
|
||||
return {
|
||||
component: {
|
||||
disabled: ctx.compute(({form})=> !!form.access?.account)
|
||||
disabled: ctx.compute(({form})=> !!form.access?.account && !form.access?.editAccount)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +85,7 @@ input:
|
||||
return form.access?._needEAB === true;
|
||||
}),
|
||||
component: {
|
||||
disabled: ctx.compute(({form})=> !!form.access?.account)
|
||||
disabled: ctx.compute(({form})=> !!form.access?.account && !form.access?.editAccount)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +103,7 @@ input:
|
||||
return form.access?._needEAB === true;
|
||||
}),
|
||||
component: {
|
||||
disabled: ctx.compute(({form})=> !!form.access?.account)
|
||||
disabled: ctx.compute(({form})=> !!form.access?.account && !form.access?.editAccount)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ input:
|
||||
placeholder: api token,用户 API 令牌
|
||||
helper: |-
|
||||
前往 [获取API令牌](https://dash.cloudflare.com/profile/api-tokens),注意是令牌,不是密钥。
|
||||
token权限必须包含:[Zone区域-Zone区域-Edit编辑], [Zone区域-DNS-Edit编辑]
|
||||
token权限必须包含两条:
|
||||
[Zone区域-Zone区域-Edit编辑]
|
||||
[Zone区域-DNS-Edit编辑]
|
||||
required: true
|
||||
encrypt: true
|
||||
proxy:
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
name: fastly
|
||||
title: Fastly授权
|
||||
icon: simple-icons:fastly
|
||||
desc: Fastly CDN / TLS Custom Certificates API 授权
|
||||
input:
|
||||
apiKey:
|
||||
title: API Token
|
||||
component:
|
||||
placeholder: Fastly API Key / Token
|
||||
helper: 前往 Fastly Account Settings -> Personal Access Tokens 创建 Token
|
||||
required: true
|
||||
encrypt: true
|
||||
proxy:
|
||||
title: HTTP代理
|
||||
component:
|
||||
placeholder: http://xxxx.xxx.xx:10811
|
||||
helper: 可选:是否使用 HTTP 代理访问 Fastly API
|
||||
required: false
|
||||
encrypt: false
|
||||
testRequest:
|
||||
title: 测试
|
||||
component:
|
||||
name: api-test
|
||||
action: TestRequest
|
||||
helper: 测试授权是否正确
|
||||
pluginType: access
|
||||
type: builtIn
|
||||
scriptFilePath: /plugins/plugin-fastly/access.js
|
||||
@@ -150,6 +150,15 @@ input:
|
||||
|
||||
helper: 请选择distributions id
|
||||
order: 0
|
||||
waitForDeployed:
|
||||
title: 等待部署完成
|
||||
value: false
|
||||
helper: >-
|
||||
开启后,会轮询等待每个CloudFront分配状态变为Deployed(通常需要几分钟)再结束任务。关闭时,提交证书更新后立即结束——CloudFront会在后台自行完成部署。仅当后续任务依赖证书已全球生效时才需要开启。
|
||||
component:
|
||||
name: a-switch
|
||||
vModel: checked
|
||||
order: 0
|
||||
output: {}
|
||||
pluginType: deploy
|
||||
type: builtIn
|
||||
|
||||
@@ -90,7 +90,7 @@ input:
|
||||
5. <b>自动匹配</b>:此处无需选择校验方式,需要在[域名管理](#/certd/cert/domain)中提前配置好校验方式
|
||||
|
||||
6. <b>DNS持久验证</b>:需要先配置ACME账号和_validation-persist持久TXT记录,续期时不再增删DNS记录;当前仅
|
||||
Let's Encrypt 测试环境可以申请
|
||||
Let's Encrypt 测试环境 可以申请
|
||||
order: 0
|
||||
dnsProviderType:
|
||||
title: DNS解析服务商
|
||||
@@ -180,7 +180,7 @@ input:
|
||||
}else if (form.challengeType === 'dnses'){
|
||||
return '给每个域名单独配置dns提供商'
|
||||
}else if (form.challengeType === 'dns-persist'){
|
||||
return '请先创建并校验_validation-persist TXT持久记录,校验成功后才能提交流水线;当前仅 Let\'s Encrypt 测试环境可以申请'
|
||||
return '请先创建并校验_validation-persist TXT持久记录,校验成功后才能提交流水线;当前仅 Let\'s Encrypt 测试环境 可以申请'
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -207,7 +207,7 @@ input:
|
||||
return {
|
||||
component:{
|
||||
on: {
|
||||
"selected-change": (scope)=>{
|
||||
"selectedChange": (scope)=>{
|
||||
// 切换颁发机构后,清空已选择的ACME账号,避免账号与颁发机构不匹配
|
||||
let form = scope.form || {};
|
||||
form = form.input || form.body || form;
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
showRunStrategy: false
|
||||
default:
|
||||
strategy:
|
||||
runStrategy: 1
|
||||
name: FastlyDeployCert
|
||||
title: Fastly-部署TLS激活
|
||||
desc: 部署 Fastly 证书 (创建 TLS Activation 绑定证书到域名)
|
||||
icon: simple-icons:fastly
|
||||
group: cdn
|
||||
input:
|
||||
cert:
|
||||
title: 域名证书
|
||||
helper: 选择前置任务输出的域名证书(将自动上传到 Fastly),或选择前置【Fastly-上传证书到Fastly】任务输出的 Fastly 证书ID。
|
||||
component:
|
||||
name: output-selector
|
||||
from:
|
||||
- ':cert:'
|
||||
- FastlyUploadCert
|
||||
required: true
|
||||
order: 0
|
||||
name:
|
||||
title: 证书名称
|
||||
helper: 可选。当传入的是域名证书需要先上传时,作为 Fastly 上的自定义证书名称/标签
|
||||
component:
|
||||
placeholder: '例如: my-fastly-cert'
|
||||
required: false
|
||||
order: 0
|
||||
accessId:
|
||||
title: Access授权
|
||||
helper: Fastly 授权凭证
|
||||
component:
|
||||
name: access-selector
|
||||
type: fastly
|
||||
required: true
|
||||
order: 0
|
||||
domainIds:
|
||||
title: TLS 域名
|
||||
component:
|
||||
name: remote-select
|
||||
vModel: value
|
||||
mode: tags
|
||||
type: plugin
|
||||
action: onGetTlsDomainList
|
||||
search: false
|
||||
pager: false
|
||||
single: false
|
||||
watches:
|
||||
- certDomains
|
||||
- accessId
|
||||
required: true
|
||||
mergeScript: |2-
|
||||
|
||||
return {
|
||||
component:{
|
||||
form: ctx.compute(({form})=>{
|
||||
return form
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
helper: >-
|
||||
选择要绑定证书的域名,可多选(将为每个域名创建或更新一个 TLS 激活)。列表为你 Fastly 服务上配置的域名,请选择被证书覆盖的具体主机名(如
|
||||
www.example.com),而非通配 *.example.com(除非确有服务使用通配域名)。列表没有的也可直接输入。
|
||||
order: 0
|
||||
configurationId:
|
||||
title: TLS 配置
|
||||
component:
|
||||
name: remote-select
|
||||
vModel: value
|
||||
mode: tags
|
||||
type: plugin
|
||||
action: onGetTlsConfigurationList
|
||||
search: false
|
||||
pager: false
|
||||
single: true
|
||||
watches:
|
||||
- certDomains
|
||||
- accessId
|
||||
required: true
|
||||
mergeScript: |2-
|
||||
|
||||
return {
|
||||
component:{
|
||||
form: ctx.compute(({form})=>{
|
||||
return form
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
helper: 选择关联的 TLS 配置
|
||||
order: 0
|
||||
output: {}
|
||||
pluginType: deploy
|
||||
type: builtIn
|
||||
scriptFilePath: /plugins/plugin-fastly/plugins/plugin-deploy-to-service.js
|
||||
@@ -0,0 +1,49 @@
|
||||
showRunStrategy: false
|
||||
default:
|
||||
strategy:
|
||||
runStrategy: 1
|
||||
name: FastlyPurgeCache
|
||||
title: Fastly-清除缓存
|
||||
desc: 清除 Fastly 指定 Service 的所有缓存 (Purge All)
|
||||
icon: simple-icons:fastly
|
||||
group: cdn
|
||||
input:
|
||||
accessId:
|
||||
title: Access授权
|
||||
helper: Fastly 授权凭证
|
||||
component:
|
||||
name: access-selector
|
||||
type: fastly
|
||||
required: true
|
||||
order: 0
|
||||
serviceId:
|
||||
title: Service (服务)
|
||||
component:
|
||||
name: remote-select
|
||||
vModel: value
|
||||
mode: tags
|
||||
type: plugin
|
||||
action: onGetServiceList
|
||||
search: false
|
||||
pager: false
|
||||
single: false
|
||||
watches:
|
||||
- certDomains
|
||||
- accessId
|
||||
required: true
|
||||
mergeScript: |2-
|
||||
|
||||
return {
|
||||
component:{
|
||||
form: ctx.compute(({form})=>{
|
||||
return form
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
helper: 选择要清理所有缓存的 Fastly 服务
|
||||
order: 0
|
||||
output: {}
|
||||
pluginType: deploy
|
||||
type: builtIn
|
||||
scriptFilePath: /plugins/plugin-fastly/plugins/plugin-purge-cache.js
|
||||
@@ -0,0 +1,60 @@
|
||||
showRunStrategy: false
|
||||
default:
|
||||
strategy:
|
||||
runStrategy: 1
|
||||
name: FastlyRefreshCert
|
||||
title: Fastly-更新证书
|
||||
desc: 自动更新 Fastly CDN 上的已有自定义证书
|
||||
icon: simple-icons:fastly
|
||||
group: cdn
|
||||
input:
|
||||
cert:
|
||||
title: 域名证书
|
||||
helper: 请选择前置任务输出的域名证书
|
||||
component:
|
||||
name: output-selector
|
||||
from:
|
||||
- ':cert:'
|
||||
required: true
|
||||
order: 0
|
||||
accessId:
|
||||
title: Access授权
|
||||
helper: Fastly 授权凭证
|
||||
component:
|
||||
name: access-selector
|
||||
type: fastly
|
||||
required: true
|
||||
order: 0
|
||||
certList:
|
||||
title: 证书列表
|
||||
component:
|
||||
name: remote-select
|
||||
vModel: value
|
||||
mode: tags
|
||||
type: plugin
|
||||
action: onGetCertList
|
||||
search: false
|
||||
pager: false
|
||||
single: false
|
||||
watches:
|
||||
- certDomains
|
||||
- accessId
|
||||
required: true
|
||||
mergeScript: |2-
|
||||
|
||||
return {
|
||||
component:{
|
||||
form: ctx.compute(({form})=>{
|
||||
return form
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
helper: >-
|
||||
选择要更新的 Fastly 证书 (必须是已存在的自定义证书)。注意:Fastly
|
||||
要求更新后的证书与原证书使用相同私钥,请确保续期时复用私钥(reuse key),否则更新会失败。
|
||||
order: 0
|
||||
output: {}
|
||||
pluginType: deploy
|
||||
type: builtIn
|
||||
scriptFilePath: /plugins/plugin-fastly/plugins/plugin-refresh-cert.js
|
||||
@@ -0,0 +1,49 @@
|
||||
showRunStrategy: false
|
||||
default:
|
||||
strategy:
|
||||
runStrategy: 1
|
||||
name: FastlyUploadCert
|
||||
title: Fastly-上传证书到Fastly
|
||||
desc: 部署 / 上传 SSL/TLS 自定义证书到 Fastly CDN
|
||||
icon: simple-icons:fastly
|
||||
group: cdn
|
||||
input:
|
||||
cert:
|
||||
title: 域名证书
|
||||
helper: 请选择前置任务输出的域名证书
|
||||
component:
|
||||
name: output-selector
|
||||
from:
|
||||
- ':cert:'
|
||||
required: true
|
||||
order: 0
|
||||
accessId:
|
||||
title: Access授权
|
||||
helper: Fastly 授权凭证
|
||||
component:
|
||||
name: access-selector
|
||||
type: fastly
|
||||
required: true
|
||||
order: 0
|
||||
certificateId:
|
||||
title: 证书ID (tls_certificate_id)
|
||||
helper: >-
|
||||
可选。若填写则对 Fastly 已有证书进行更新(PATCH);留空则新建上传(POST)。注意:Fastly
|
||||
更新证书要求新证书与原证书使用相同的私钥,请确保续期时复用私钥(reuse key),否则更新会失败。
|
||||
component:
|
||||
placeholder: '例如: tls_cert_xxx (留空表示新建)'
|
||||
required: false
|
||||
order: 0
|
||||
name:
|
||||
title: 证书名称
|
||||
helper: 可选。上传到 Fastly 上的自定义证书名称/标签
|
||||
component:
|
||||
placeholder: '例如: my-fastly-cert'
|
||||
required: false
|
||||
order: 0
|
||||
output:
|
||||
fastlyCertId:
|
||||
title: Fastly 证书 ID
|
||||
pluginType: deploy
|
||||
type: builtIn
|
||||
scriptFilePath: /plugins/plugin-fastly/plugins/plugin-upload-cert.js
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@certd/ui-server",
|
||||
"version": "1.44.0",
|
||||
"version": "1.44.4",
|
||||
"description": "fast-server base midway",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
@@ -43,20 +43,20 @@
|
||||
"lint1": "eslint --fix --ext .ts src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certd/acme-client": "^1.44.0",
|
||||
"@certd/basic": "^1.44.0",
|
||||
"@certd/commercial-core": "^1.44.0",
|
||||
"@certd/acme-client": "^1.44.4",
|
||||
"@certd/basic": "^1.44.4",
|
||||
"@certd/commercial-core": "^1.44.4",
|
||||
"@certd/cv4pve-api-javascript": "^8.4.2",
|
||||
"@certd/jdcloud": "^1.44.0",
|
||||
"@certd/lib-huawei": "^1.44.0",
|
||||
"@certd/lib-k8s": "^1.44.0",
|
||||
"@certd/lib-server": "^1.44.0",
|
||||
"@certd/midway-flyway-js": "^1.44.0",
|
||||
"@certd/pipeline": "^1.44.0",
|
||||
"@certd/plugin-cert": "^1.44.0",
|
||||
"@certd/plugin-lib": "^1.44.0",
|
||||
"@certd/plugin-plus": "^1.44.0",
|
||||
"@certd/plus-core": "^1.44.0",
|
||||
"@certd/jdcloud": "^1.44.4",
|
||||
"@certd/lib-huawei": "^1.44.4",
|
||||
"@certd/lib-k8s": "^1.44.4",
|
||||
"@certd/lib-server": "^1.44.4",
|
||||
"@certd/midway-flyway-js": "^1.44.4",
|
||||
"@certd/pipeline": "^1.44.4",
|
||||
"@certd/plugin-cert": "^1.44.4",
|
||||
"@certd/plugin-lib": "^1.44.4",
|
||||
"@certd/plugin-plus": "^1.44.4",
|
||||
"@certd/plus-core": "^1.44.4",
|
||||
"@koa/cors": "^5.0.0",
|
||||
"@midwayjs/bootstrap": "3.20.11",
|
||||
"@midwayjs/cache": "3.14.0",
|
||||
|
||||
@@ -45,6 +45,7 @@ process.on("uncaughtException", error => {
|
||||
// setInterval(log, 200);
|
||||
// log()
|
||||
// }
|
||||
|
||||
// startHeapLog();
|
||||
|
||||
@Configuration({
|
||||
@@ -128,7 +129,6 @@ export class MainConfiguration {
|
||||
ctx.response.set("Cache-Control", "public,max-age=0");
|
||||
}
|
||||
});
|
||||
|
||||
//acme setlogger
|
||||
setLogger((text: string) => {
|
||||
logger.info(text);
|
||||
|
||||
@@ -3,6 +3,7 @@ import { merge } from "lodash-es";
|
||||
import { CrudController } from "@certd/lib-server";
|
||||
import {
|
||||
OnlinePluginAuthorAddReq,
|
||||
OnlinePluginAuthorUpdateReq,
|
||||
OnlinePluginDependenciesReq,
|
||||
OnlinePluginInstallReq,
|
||||
OnlinePluginListReq,
|
||||
@@ -103,11 +104,11 @@ export class PluginController extends CrudController<PluginService> {
|
||||
await this.auditLog({ content: "保存了公共插件配置" });
|
||||
return this.ok(res);
|
||||
}
|
||||
@Post("/getPluginByName", { description: "sys:settings:view" })
|
||||
async getPluginByName(@Body("name") name: string) {
|
||||
@Post("/getSetting", { description: "sys:settings:view" })
|
||||
async getSetting(@Body("name") name: string, @Body("type") type: string) {
|
||||
const res = await this.pluginConfigService.getPluginConfig({
|
||||
name: name,
|
||||
type: "builtIn",
|
||||
name,
|
||||
type,
|
||||
});
|
||||
return this.ok(res);
|
||||
}
|
||||
@@ -192,6 +193,11 @@ export class PluginController extends CrudController<PluginService> {
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
@Post("/online/author/update", { description: "sys:settings:edit", summary: "修改在线插件作者邮箱" })
|
||||
async onlineAuthorUpdate(@Body(ALL) body: OnlinePluginAuthorUpdateReq) {
|
||||
return this.ok(await this.service.updateOnlinePluginAuthor(body));
|
||||
}
|
||||
|
||||
@Post("/online/publish/info", { description: "sys:settings:view", summary: "查询本地插件发布信息" })
|
||||
async onlinePublishInfo(@Body(ALL) body: OnlinePluginPublishInfoReq) {
|
||||
const res = await this.service.getOnlinePluginPublishInfo(body);
|
||||
@@ -203,4 +209,10 @@ export class PluginController extends CrudController<PluginService> {
|
||||
const res = await this.service.exportPlugin(id);
|
||||
return this.ok(res);
|
||||
}
|
||||
|
||||
@Post("/getPluginDefine", { description: "sys:settings:view", summary: "根据插件名称获取插件定义" })
|
||||
async getPluginDefine(@Body("name") name: string) {
|
||||
const res = await this.service.getPluginDefine(name);
|
||||
return this.ok(res);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,12 @@ import { RoleService } from "../../../modules/sys/authority/service/role-service
|
||||
import { PipelineService } from "../../../modules/pipeline/service/pipeline-service.js";
|
||||
import { HistoryService } from "../../../modules/pipeline/service/history-service.js";
|
||||
import { CertInfoService, CertStatus } from "../../../modules/monitor/index.js";
|
||||
import { SiteInfoService } from "../../../modules/monitor/service/site-info-service.js";
|
||||
import { DomainService } from "../../../modules/cert/service/domain-service.js";
|
||||
import { Between, LessThan } from "typeorm";
|
||||
import { ApiTags } from "@midwayjs/swagger";
|
||||
import { UserSettingsService } from "../../../modules/mine/service/user-settings-service.js";
|
||||
import { UserStatisticSetting } from "../../../modules/mine/service/models.js";
|
||||
|
||||
export type ChartItem = {
|
||||
name: string;
|
||||
@@ -25,7 +30,24 @@ export type UserStatisticCount = {
|
||||
expiring: number;
|
||||
notExpired: number;
|
||||
};
|
||||
siteCount?: {
|
||||
total: number;
|
||||
abnormal: number;
|
||||
normal: number;
|
||||
};
|
||||
domainCount?: {
|
||||
total: number;
|
||||
expired: number;
|
||||
expiring: number;
|
||||
notExpired: number;
|
||||
};
|
||||
expiringList: any[];
|
||||
genCertCount: {
|
||||
singleDomainCertCount: number;
|
||||
multiDomainCertCount: number;
|
||||
wildcardCertCount: number;
|
||||
totalPipelineRuns: number;
|
||||
};
|
||||
};
|
||||
/**
|
||||
*/
|
||||
@@ -46,6 +68,15 @@ export class StatisticController extends BaseController {
|
||||
@Inject()
|
||||
certInfoService: CertInfoService;
|
||||
|
||||
@Inject()
|
||||
siteInfoService: SiteInfoService;
|
||||
|
||||
@Inject()
|
||||
domainService: DomainService;
|
||||
|
||||
@Inject()
|
||||
userSettingsService: UserSettingsService;
|
||||
|
||||
@Post("/count", { description: Constants.per.authOnly, summary: "查询仪表盘统计数据" })
|
||||
public async count() {
|
||||
const { userId, projectId } = await this.getProjectUserIdRead();
|
||||
@@ -57,14 +88,33 @@ export class StatisticController extends BaseController {
|
||||
const expiringList = await this.pipelineService.latestExpiringList({ userId, projectId, count: 5 });
|
||||
|
||||
const certCount = await this.certInfoService.count({ userId, projectId, status: CertStatus.active });
|
||||
const now = Date.now();
|
||||
const oneMonthLater = now + 30 * 24 * 60 * 60 * 1000;
|
||||
const userProjectQuery = this.siteInfoService.buildUserProjectQuery(userId, projectId);
|
||||
const siteTotal = await this.siteInfoService.repository.count({ where: userProjectQuery });
|
||||
const siteAbnormal = await this.siteInfoService.repository.count({
|
||||
where: { ...userProjectQuery, checkStatus: "error" },
|
||||
});
|
||||
const domainProjectQuery = this.domainService.buildUserProjectQuery(userId, projectId);
|
||||
const domainTotal = await this.domainService.repository.count({ where: domainProjectQuery });
|
||||
const domainExpired = await this.domainService.repository.count({
|
||||
where: { ...domainProjectQuery, expirationDate: LessThan(now) },
|
||||
});
|
||||
const domainExpiring = await this.domainService.repository.count({
|
||||
where: { ...domainProjectQuery, expirationDate: Between(now, oneMonthLater) },
|
||||
});
|
||||
const userStatistic = await this.userSettingsService.getSetting<UserStatisticSetting>(userId, projectId, UserStatisticSetting);
|
||||
|
||||
const count: UserStatisticCount = {
|
||||
pipelineCount,
|
||||
pipelineStatusCount,
|
||||
pipelineEnableCount,
|
||||
certCount,
|
||||
siteCount: { total: siteTotal, abnormal: siteAbnormal, normal: siteTotal - siteAbnormal },
|
||||
domainCount: { total: domainTotal, expired: domainExpired, expiring: domainExpiring, notExpired: domainTotal - domainExpired - domainExpiring },
|
||||
historyCountPerDay: historyCount,
|
||||
expiringList,
|
||||
genCertCount: userStatistic.genCertCount,
|
||||
};
|
||||
return this.ok(count);
|
||||
}
|
||||
|
||||
@@ -201,12 +201,16 @@ export class PipelineController extends CrudController<PipelineService> {
|
||||
@Post("/save", { description: Constants.per.authOnly, summary: "新增/更新流水线" })
|
||||
async save(@Body() bean: PipelineSaveDTO) {
|
||||
const { userId, projectId } = await this.getProjectUserIdWrite();
|
||||
const isNew = bean.id <= 0;
|
||||
if (bean.id < 0) {
|
||||
throw new Error("流水线ID不能小于0");
|
||||
}
|
||||
const isNew = bean.id == 0 || bean.id == null;
|
||||
if (!isNew) {
|
||||
const { userId, projectId } = await this.checkOwner(this.getService(), bean.id, "write", true);
|
||||
bean.userId = userId;
|
||||
bean.projectId = projectId;
|
||||
} else {
|
||||
delete bean.id;
|
||||
bean.userId = userId;
|
||||
bean.projectId = projectId;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { getRuntimeDepsService, initRuntimeDepsService } from "@certd/pipeline";
|
||||
import { pluginRegistry, accessRegistry, notificationRegistry } from "@certd/pipeline";
|
||||
import { dnsProviderRegistry } from "@certd/plugin-lib";
|
||||
import { addonRegistry } from "@certd/lib-server";
|
||||
import { PluginConfigService } from "../plugin/service/plugin-config-service.js";
|
||||
|
||||
@Provide()
|
||||
@Scope(ScopeEnum.Request, { allowDowngrade: true })
|
||||
@@ -13,6 +14,9 @@ export class AutoLoadPlugins {
|
||||
@Inject()
|
||||
pluginService: PluginService;
|
||||
|
||||
@Inject()
|
||||
pluginConfigService: PluginConfigService;
|
||||
|
||||
@Config("runtimeDeps")
|
||||
runtimeDepsConfig: any;
|
||||
|
||||
@@ -34,8 +38,8 @@ export class AutoLoadPlugins {
|
||||
}
|
||||
// await import("../../plugins/index.js")
|
||||
await this.pluginService.registerFromDb();
|
||||
|
||||
await registerPaymentProviders();
|
||||
await this.pluginConfigService.loadAllPluginSetting();
|
||||
registerPaymentProviders();
|
||||
logger.info(`加载插件完成,加载模式:${process.env.certd_plugin_loadmode}`);
|
||||
|
||||
//初始化第三方依赖服务
|
||||
|
||||
@@ -93,6 +93,7 @@ export class CommonEabToAcmeAccountFix {
|
||||
...certApplyConfig.sysSetting,
|
||||
input,
|
||||
},
|
||||
type: "builtIn",
|
||||
});
|
||||
}
|
||||
return true;
|
||||
|
||||
+8
-11
@@ -108,11 +108,8 @@ describe("DnsPersistRecordService", () => {
|
||||
param.id = 77;
|
||||
saved = { ...param };
|
||||
},
|
||||
async findOneBy(where: any) {
|
||||
return where.id === 77 ? saved : null;
|
||||
},
|
||||
async findOne() {
|
||||
return null;
|
||||
return saved;
|
||||
},
|
||||
};
|
||||
(service as any).accessService = {
|
||||
@@ -218,11 +215,11 @@ describe("DnsPersistRecordService", () => {
|
||||
saved = { ...saved, ...param };
|
||||
},
|
||||
async findOne(options: any) {
|
||||
if (options.where.id === 89) {
|
||||
return saved;
|
||||
}
|
||||
return options.where.domain === "example.com" && options.where.acmeAccountAccessId === 1 ? saved : null;
|
||||
},
|
||||
async findOneBy(where: any) {
|
||||
return where.id === 89 ? saved : null;
|
||||
},
|
||||
};
|
||||
(service as any).accessService = {
|
||||
async getAccessById() {
|
||||
@@ -256,8 +253,8 @@ describe("DnsPersistRecordService", () => {
|
||||
const service = new DnsPersistRecordService();
|
||||
let deletedIds: any = null;
|
||||
(service as any).repository = {
|
||||
async findOneBy(where: any) {
|
||||
return where.id === 90
|
||||
async findOne(options: any) {
|
||||
return options.where.id === 90
|
||||
? {
|
||||
id: 90,
|
||||
domain: "example.com",
|
||||
@@ -292,8 +289,8 @@ describe("DnsPersistRecordService", () => {
|
||||
status: "pending",
|
||||
};
|
||||
(service as any).repository = {
|
||||
async findOneBy(where: any) {
|
||||
return where.id === 91 ? saved : null;
|
||||
async findOne(options: any) {
|
||||
return options.where.id === 91 ? saved : null;
|
||||
},
|
||||
async save(param: any) {
|
||||
saved = { ...saved, ...param };
|
||||
|
||||
@@ -73,10 +73,11 @@ export class DomainService extends BaseService<DomainEntity> {
|
||||
if (!param.domain) {
|
||||
throw new Error("domain 不能为空");
|
||||
}
|
||||
const old = await this.repository.findOne({
|
||||
const query = this.buildUserProjectQuery(param.userId, param.projectId);
|
||||
const old = await this.findOne({
|
||||
where: {
|
||||
domain: param.domain,
|
||||
userId: param.userId,
|
||||
...query,
|
||||
},
|
||||
});
|
||||
if (old) {
|
||||
@@ -96,11 +97,11 @@ export class DomainService extends BaseService<DomainEntity> {
|
||||
if (!old) {
|
||||
throw new Error("domain记录不存在");
|
||||
}
|
||||
|
||||
const query = this.buildUserProjectQuery(param.userId, param.projectId);
|
||||
const same = await this.repository.findOne({
|
||||
where: {
|
||||
domain: param.domain,
|
||||
userId: old.userId,
|
||||
...query,
|
||||
id: Not(param.id),
|
||||
},
|
||||
});
|
||||
@@ -222,7 +223,6 @@ export class DomainService extends BaseService<DomainEntity> {
|
||||
}
|
||||
domainVerifiers[domain] = null;
|
||||
}
|
||||
|
||||
return domainVerifiers;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,20 @@ export class UserGrantSetting extends BaseSettings {
|
||||
allowAdminViewCerts = false;
|
||||
}
|
||||
|
||||
export class UserStatisticSetting extends BaseSettings {
|
||||
static __title__ = "用户累计统计";
|
||||
static __key__ = "user.statistic";
|
||||
|
||||
genCertCount = {
|
||||
singleDomainCertCount: 0,
|
||||
multiDomainCertCount: 0,
|
||||
wildcardCertCount: 0,
|
||||
totalPipelineRuns: 0,
|
||||
};
|
||||
}
|
||||
|
||||
export type UserStatisticField = `genCertCount.${keyof UserStatisticSetting["genCertCount"]}`;
|
||||
|
||||
export class UserPreferencesSetting extends BaseSettings {
|
||||
static __title__ = "用户偏好设置";
|
||||
static __key__ = "user.preferences";
|
||||
|
||||
@@ -3,7 +3,9 @@ import { InjectEntityModel } from "@midwayjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
import { BaseService, BaseSettings } from "@certd/lib-server";
|
||||
import { UserSettingsEntity } from "../entity/user-settings.js";
|
||||
import { LocalCache, mergeUtils } from "@certd/basic";
|
||||
import { LocalCache, locker, mergeUtils } from "@certd/basic";
|
||||
import { UserStatisticField, UserStatisticSetting } from "./models.js";
|
||||
import { get, set } from "lodash-es";
|
||||
const { merge } = mergeUtils;
|
||||
|
||||
const UserSettingCache = new LocalCache({
|
||||
@@ -133,4 +135,14 @@ export class UserSettingsService extends BaseService<UserSettingsEntity> {
|
||||
newEntity.setting = JSON.stringify(bean);
|
||||
await this.repository.save(newEntity);
|
||||
}
|
||||
|
||||
async incrementStatistic(userId: number, projectId: number | null | undefined, field: UserStatisticField) {
|
||||
const lockKey = `user-statistic:${userId}:${projectId ?? "global"}`;
|
||||
await locker.execute(lockKey, async () => {
|
||||
const setting = await this.getSetting<UserStatisticSetting>(userId, projectId ?? undefined, UserStatisticSetting);
|
||||
const current = Number(get(setting, field, 0)) || 0;
|
||||
set(setting, field, current + 1);
|
||||
await this.saveSetting(userId, projectId ?? undefined, setting);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,14 @@ function createSelfSignedCert(domain = "example.com") {
|
||||
};
|
||||
}
|
||||
|
||||
function createCertInfoService() {
|
||||
const service = new CertInfoService();
|
||||
service.userSettingsService = {
|
||||
async incrementStatistic() {},
|
||||
} as any;
|
||||
return service;
|
||||
}
|
||||
|
||||
describe("CertInfoService", () => {
|
||||
it("counts wildcard domains by normalized prefix", () => {
|
||||
const service = new CertInfoService();
|
||||
@@ -32,7 +40,7 @@ describe("CertInfoService", () => {
|
||||
|
||||
describe("updateCertByPipelineId 多条模式", () => {
|
||||
it("新建激活证书并绑定流水线id、继承已有记录来源", async () => {
|
||||
const service = new CertInfoService();
|
||||
const service = createCertInfoService();
|
||||
// 该流水线已有旧证书记录(upload 来源),但没有空证书记录
|
||||
const existing = { id: 10, userId: 2, projectId: 3, pipelineId: 1, fromType: "upload", status: CertStatus.inactive };
|
||||
service.repository = {
|
||||
@@ -74,8 +82,11 @@ describe("CertInfoService", () => {
|
||||
});
|
||||
|
||||
it("无已有记录时按流水线类型推导来源,并把同流水线旧激活证书标记为未激活", async () => {
|
||||
const service = new CertInfoService();
|
||||
const service = createCertInfoService();
|
||||
service.repository = {
|
||||
async find() {
|
||||
return [];
|
||||
},
|
||||
async findOne() {
|
||||
return null;
|
||||
},
|
||||
@@ -117,8 +128,11 @@ describe("CertInfoService", () => {
|
||||
});
|
||||
|
||||
it("无已有记录且流水线类型无对应来源时,使用传入来源", async () => {
|
||||
const service = new CertInfoService();
|
||||
const service = createCertInfoService();
|
||||
service.repository = {
|
||||
async find() {
|
||||
return [];
|
||||
},
|
||||
async findOne() {
|
||||
return null;
|
||||
},
|
||||
@@ -143,8 +157,11 @@ describe("CertInfoService", () => {
|
||||
});
|
||||
|
||||
it("传申请任务id时,新证书记录任务id,且只把同任务旧证书标记为未激活", async () => {
|
||||
const service = new CertInfoService();
|
||||
const service = createCertInfoService();
|
||||
service.repository = {
|
||||
async find() {
|
||||
return [];
|
||||
},
|
||||
async findOne() {
|
||||
return null;
|
||||
},
|
||||
@@ -182,10 +199,13 @@ describe("CertInfoService", () => {
|
||||
});
|
||||
|
||||
it("存在空证书记录时,申请成功后直接更新占位记录,不新建记录", async () => {
|
||||
const service = new CertInfoService();
|
||||
const service = createCertInfoService();
|
||||
// 保存流水线时创建的空证书记录(占位,certInfo 为空,已记录申请任务id)
|
||||
const emptyRecord = { id: 184, userId: 2, projectId: 1, pipelineId: 209, fromType: "pipeline", status: CertStatus.active, taskId: "PgaUhdeO1DWB_BLbIlzqV", certInfo: null };
|
||||
service.repository = {
|
||||
async find() {
|
||||
return [];
|
||||
},
|
||||
async findOne() {
|
||||
// 既有记录查询与空证书记录查询都命中同一条占位记录
|
||||
return emptyRecord;
|
||||
@@ -228,7 +248,7 @@ describe("CertInfoService", () => {
|
||||
});
|
||||
|
||||
it("updateCertByPipelineId 从流水线配置解析并记录 ACME账号授权id(供吊销读表,不再查流水线)", async () => {
|
||||
const service = new CertInfoService();
|
||||
const service = createCertInfoService();
|
||||
service.repository = {
|
||||
async findOne(args: any) {
|
||||
const where = args?.where ?? args;
|
||||
@@ -282,6 +302,9 @@ describe("CertInfoService", () => {
|
||||
it("updateDomains 保存流水线时为申请任务创建空证书记录(记录任务id)", async () => {
|
||||
const service = new CertInfoService();
|
||||
service.repository = {
|
||||
async find() {
|
||||
return [];
|
||||
},
|
||||
async findOne() {
|
||||
return null;
|
||||
},
|
||||
@@ -312,6 +335,9 @@ describe("CertInfoService", () => {
|
||||
const service = new CertInfoService();
|
||||
const existing = { id: 10, pipelineId: 1, userId: 2, projectId: 3, fromType: "auto", taskId: "apply-step-1", status: CertStatus.active };
|
||||
service.repository = {
|
||||
async find() {
|
||||
return [existing];
|
||||
},
|
||||
async findOne() {
|
||||
return existing;
|
||||
},
|
||||
@@ -337,6 +363,9 @@ describe("CertInfoService", () => {
|
||||
const service = new CertInfoService();
|
||||
let findOneCount = 0;
|
||||
service.repository = {
|
||||
async find() {
|
||||
return [];
|
||||
},
|
||||
async findOne() {
|
||||
findOneCount++;
|
||||
return null;
|
||||
@@ -374,6 +403,9 @@ describe("CertInfoService", () => {
|
||||
it("updateDomains 删除孤儿 active 记录(流水线中已不存在的任务,含 taskId 为空的遗留记录)", async () => {
|
||||
const service = new CertInfoService();
|
||||
service.repository = {
|
||||
async find() {
|
||||
return [];
|
||||
},
|
||||
async findOne() {
|
||||
return null;
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import { logger, utils } from "@certd/basic";
|
||||
import { CertInfo, CertReader } from "@certd/plugin-cert";
|
||||
import { AcmeService } from "../../../plugins/plugin-cert/plugin/cert-plugin/acme.js";
|
||||
import { PipelineEntity } from "../../pipeline/entity/pipeline.js";
|
||||
import { UserSettingsService } from "../../mine/service/user-settings-service.js";
|
||||
|
||||
export type UploadCertReq = {
|
||||
id?: number;
|
||||
@@ -39,6 +40,9 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
@InjectEntityModel(PipelineEntity)
|
||||
pipelineRepository: Repository<PipelineEntity>;
|
||||
|
||||
@Inject()
|
||||
userSettingsService: UserSettingsService;
|
||||
|
||||
//@ts-ignore
|
||||
getRepository() {
|
||||
return this.repository;
|
||||
@@ -213,6 +217,12 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
status: CertStatus.inactive,
|
||||
}
|
||||
);
|
||||
if (certUserId != null) {
|
||||
const domains = cert?.crt ? new CertReader(cert).getAltNames() : [];
|
||||
const wildcardCount = domains.filter(domain => String(domain).trim().toLowerCase().startsWith("*.")).length;
|
||||
const field = wildcardCount > 0 ? "genCertCount.wildcardCertCount" : domains.length > 1 ? "genCertCount.multiDomainCertCount" : "genCertCount.singleDomainCertCount";
|
||||
await this.userSettingsService.incrementStatistic(certUserId, certProjectId, field);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@@ -243,20 +253,28 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
}
|
||||
const userProjectQuery = this.buildUserProjectQuery(userId, projectId);
|
||||
const taskIds = applyTasks.map(task => task.taskId);
|
||||
|
||||
const foundHistory = await this.repository.find({
|
||||
where: {
|
||||
pipelineId,
|
||||
status: CertStatus.active,
|
||||
...userProjectQuery,
|
||||
},
|
||||
});
|
||||
|
||||
for (const task of applyTasks) {
|
||||
// 每个申请任务维护一条 active 记录(不重复创建,最新真证书申请成功后旧记录会被标记为 inactive)
|
||||
const found = await this.repository.findOne({
|
||||
where: {
|
||||
pipelineId,
|
||||
taskId: task.taskId,
|
||||
status: CertStatus.active,
|
||||
...userProjectQuery,
|
||||
},
|
||||
});
|
||||
const bean = new CertInfoEntity();
|
||||
let found = foundHistory.find(item => item.taskId === task.taskId);
|
||||
if (!found && foundHistory.length == 1 && applyTasks.length == 1) {
|
||||
found = foundHistory[0];
|
||||
}
|
||||
if (found) {
|
||||
//已有 active 记录(空占位或真证书):更新域名信息
|
||||
bean.id = found.id;
|
||||
if (!found.taskId) {
|
||||
bean.taskId = task.taskId;
|
||||
}
|
||||
} else {
|
||||
//创建空证书记录
|
||||
bean.pipelineId = pipelineId;
|
||||
@@ -281,7 +299,6 @@ export class CertInfoService extends BaseService<CertInfoEntity> {
|
||||
taskId: Or(IsNull(), Not(In(taskIds))),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据流水线类型推导证书来源(与流水线保存逻辑保持一致)
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user