Compare commits

..

62 Commits

Author SHA1 Message Date
xiaojunnuo 01568ca148 feat: 通过插件配置懒加载依赖,动态加载第三方依赖包,精简安装镜像大小 2026-06-19 17:44:57 +08:00
xiaojunnuo 0d97ad67c5 chore: 1 2026-06-17 00:39:04 +08:00
xiaojunnuo c94a5537a3 chore: 1 2026-06-17 00:26:50 +08:00
xiaojunnuo d88dfc197e chore: 1 2026-06-17 00:04:24 +08:00
xiaojunnuo f709c05c0d build(ui): 重构Dockerfile支持多架构 2026-06-16 23:41:40 +08:00
xiaojunnuo 2c609da7a1 build: release 2026-06-16 23:15:03 +08:00
xiaojunnuo d770c7bd08 chore: 1 2026-06-16 23:09:33 +08:00
xiaojunnuo b9ccd4a8a0 chore: 1 2026-06-16 22:30:21 +08:00
xiaojunnuo 8875e8059f chore: ncurses-base 退格键 ^H的问题 2026-06-16 00:27:32 +08:00
xiaojunnuo 6490366c68 chore: 1 2026-06-16 00:21:24 +08:00
xiaojunnuo c278946771 chore(nettest): 修复跨平台端口测试匹配逻辑 2026-06-16 00:15:42 +08:00
xiaojunnuo 1562d9de36 chore: 1 2026-06-15 23:39:47 +08:00
xiaojunnuo 5bb0990abb chore: 1 2026-06-15 23:32:32 +08:00
xiaojunnuo bfd3cacc68 perf: 优化ACME账号字段的选择提示 2026-06-15 23:30:13 +08:00
xiaojunnuo c7e1163d59 chore: 改回dnsResultOrder不使用默认ipv4 2026-06-15 23:26:20 +08:00
xiaojunnuo fba7aeb71b Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-06-15 23:24:46 +08:00
xiaojunnuo c66a2bd77a perf: 基础镜像改成node:22-trixie-slim,对网络兼容性更好 2026-06-15 23:24:06 +08:00
xiaojunnuo ed58ae3c53 perf: 优化阿里云API网关增加翻页查询 2026-06-15 10:02:02 +08:00
xiaojunnuo 194463bea9 perf: dns默认ipv4first 2026-06-14 23:18:37 +08:00
xiaojunnuo 260f5ae777 fix: 修复jdk证书格式的问题 2026-06-14 23:18:17 +08:00
xiaojunnuo e85d824337 chore:1 2026-06-14 22:21:07 +08:00
xiaojunnuo e17fc39709 chore: 1 2026-06-14 22:14:59 +08:00
xiaojunnuo da9b297b12 chore: 1 2026-06-14 21:46:06 +08:00
xiaojunnuo 807dfcd57a chore: 尝试使用 node:22.22-trixie-slim 2026-06-14 21:43:18 +08:00
xiaojunnuo 0a410db52a build: publish 2026-06-14 21:30:16 +08:00
xiaojunnuo 4501095106 build: trigger build image 2026-06-14 21:30:05 +08:00
xiaojunnuo bc731e4fb1 v1.41.4 2026-06-14 21:29:12 +08:00
xiaojunnuo 53561d2755 build: prepare to build 2026-06-14 21:25:11 +08:00
xiaojunnuo e913fe509c chore: 优化代码格式 2026-06-14 20:59:38 +08:00
xiaojunnuo a3a215b7ae perf(plugin): 增加 Dynadot DNS and access 插件
新增Dynadot域名解析提供商插件,包含API授权配置和DNS记录增删查功能
2026-06-14 17:23:24 +08:00
xiaojunnuo 56f2949ac5 chore: 1 2026-06-14 15:07:38 +08:00
xiaojunnuo c1b5a35f90 fix: 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug 2026-06-14 14:57:47 +08:00
xiaojunnuo 48ab1fbffe build: release 2026-06-12 00:17:31 +08:00
xiaojunnuo 5f078273b3 build: publish 2026-06-11 23:57:37 +08:00
xiaojunnuo 636338f9ed build: trigger build image 2026-06-11 23:57:24 +08:00
xiaojunnuo 6cbd629777 v1.41.3 2026-06-11 23:56:12 +08:00
xiaojunnuo 5a07dce759 build: prepare to build 2026-06-11 23:53:30 +08:00
xiaojunnuo 15484bc119 perf: 首页夜间模式主图切换为黑色背景 2026-06-11 23:51:45 +08:00
xiaojunnuo d6cd9d136d fix: 修复litessl无法申请证书,报authorization must be pending 错误的问题 2026-06-11 23:40:44 +08:00
xiaojunnuo c76815756b chore: 1 2026-06-11 01:17:48 +08:00
xiaojunnuo eef93250ac build: release 2026-06-11 00:25:37 +08:00
xiaojunnuo a1c6cf0477 build: release 2026-06-11 00:20:14 +08:00
xiaojunnuo 14a0ccac93 chore: popularize agents.md 2026-06-11 00:07:02 +08:00
xiaojunnuo 9439743b7e build: publish 2026-06-10 23:39:06 +08:00
xiaojunnuo acbac6a9c3 build: trigger build image 2026-06-10 23:38:54 +08:00
xiaojunnuo cc38ccd0e9 v1.41.2 2026-06-10 23:37:43 +08:00
xiaojunnuo 7d0cf846ac build: prepare to build 2026-06-10 23:34:12 +08:00
xiaojunnuo f9541fab70 perf: 新增站点证书监控从DNS解析记录批量导入功能
本次提交新增了从DNS解析记录批量导入站点监控的完整功能:
1. 扩展Registrable类型新增icon字段支持
2. 新增DNS解析记录获取接口和基础实现
3. 为阿里云、腾讯云、Cloudflare等DNS提供商添加解析记录分页获取支持
4. 新增站点监控导入任务管理功能,支持保存、启动、删除导入任务
5. 新增中文/英文多语言支持
6. 优化暗黑模式表格样式
7. 修复ACME账户访问修复逻辑中项目ID可选的问题
8. 优化HiPM DNS提供商的域名获取逻辑
2026-06-10 23:32:39 +08:00
xiaojunnuo 8d9870e9c6 Merge branch 'v2' into v2-dev 2026-06-09 23:10:45 +08:00
HINS 0f3f8519e0 perf: 优化 HiPM DNSMgr 插件,添加域名查询双层策略 (#744) @WUHINS
- 新增 getDomainId() 方法,首选 keyword 直接查询(O(1))
- 列表匹配作为降级方案(向后兼容)
- 性能提升 99%,减少 99% 数据传输
- 与 ddns-go hipmdnsmgr.go 实现保持一致
2026-06-09 23:10:15 +08:00
xiaojunnuo 016ae865b1 fix(cert-plugin): 修复DNS提供商授权无法回显的bug 2026-06-09 23:08:45 +08:00
xiaojunnuo 3e9953a74a chore: 1 2026-06-08 16:54:33 +08:00
xiaojunnuo 1fc80d2b93 chore: 1 2026-06-08 16:52:36 +08:00
xiaojunnuo b55fe2ef19 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-06-08 16:48:39 +08:00
xiaojunnuo 71030b7e27 chore: handsfree.work 域名 修改 2026-06-08 16:48:30 +08:00
greper 5e8bdac008 Revert "perf: 添加AWS Rate Limit应对措施 (#748)" (#749)
This reverts commit 56b8c689ec.
2026-06-08 11:43:10 +08:00
Steven Zhu 56b8c689ec perf: 添加AWS Rate Limit应对措施 (#748)
* Parse PEM chain and import certificate chain

Split the PEM in certInfo.crt into a leaf certificate and intermediate chain (using a lookbehind regex), trim the blocks, and pass the chain to ImportCertificateCommand only when present. Replace console.log with this.logger.info and log the returned CertificateArn. This ensures the leaf cert is uploaded separately from its chain and avoids sending an empty CertificateChain.

* Add AWS retry & CloudFront deployment wait

Introduce robust retry and polling helpers to handle AWS throttling and CloudFront propagation. Added AwsClient.withRetry (exponential backoff, handles common throttling errors, default 5 attempts/base 2s) and waitForDistributionDeployed (polls until distribution Status is "Deployed", default 10min timeout/15s interval). Update deploy-to-cloudfront plugin to use withRetry for Get/UpdateDistribution and importCertificate, pass AwsClient into uploadToACM, and wait for each distribution to finish deploying before continuing to avoid PreconditionFailed errors. Improves reliability when facing rate limits and global CloudFront propagation delays; adds informative logging for retry and deployment status.
2026-06-08 10:29:11 +08:00
Steven Zhu 454912d314 fix: Parse PEM chain and import certificate chain (#747)
Split the PEM in certInfo.crt into a leaf certificate and intermediate chain (using a lookbehind regex), trim the blocks, and pass the chain to ImportCertificateCommand only when present. Replace console.log with this.logger.info and log the returned CertificateArn. This ensures the leaf cert is uploaded separately from its chain and avoids sending an empty CertificateChain.
2026-06-08 10:28:39 +08:00
xiaojunnuo 61e3f5761c build: release 2026-06-06 03:06:48 +08:00
xiaojunnuo 2908569841 chore: 1 2026-06-06 03:02:47 +08:00
xiaojunnuo 775226b49f build: publish 2026-06-06 02:38:20 +08:00
xiaojunnuo e3dacb5b3f build: trigger build image 2026-06-06 02:38:08 +08:00
173 changed files with 3884 additions and 504 deletions
+3 -2
View File
@@ -37,5 +37,6 @@ pnpm-lock.yaml
.studio/ .studio/
# Certd 推广报告,仅本地使用 # Certd 推广报告,仅本地使用
/popularize/ /popularize/reports/
output/
.uploads/
@@ -0,0 +1,412 @@
# 插件依赖按需加载方案
## 背景与目标
### 当前问题
- `packages/ui/certd-server/node_modules` 包含 50+ 个插件的所有依赖,体积庞大
- 大量云厂商 SDK(AWS、阿里云、腾讯云、华为云等)只在特定插件中使用
- 用户通常只使用少数几个插件,但必须安装所有依赖
### 目标
实现依赖的按需下载和加载:
1. 插件依赖独立管理,不占用主 `node_modules` 空间
2. 只有当用户首次使用某插件时,才动态下载该插件需要的依赖
3. 依赖安装完成后,通过 `await import()` 从独立路径加载
4. 保持现有插件代码的最小改动
## 当前架构分析
### 插件加载机制
- 插件位于 `packages/ui/certd-server/src/plugins/` 下(50+ 个插件目录)
- `AutoLoadPlugins` 类在启动时扫描 `dist/plugins` 目录并动态导入
- 插件注册到不同的 registry`accessRegistry`, `pluginRegistry`, `dnsProviderRegistry`
- 插件代码已经使用 `await import()` 进行懒加载(如 `await import("@aws-sdk/client-acm")`
### 重型依赖分布
`packages/ui/certd-server/package.json` 分析,以下依赖体积大且仅特定插件使用:
**云厂商 SDK(按插件分组):**
- **AWS 插件**`@aws-sdk/client-acm`, `@aws-sdk/client-cloudfront`, `@aws-sdk/client-iam`, `@aws-sdk/client-route-53`, `@aws-sdk/client-s3`, `@aws-sdk/client-sts`
- **阿里云插件**`@alicloud/openapi-client`, `@alicloud/pop-core`, `@alicloud/tea-typescript`, `@alicloud/fc20230330`
- **腾讯云插件**`tencentcloud-sdk-nodejs`, `cos-nodejs-sdk-v5`
- **华为云插件**`@huaweicloud/huaweicloud-sdk-cdn`, `@huaweicloud/huaweicloud-sdk-core`
- **Azure 插件**`@azure/arm-dns`, `@azure/identity`
- **Google Cloud 插件**`@google-cloud/dns`, `@google-cloud/publicca`
- **火山引擎插件**`@volcengine/openapi`, `@volcengine/tos-sdk`
**网络/工具库:**
- `ssh2`, `socks`, `socks-proxy-agent`SSH 相关插件)
- `ali-oss`, `qiniu`, `basic-ftp`(存储/传输插件)
- `nodemailer`(邮件通知插件)
**通用依赖(保留在主 package.json):**
- `@midwayjs/*` 系列(框架核心)
- `@certd/*` 系列(项目内部包)
- `axios`, `lodash-es`, `dayjs`, `js-yaml` 等基础工具
## 设计方案
### 架构概览
```
packages/ui/certd-server/
├── package.json # 主依赖(框架、通用工具)
├── node_modules/ # 主依赖安装目录
├── optional-deps/ # 新增:可选依赖管理目录
│ ├── package.json # 可选依赖总配置(用于 pnpm install
│ ├── pnpm-lock.yaml # 可选依赖锁文件
│ └── node_modules/ # 可选依赖安装目录
├── src/
│ └── modules/
│ └── dependency/ # 新增:依赖管理模块
│ ├── dependency-manager.ts # 核心:依赖管理器
│ ├── dependency-registry.ts # 依赖注册表(插件 -> 依赖映射)
│ └── types.ts # 类型定义
```
### 核心组件
#### 1. 依赖管理器(DependencyManager
**职责:**
- 检查依赖是否已安装
- 动态执行 `pnpm install` 安装缺失依赖
- 提供从 `optional-deps/node_modules` 加载依赖的方法
- 并发控制:避免多个插件同时触发安装
**关键方法:**
```typescript
class DependencyManager {
// 确保依赖已安装,返回依赖模块
async ensureAndImport<T>(packageName: string): Promise<T>
// 检查依赖是否已安装
async isInstalled(packageName: string): Promise<boolean>
// 安装依赖(带锁,避免并发)
async installDependencies(packages: string[]): Promise<void>
// 从 optional-deps/node_modules 加载依赖
async loadModule<T>(packageName: string): Promise<T>
}
```
**实现要点:**
- 使用文件锁(如 `proper-lockfile`)防止并发安装
- 安装前检查 `optional-deps/node_modules/{packageName}` 是否存在
- 安装命令:`pnpm install --dir optional-deps --ignore-workspace`
- 加载时使用绝对路径:`import('file:///absolute/path/to/optional-deps/node_modules/package')`
#### 2. 依赖注册表(DependencyRegistry
**职责:**
- 维护插件名称到依赖列表的映射
- 提供依赖查询接口
**数据结构:**
```typescript
interface PluginDependencyConfig {
pluginName: string;
dependencies: {
packageName: string;
version: string;
optional?: boolean; // 是否可选(安装失败不阻塞)
}[];
}
// 示例注册
dependencyRegistry.register('plugin-aws', [
{ packageName: '@aws-sdk/client-acm', version: '^3.964.0' },
{ packageName: '@aws-sdk/client-cloudfront', version: '^3.964.0' },
{ packageName: '@aws-sdk/client-route-53', version: '^3.964.0' },
]);
```
#### 3. 插件集成
**改造现有插件代码:**
改造前(`plugin-aws/libs/aws-client.ts`):
```typescript
const { ACMClient, ImportCertificateCommand } = await import("@aws-sdk/client-acm");
```
改造后:
```typescript
import { DependencyManager } from "../../../modules/dependency/dependency-manager.js";
const depManager = new DependencyManager();
const { ACMClient, ImportCertificateCommand } = await depManager.ensureAndImport("@aws-sdk/client-acm");
```
**简化方案(推荐):**
创建辅助函数,减少改动量:
```typescript
// src/modules/dependency/import-helper.ts
export async function importOptionalDep<T>(packageName: string): Promise<T> {
const depManager = new DependencyManager();
return await depManager.ensureAndImport<T>(packageName);
}
// 插件中使用
import { importOptionalDep } from "../../../modules/dependency/import-helper.js";
const { ACMClient } = await importOptionalDep("@aws-sdk/client-acm");
```
### 实施步骤
#### 阶段一:基础设施搭建
1. 创建 `optional-deps/` 目录结构
2. 生成 `optional-deps/package.json`(包含所有可选依赖)
3. 实现 `DependencyManager` 核心逻辑
4. 实现依赖安装锁机制
5. 编写单元测试
#### 阶段二:依赖迁移
6. 从主 `package.json` 移除可选依赖
7. 将依赖添加到 `optional-deps/package.json`
8. 创建依赖注册表,映射插件到依赖
#### 阶段三:插件改造
9. 创建 `import-helper.ts` 辅助函数
10. 逐步改造插件代码,使用 `importOptionalDep` 加载依赖
11. 优先改造重型依赖(AWS、阿里云、腾讯云等)
#### 阶段四:测试与优化
12. 端到端测试:验证依赖按需安装和加载
13. 性能优化:缓存已加载的模块
14. 错误处理:安装失败时的降级策略
15. 文档:编写使用说明和迁移指南
## 关键技术决策
### 1. 依赖分组策略
**选择:按插件分组**
- 每个插件声明自己需要的依赖
- 优点:职责清晰,易于维护
- 缺点:可能有重复依赖(但 pnpm 会去重)
**备选:按功能分组**
- 将依赖按功能分组(如 "aws-deps", "aliyun-deps"
- 优点:更细粒度控制
- 缺点:增加复杂度
### 2. 安装触发时机
**选择:首次使用时触发**
- 在插件的 `execute()``getClient()` 方法中触发安装
- 优点:真正的按需加载
- 缺点:首次使用有延迟
**备选:启动时预检查**
- 启动时扫描启用的插件,预安装依赖
- 优点:避免运行时延迟
- 缺点:可能安装不需要的依赖
### 3. 依赖路径解析
**选择:使用绝对路径 + `file://` 协议**
```typescript
const modulePath = path.resolve(__dirname, '../../optional-deps/node_modules', packageName);
return await import(`file://${modulePath}/index.js`);
```
**原因:**
- Node.js ESM 要求明确的 URL 格式
- 避免模块解析冲突
### 4. 并发控制
**选择:文件锁 + 内存锁双重保护**
- 使用 `proper-lockfile` 锁定 `optional-deps/` 目录
- 内存中使用 `Map` 记录正在安装的依赖
- 避免多个插件同时触发安装
### 5. 错误处理
**策略:**
- 安装失败时记录日志,抛出明确的错误信息
- 提供手动安装命令提示:`请运行: cd optional-deps && pnpm install`
- 支持降级:某些非核心依赖安装失败时,插件可以部分功能可用
## 验证方案
### 单元测试
1. 测试 `DependencyManager.isInstalled()` 正确检测依赖状态
2. 测试 `DependencyManager.installDependencies()` 成功安装依赖
3. 测试并发安装时的锁机制
4. 测试从 `optional-deps/node_modules` 加载模块
### 集成测试
1. 清空 `optional-deps/node_modules`
2. 启动服务,验证不触发安装
3. 调用 AWS 插件,验证触发安装并成功加载
4. 再次调用,验证不重复安装
5. 验证主 `node_modules` 体积减少
### 性能测试
1. 测量首次安装依赖的耗时
2. 测量后续加载的耗时(应该与正常 import 相近)
3. 对比改造前后的 `node_modules` 大小
## 风险与挑战
### 1. 首次使用延迟
**风险:** 用户首次使用插件时需要等待依赖安装(可能几十秒)
**缓解:**
- 在 UI 上显示安装进度
- 提供预安装命令:`pnpm run install-optional-deps`
- 文档说明首次使用会有延迟
### 2. 离线环境
**风险:** 离线环境无法下载依赖
**缓解:**
- 提供完整安装包(包含所有可选依赖)
- 支持手动复制 `node_modules`
### 3. 版本冲突
**风险:** 可选依赖与主依赖版本冲突
**缓解:**
- 使用 `--ignore-workspace` 隔离安装
- 定期同步主依赖版本
### 4. TypeScript 类型
**风险:** 动态导入的类型推断
**缓解:**
- 保留 `@types/*` 在主 `devDependencies`
- 使用泛型和类型断言
## 预期收益
1. **空间节省:**`node_modules` 体积减少 60-70%(估算)
2. **安装速度:** 初始 `pnpm install` 速度提升 3-5 倍
3. **用户体验:** 不使用的插件不占用空间,按需加载
4. **维护性:** 依赖分组清晰,易于管理
## 后续优化
1. **依赖预热:** 在后台预安装常用插件依赖
2. **依赖缓存:** 支持从 CDN 或本地缓存安装
3. **依赖更新:** 提供命令批量更新可选依赖
4. **插件市场:** 支持从远程下载插件及其依赖配置
## 附录:依赖分类清单
### 可选依赖(迁移到 optional-deps/package.json
**AWS 相关(plugin-aws, plugin-aws-cn):**
```json
{
"@aws-sdk/client-acm": "^3.964.0",
"@aws-sdk/client-cloudfront": "^3.964.0",
"@aws-sdk/client-iam": "^3.964.0",
"@aws-sdk/client-route-53": "^3.964.0",
"@aws-sdk/client-s3": "^3.964.0",
"@aws-sdk/client-sts": "^3.990.0"
}
```
**阿里云相关(plugin-aliyun, plugin-lib/aliyun):**
```json
{
"@alicloud/fc20230330": "^4.1.7",
"@alicloud/openapi-client": "^0.4.12",
"@alicloud/openapi-util": "^0.3.2",
"@alicloud/pop-core": "^1.7.10",
"@alicloud/sts-sdk": "^1.0.2",
"@alicloud/tea-typescript": "^1.8.0",
"@alicloud/tea-util": "^1.4.10",
"ali-oss": "^6.21.0"
}
```
**腾讯云相关(plugin-tencent, plugin-lib/tencent):**
```json
{
"tencentcloud-sdk-nodejs": "^4.1.112",
"cos-nodejs-sdk-v5": "^2.14.6"
}
```
**华为云相关(plugin-huawei):**
```json
{
"@huaweicloud/huaweicloud-sdk-cdn": "3.1.185",
"@huaweicloud/huaweicloud-sdk-core": "3.1.185",
"@huaweicloud/huaweicloud-sdk-elb": "3.1.185",
"@huaweicloud/huaweicloud-sdk-iam": "3.1.185",
"esdk-obs-nodejs": "^3.25.6"
}
```
**Azure 相关(plugin-azure):**
```json
{
"@azure/arm-dns": "^5.1.0",
"@azure/identity": "^4.13.1"
}
```
**Google Cloud 相关(plugin-google, plugin-cert/google):**
```json
{
"@google-cloud/dns": "^5.3.1",
"@google-cloud/publicca": "^1.3.0"
}
```
**火山引擎相关(plugin-volcengine):**
```json
{
"@volcengine/openapi": "^1.28.1",
"@volcengine/tos-sdk": "^2.9.1"
}
```
**SSH/网络相关(plugin-host, plugin-lib/ssh):**
```json
{
"ssh2": "^1.17.0",
"socks": "^2.8.3",
"socks-proxy-agent": "^8.0.4",
"basic-ftp": "^5.0.5"
}
```
**其他存储/传输(plugin-qiniu, plugin-lib/qiniu):**
```json
{
"qiniu": "^7.12.0"
}
```
**邮件通知(plugin-notification/email):**
```json
{
"nodemailer": "^6.9.16"
}
```
### 主依赖(保留在主 package.json
**框架核心:**
- `@midwayjs/*` 系列
- `@koa/cors`
- `typeorm`, `better-sqlite3`, `mysql2`, `pg`
**项目内部包:**
- `@certd/*` 系列
**通用工具:**
- `axios`, `lodash-es`, `dayjs`, `js-yaml`
- `crypto-js`, `jsonwebtoken`, `bcryptjs`
- `reflect-metadata`, `uuid`, `nanoid`
- 等等
## 总结
本方案通过引入独立的可选依赖管理机制,实现了插件依赖的按需下载和加载。核心思路是:
1. **隔离管理:**`optional-deps/` 目录下维护独立的 `package.json``node_modules`
2. **动态安装:** 通过 `DependencyManager` 在首次使用时触发 `pnpm install`
3. **路径加载:** 使用绝对路径从独立目录加载依赖模块
4. **最小改动:** 通过辅助函数 `importOptionalDep` 简化插件代码改造
该方案可以显著减少主 `node_modules` 体积,提升初始安装速度,同时保持现有架构的兼容性和可维护性。
-13
View File
@@ -1,13 +0,0 @@
你是一名资深nodejs工程师,擅长开发Certd开源系统的任务插件。
certd是一款全自动证书申请部署管理工具,基于流水线的方式,通过里面申请证书插件申请证书,然后将证书传递给下一个部署任务插件,不同的部署任务插件将证书部署到用户的各个应用系统当中。
certd插件分成以下几种类型:
Access:存储用户的第三放应用的授权数据,比如用户名密码,accessSecret 或 accessToken等。同时它里面的方法还负责对接第三方的api接口
Task 部署任务插件,它继承AbstractTaskPlugin类,被流水线调用execute方法,将证书部署到对应的应用上
DnsProvider: DNS提供商插件,它用于在ACME申请证书时给域名添加txt解析记录。
注意事项:
1、使用技能:在开始工作前,请阅读并加载.trae/skills下面的技能,根据skills进行相应的插件开发
2、迭代技能:当开发过程用户提醒你更好的做法时,你需要总结经验,更新相应的skills,让skills越来越完善,能够在以后得新插件开发中具备指导意义。
3、一般调用的api接口文档会比较复杂,你不知道接口是什么时,请务必询问用户,让用户提供API接口文档
4、完成开发后无需测试,通知用户自己去测试
+2
View File
@@ -54,10 +54,12 @@ Certd 是支持私有化部署的 SSL/TLS 证书自动化管理平台,提供 W
- 先读本文;需要代码导航、目录入口、参考文件或验证命令时读 `.codex/repo-map.md` - 先读本文;需要代码导航、目录入口、参考文件或验证命令时读 `.codex/repo-map.md`
- 任务涉及后端、前端、插件、测试或代码风格时,先读取 `.codex/agent-rules/` 下对应规则文件,再查看具体代码。 - 任务涉及后端、前端、插件、测试或代码风格时,先读取 `.codex/agent-rules/` 下对应规则文件,再查看具体代码。
- 在 PowerShell 中读取中文、Markdown、locale、文档类文件时,显式使用 `Get-Content -Encoding utf8`;如果仍乱码,再执行 `[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()` 后重试。 - 在 PowerShell 中读取中文、Markdown、locale、文档类文件时,显式使用 `Get-Content -Encoding utf8`;如果仍乱码,再执行 `[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()` 后重试。
- 在 PowerShell 中使用 `rg` 搜索包含引号、括号、反斜杠等特殊字符的模式时,优先用单引号包裹整个 pattern,例如 `rg 'await import\("tencentcloud-sdk-nodejs' packages/ui/certd-server/src -g '*.ts'`;不要在双引号字符串里再直接写未转义的 `"`,否则 PowerShell 会截断参数并把后半段当成文件路径,出现 `The string is missing the terminator``rg: xxx: 系统找不到指定的文件`
- 做后端任务时,先定位 `packages/ui/certd-server/src/modules` 下的模块,以及相关 entity/service/controller。 - 做后端任务时,先定位 `packages/ui/certd-server/src/modules` 下的模块,以及相关 entity/service/controller。
- 做前端任务时,先定位 `packages/ui/certd-client/src/views/certd` 下的页面,再找对应 `src/api` - 做前端任务时,先定位 `packages/ui/certd-client/src/views/certd` 下的页面,再找对应 `src/api`
- 做服务商、DNS、部署、通知相关任务时,先看 `packages/ui/certd-server/src/plugins`,再看 `packages/plugins/plugin-lib` 里的共享辅助能力。 - 做服务商、DNS、部署、通知相关任务时,先看 `packages/ui/certd-server/src/plugins`,再看 `packages/plugins/plugin-lib` 里的共享辅助能力。
- 优先沿用现有模块、插件、服务模式,再考虑新增抽象;避免为了形式上的“复用”制造过度设计。 - 优先沿用现有模块、插件、服务模式,再考虑新增抽象;避免为了形式上的“复用”制造过度设计。
- 为了提升可读性,不要把一个方法调用链直接塞进另一个方法的参数里;应先用有意义的局部变量承载返回值,再把变量传入下一步调用。
- 实现新功能或修复行为缺陷前,优先补对应单元测试并确认红灯,再实现代码并跑聚焦验证。确实不适合先写测试时,在回复中说明原因和替代验证方式。 - 实现新功能或修复行为缺陷前,优先补对应单元测试并确认红灯,再实现代码并跑聚焦验证。确实不适合先写测试时,在回复中说明原因和替代验证方式。
- 后补单元测试时,先按正确行为写预期;如果红灯需要修改既有实现,先向用户确认这是 bug 还是既有需求,避免未经确认改变行为。 - 后补单元测试时,先按正确行为写预期;如果红灯需要修改既有实现,先向用户确认这是 bug 还是既有需求,避免未经确认改变行为。
- 优先对改动包运行聚焦测试或格式化/ESLint;只有跨包影响明显时再考虑更大范围构建。 - 优先对改动包运行聚焦测试或格式化/ESLint;只有跨包影响明显时再考虑更大范围构建。
+37
View File
@@ -3,6 +3,43 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
### Bug Fixes
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
### Performance Improvements
* **plugin:** 增加 Dynadot DNS and access 插件 ([a3a215b](https://github.com/certd/certd/commit/a3a215b7ae2b90efcde91270ce4165bbfe77dc64))
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
### Bug Fixes
* 修复litessl无法申请证书,报authorization must be pending 错误的问题 ([d6cd9d1](https://github.com/certd/certd/commit/d6cd9d136d2812b2335917305f36d6d9414507ad))
### Performance Improvements
* 首页夜间模式主图切换为黑色背景 ([15484bc](https://github.com/certd/certd/commit/15484bc119fef7a0ca7f3fdab01d665fde47e688))
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Bug Fixes
* **cert-plugin:** 修复DNS提供商授权无法回显的bug ([016ae86](https://github.com/certd/certd/commit/016ae865b1d914fe5792e77a08e3ab5358df5f89))
* Parse PEM chain and import certificate chain ([#747](https://github.com/certd/certd/issues/747)) ([454912d](https://github.com/certd/certd/commit/454912d31407d350cbd170953ccbd0564e74fd6c))
### Performance Improvements
* 添加AWS Rate Limit应对措施 ([#748](https://github.com/certd/certd/issues/748)) ([56b8c68](https://github.com/certd/certd/commit/56b8c689ec2b5cff49010a8c765483dd36803e9d))
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
* 优化 HiPM DNSMgr 插件,添加域名查询双层策略 ([#744](https://github.com/certd/certd/issues/744)) @WUHINS ([0f3f851](https://github.com/certd/certd/commit/0f3f8519e04d95cb848e28b98a3d4fcbed481fce))
### Reverts
* Revert "perf: 添加AWS Rate Limit应对措施 (#748)" (#749) ([5e8bdac](https://github.com/certd/certd/commit/5e8bdac00850bed4f5f2a272bee42c490730ec21)), closes [#748](https://github.com/certd/certd/issues/748) [#749](https://github.com/certd/certd/issues/749)
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
### Performance Improvements ### Performance Improvements
+4 -1
View File
@@ -179,7 +179,10 @@ https://certd.handfree.work/
2. 您的需求我们将优先实现,并且可能将作为专业版功能提供 2. 您的需求我们将优先实现,并且可能将作为专业版功能提供
3. 获得专业版功能 3. 获得专业版功能
[50元专业版优惠券限时领取](https://app.handfree.work/subject/#/app/certd/product)
> [50元专业版优惠券限时领取](https://app.handfree.work/subject/#/app/certd/product) https://app.handfree.work/subject/#/app/certd/product
> app.handfree.work是Certd官方激活码购买平台
专业版、商业版特权对比 专业版、商业版特权对比
+47 -1
View File
@@ -1,9 +1,55 @@
# Change Log # Change Log
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
### Bug Fixes
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
### Performance Improvements
* **plugin:** 增加 Dynadot DNS and access 插件 ([a3a215b](https://github.com/certd/certd/commit/a3a215b7ae2b90efcde91270ce4165bbfe77dc64))
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
### Bug Fixes
* 修复litessl无法申请证书,报authorization must be pending 错误的问题 ([d6cd9d1](https://github.com/certd/certd/commit/d6cd9d136d2812b2335917305f36d6d9414507ad))
### Performance Improvements
* 首页夜间模式主图切换为黑色背景 ([15484bc](https://github.com/certd/certd/commit/15484bc119fef7a0ca7f3fdab01d665fde47e688))
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Bug Fixes
* **cert-plugin:** 修复DNS提供商授权无法回显的bug ([016ae86](https://github.com/certd/certd/commit/016ae865b1d914fe5792e77a08e3ab5358df5f89))
* Parse PEM chain and import certificate chain ([#747](https://github.com/certd/certd/issues/747)) ([454912d](https://github.com/certd/certd/commit/454912d31407d350cbd170953ccbd0564e74fd6c))
### Performance Improvements
* 添加AWS Rate Limit应对措施 ([#748](https://github.com/certd/certd/issues/748)) ([56b8c68](https://github.com/certd/certd/commit/56b8c689ec2b5cff49010a8c765483dd36803e9d))
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
* 优化 HiPM DNSMgr 插件,添加域名查询双层策略 ([#744](https://github.com/certd/certd/issues/744)) @WUHINS ([0f3f851](https://github.com/certd/certd/commit/0f3f8519e04d95cb848e28b98a3d4fcbed481fce))
### Reverts
* Revert "perf: 添加AWS Rate Limit应对措施 (#748)" (#749) ([5e8bdac](https://github.com/certd/certd/commit/5e8bdac00850bed4f5f2a272bee42c490730ec21)), closes [#748](https://github.com/certd/certd/issues/748) [#749](https://github.com/certd/certd/issues/749)
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
### Performance Improvements
* 流水线、监控站点支持导出 ([99fd308](https://github.com/certd/certd/commit/99fd3083f259cdb96fd656f04858dd708d1251c7))
* 优化列表页面请求两次的问题 ([5546af5](https://github.com/certd/certd/commit/5546af518e92c765513787ccaf8e856be789bcf9))
* 优化邀请注册流程 ([7a71e45](https://github.com/certd/certd/commit/7a71e45799d782d0691606fb42b4236f1d3009b0))
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
* **volcengine-vke:** 火山VKE集群证书支持两种类型的证书保密字典 ([77b8024](https://github.com/certd/certd/commit/77b802445322d576d54d194f7c505da49e0e824c))
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04) # [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
### Bug Fixes ### Bug Fixes
+1 -1
View File
@@ -6,7 +6,7 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具 关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具
| 官方开源地址: | | | &nbsp;|官方开源地址: |
| ---- | ---- | | ---- | ---- |
| [Github](https://github.com/certd/certd)| ![](https://img.shields.io/github/stars/certd/certd?logo=github) | | [Github](https://github.com/certd/certd)| ![](https://img.shields.io/github/stars/certd/certd?logo=github) |
| [Gitee](https://gitee.com/certd/certd) | ![](https://gitee.com/certd/certd/badge/star.svg?theme=dark) | | [Gitee](https://gitee.com/certd/certd) | ![](https://gitee.com/certd/certd/badge/star.svg?theme=dark) |
+48 -47
View File
@@ -33,53 +33,54 @@
| 29.| **彩虹DNS** | 彩虹DNS管理系统授权 | | 29.| **彩虹DNS** | 彩虹DNS管理系统授权 |
| 30.| **多吉云** | | | 30.| **多吉云** | |
| 31.| **Dokploy授权** | | | 31.| **Dokploy授权** | |
| 32.| **farcdn授权** | | | 32.| **Dynadot授权** | |
| 33.| **FlexCDN授权** | | | 33.| **farcdn授权** | |
| 34.| **Gcore** | Gcore | | 34.| **FlexCDN授权** | |
| 35.| **Github授权** | | | 35.| **Gcore** | Gcore |
| 36.| **godaddy授权** | | | 36.| **Github授权** | |
| 37.| **HiPM DNSMgr** | HiPM DNSMgr API Token 授权 | | 37.| **godaddy授权** | |
| 38.| **金山云授权** | | | 38.| **HiPM DNSMgr** | HiPM DNSMgr API Token 授权 |
| 39.| **FTP授权** | | | 39.| **金山云授权** | |
| 40.| **七牛OSS授权** | | | 40.| **FTP授权** | |
| 41.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 | | 41.| **七牛OSS授权** | |
| 42.| **s3/minio授权** | S3/minio oss授权 | | 42.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 |
| 43.| **namesilo授权** | | | 43.| **s3/minio授权** | S3/minio oss授权 |
| 44.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 | | 44.| **namesilo授权** | |
| 45.| **Nginx Proxy Manager 授权** | 用于登录 Nginx Proxy Manager,并为代理主机证书部署提供授权。 | | 45.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 |
| 46.| **1panel授权** | 账号和密码 | | 46.| **Nginx Proxy Manager 授权** | 用于登录 Nginx Proxy Manager,并为代理主机证书部署提供授权。 |
| 47.| **支付宝** | | | 47.| **1panel授权** | 账号和密码 |
| 48.| **白山云授权** | | | 48.| **支付宝** | |
| 49.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 | | 49.| **白山云授权** | |
| 50.| **cdnfly授权** | | | 50.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 |
| 51.| **k8s授权** | | | 51.| **cdnfly授权** | |
| 52.| **括彩云cdn授权** | 括彩云CDN,每月免费30G[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) | | 52.| **k8s授权** | |
| 53.| **LeCDN授权** | | | 53.| **括彩云cdn授权** | 括彩云CDN,每月免费30G[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
| 54.| **lucky** | | | 54.| **LeCDN授权** | |
| 55.| **猫云授权** | | | 55.| **lucky** | |
| 56.| **plesk授权** | | | 56.| **猫云授权** | |
| 57.| **长亭雷池授权** | | | 57.| **plesk授权** | |
| 58.| **群晖登录授权** | | | 58.| **长亭雷池授权** | |
| 59.| **uniCloud** | unicloud授权 | | 59.| **群晖登录授权** | |
| 60.| **微信支付** | | | 60.| **uniCloud** | unicloud授权 |
| 61.| **易盾rcdn授权** | 易盾CDN,每月免费30G[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) | | 61.| **微信支付** | |
| 62.| **易发云短信** | sms.yfyidc.cn/ | | 62.| **易盾rcdn授权** | 易盾CDN,每月免费30G[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
| 63.| **易盾DCDN授权** | https://user.yiduncdn.com | | 63.| **易发云短信** | sms.yfyidc.cn/ |
| 64.| **易支付** | | | 64.| **易盾DCDN授权** | https://user.yiduncdn.com |
| 65.| **proxmox** | | | 65.| **易支付** | |
| 66.| **Spaceship.com 授权** | Spaceship.com API 授权插件 | | 66.| **proxmox** | |
| 67.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器授权 | | 67.| **Spaceship.com 授权** | Spaceship.com API 授权插件 |
| 68.| **UCloud授权** | 优刻得授权 | | 68.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器授权 |
| 69.| **又拍云** | | | 69.| **UCloud授权** | 优刻得授权 |
| 70.| **网宿授权** | | | 70.| **又拍云** | |
| 71.| **西部数码授权** | | | 71.| **网宿授权** | |
| 72.| **我爱云授权** | 我爱云CDN | | 72.| **西部数码授权** | |
| 73.| **新网授权(代理方式)** | | | 73.| **我爱云授权** | 我爱云CDN |
| 74.| **新网授权** | | | 74.| **新网授权(代理方式)** | |
| 75.| **新网互联授权** | 仅支持代理账号,ip需要加入白名单 | | 75.| **新网授权** | |
| 76.| **Zenlayer授权** | Zenlayer授权 | | 76.| **新网互联授权** | 仅支持代理账号,ip需要加入白名单 |
| 77.| **GoEdge授权** | | | 77.| **Zenlayer授权** | Zenlayer授权 |
| 78.| **雨云授权** | https://app.rainyun.com/ | | 78.| **GoEdge授权** | |
| 79.| **雨云授权** | https://app.rainyun.com/ |
<style module> <style module>
table th:first-of-type { table th:first-of-type {
+16 -15
View File
@@ -13,21 +13,22 @@
| 9.| **BIND9 DNS** | 通过 SSH 连接到 BIND9 服务器,使用 nsupdate 命令管理 DNS 记录 | | 9.| **BIND9 DNS** | 通过 SSH 连接到 BIND9 服务器,使用 nsupdate 命令管理 DNS 记录 |
| 10.| **cloudflare** | cloudflare dns provider | | 10.| **cloudflare** | cloudflare dns provider |
| 11.| **dns.la** | dns.la | | 11.| **dns.la** | dns.la |
| 12.| **godaddy** | GoDaddy | | 12.| **Dynadot** | Dynadot DNS提供商 |
| 13.| **HiPM DNSMgr** | HiPM DNSMgr DNS 解析提供商 | | 13.| **godaddy** | GoDaddy |
| 14.| **华为云** | 华为云DNS解析提供商 | | 14.| **HiPM DNSMgr** | HiPM DNSMgr DNS 解析提供商 |
| 15.| **namesilo** | namesilo dns provider | | 15.| **华为云** | 华为云DNS解析提供商 |
| 16.| **雨云** | 雨云DNS解析提供商 | | 16.| **namesilo** | namesilo dns provider |
| 17.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器 | | 17.| **雨云** | 雨云DNS解析提供商 |
| 18.| **腾讯云** | 腾讯云域名DNS解析提供者 | | 18.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器 |
| 19.| **腾讯云EO DNS** | 腾讯云EO DNS解析提供者 | | 19.| **腾讯云** | 腾讯云域名DNS解析提供者 |
| 20.| **西部数码** | west dns provider | | 20.| **腾讯云EO DNS** | 腾讯云EO DNS解析提供者 |
| 21.| **Google Cloud DNS** | Google Cloud DNS提供商 | | 21.| **西部数码** | west dns provider |
| 22.| **Dns提供商Demo** | dns provider示例 | | 22.| **Google Cloud DNS** | Google Cloud DNS提供商 |
| 23.| **彩虹DNS** | 彩虹DNS管理系统 | | 23.| **Dns提供商Demo** | dns provider示例 |
| 24.| **Spaceship** | Spaceship 域名解析 | | 24.| **彩虹DNS** | 彩虹DNS管理系统 |
| 25.| **51dns** | 51DNS | | 25.| **Spaceship** | Spaceship 域名解析 |
| 26.| **新网互联** | 新网互联 | | 26.| **51dns** | 51DNS |
| 27.| **新网互联** | 新网互联 |
<style module> <style module>
table th:first-of-type { table th:first-of-type {
View File
+1 -1
View File
@@ -9,5 +9,5 @@
} }
}, },
"npmClient": "pnpm", "npmClient": "pnpm",
"version": "1.41.1" "version": "1.41.4"
} }
+1 -2
View File
@@ -15,8 +15,7 @@
"vitepress-plugin-lightbox": "^1.0.2" "vitepress-plugin-lightbox": "^1.0.2"
}, },
"scripts": { "scripts": {
"start": "lerna bootstrap --hoist", "start": "cd ./packages/ui/certd-server && pnpm start",
"start:server": "cd ./packages/ui/certd-server && pnpm start",
"devb": "lerna run dev-build", "devb": "lerna run dev-build",
"i-all": "lerna link && lerna exec npm install ", "i-all": "lerna link && lerna exec npm install ",
"publish": "pnpm run prepublishOnly2 && lerna publish --force-publish=pro/plus-core --conventional-commits && pnpm run afterpublishOnly ", "publish": "pnpm run prepublishOnly2 && lerna publish --force-publish=pro/plus-core --conventional-commits && pnpm run afterpublishOnly ",
+14
View File
@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/publishlab/node-acme-client/compare/v1.41.3...v1.41.4) (2026-06-14)
**Note:** Version bump only for package @certd/acme-client
## [1.41.3](https://github.com/publishlab/node-acme-client/compare/v1.41.2...v1.41.3) (2026-06-11)
### Bug Fixes
* 修复litessl无法申请证书,报authorization must be pending 错误的问题 ([d6cd9d1](https://github.com/publishlab/node-acme-client/commit/d6cd9d136d2812b2335917305f36d6d9414507ad))
## [1.41.2](https://github.com/publishlab/node-acme-client/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/acme-client
## [1.41.1](https://github.com/publishlab/node-acme-client/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/publishlab/node-acme-client/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/acme-client **Note:** Version bump only for package @certd/acme-client
+3 -3
View File
@@ -3,7 +3,7 @@
"description": "Simple and unopinionated ACME client", "description": "Simple and unopinionated ACME client",
"private": false, "private": false,
"author": "nmorsman", "author": "nmorsman",
"version": "1.41.1", "version": "1.41.4",
"type": "module", "type": "module",
"module": "./dist/index.js", "module": "./dist/index.js",
"main": "./dist/index.js", "main": "./dist/index.js",
@@ -18,7 +18,7 @@
"types" "types"
], ],
"dependencies": { "dependencies": {
"@certd/basic": "^1.41.1", "@certd/basic": "^1.41.4",
"@peculiar/x509": "^1.11.0", "@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5", "asn1js": "^3.0.5",
"axios": "^1.9.0", "axios": "^1.9.0",
@@ -76,5 +76,5 @@
"bugs": { "bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues" "url": "https://github.com/publishlab/node-acme-client/issues"
}, },
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c" "gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
} }
@@ -0,0 +1,69 @@
import assert from "node:assert/strict";
import auto from "./auto.js";
import { createCsr, createPrivateRsaKey } from "./crypto/index.js";
declare const describe: any;
declare const it: any;
describe("auto challenge status polling", () => {
it("polls the authorization URL after completing a challenge", async () => {
const [, csr] = await createCsr({ commonName: "example.com" }, await createPrivateRsaKey());
const challenge = {
type: "dns-01",
url: "https://ca.example/chall/1",
token: "token",
};
const authz = {
status: "pending",
identifier: { type: "dns", value: "example.com" },
url: "https://ca.example/authz/1",
challenges: [challenge],
};
const order = {
status: "pending",
url: "https://ca.example/order/1",
finalize: "https://ca.example/order/1/finalize",
authorizations: [authz.url],
};
const polledUrls: string[] = [];
const originalSetTimeout = globalThis.setTimeout;
(globalThis as any).setTimeout = (fn: (...args: any[]) => void) => originalSetTimeout(fn, 0);
try {
const certificate = await auto(
{
logger: { info: () => {} },
sslProvider: "litessl",
getAccountUrl: () => "https://ca.example/acct/1",
createOrder: async () => order,
getAuthorizations: async () => [authz],
getChallengeKeyAuthorization: async () => "key-authorization",
verifyChallenge: async () => {},
completeChallenge: async () => ({ ...challenge, status: "processing" }),
waitForValidStatus: async (item: { url: string }) => {
polledUrls.push(item.url);
return { ...item, status: "valid" };
},
finalizeOrder: async () => ({ ...order, status: "valid", certificate: "https://ca.example/cert/1" }),
getCertificate: async () => "CERTIFICATE",
} as any,
{
csr,
termsOfServiceAgreed: true,
waitDnsDiffuseTime: 0,
challengeCreateFn: async (_authz: any, keyAuthorizationGetter: (challenge: any) => Promise<string>) => ({
challenge,
keyAuthorization: await keyAuthorizationGetter(challenge),
}),
challengeRemoveFn: async () => {},
}
);
assert.equal(certificate, "CERTIFICATE");
assert.deepEqual(polledUrls, [authz.url]);
} finally {
(globalThis as any).setTimeout = originalSetTimeout;
}
});
});
+1 -1
View File
@@ -172,7 +172,7 @@ export default async (client, userOpts) => {
} }
challengeCompleted = true; challengeCompleted = true;
log(`[auto] [${d}] 等待返回valid状态`); log(`[auto] [${d}] 等待返回valid状态`);
await client.waitForValidStatus(challenge,d); await client.waitForValidStatus(authz,d);
}); });
+1 -1
View File
@@ -263,4 +263,4 @@ export function createChallengeFn(opts = {}) {
// createChallengeFn({logger:{info:console.log}}).walkDnsChallengeRecord("handsfree.work") // createChallengeFn({logger:{info:console.log}}).walkDnsChallengeRecord("handfree.work")
+16
View File
@@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
### Bug Fixes
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/basic
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Performance Improvements
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
### Performance Improvements ### Performance Improvements
+1 -1
View File
@@ -1 +1 @@
02:33 21:25
+2 -2
View File
@@ -1,7 +1,7 @@
{ {
"name": "@certd/basic", "name": "@certd/basic",
"private": false, "private": false,
"version": "1.41.1", "version": "1.41.4",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@@ -52,5 +52,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c" "gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
} }
+14
View File
@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
**Note:** Version bump only for package @certd/pipeline
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/pipeline
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Performance Improvements
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/pipeline **Note:** Version bump only for package @certd/pipeline
+4 -4
View File
@@ -1,7 +1,7 @@
{ {
"name": "@certd/pipeline", "name": "@certd/pipeline",
"private": false, "private": false,
"version": "1.41.1", "version": "1.41.4",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@@ -19,8 +19,8 @@
"compile": "tsc --skipLibCheck --watch" "compile": "tsc --skipLibCheck --watch"
}, },
"dependencies": { "dependencies": {
"@certd/basic": "^1.41.1", "@certd/basic": "^1.41.4",
"@certd/plus-core": "^1.41.1", "@certd/plus-core": "^1.41.4",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"reflect-metadata": "^0.1.13" "reflect-metadata": "^0.1.13"
@@ -49,5 +49,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c" "gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
} }
+23 -1
View File
@@ -3,6 +3,7 @@ import { FormItemProps } from "../dt/index.js";
import { HttpClient, ILogger, utils } from "@certd/basic"; import { HttpClient, ILogger, utils } from "@certd/basic";
import * as _ from "lodash-es"; import * as _ from "lodash-es";
import { PluginRequestHandleReq } from "../plugin/index.js"; import { PluginRequestHandleReq } from "../plugin/index.js";
import { IServiceGetter } from "../service/index.js";
// export type AccessRequestHandleReqInput<T = any> = { // export type AccessRequestHandleReqInput<T = any> = {
// id?: number; // id?: number;
@@ -20,6 +21,8 @@ export type AccessInputDefine = FormItemProps & {
export type AccessDefine = Registrable & { export type AccessDefine = Registrable & {
icon?: string; icon?: string;
subtype?: string; subtype?: string;
dependPlugins?: Record<string, string>;
dependPackages?: Record<string, string>;
input?: { input?: {
[key: string]: AccessInputDefine; [key: string]: AccessInputDefine;
}; };
@@ -39,13 +42,32 @@ export type AccessContext = {
logger: ILogger; logger: ILogger;
utils: typeof utils; utils: typeof utils;
accessService: IAccessService; accessService: IAccessService;
serviceGetter?: IServiceGetter;
define?: AccessDefine;
}; };
export abstract class BaseAccess implements IAccess { export abstract class BaseAccess implements IAccess {
ctx!: AccessContext; ctx!: AccessContext;
runtimeDepsService?: {
ensureRuntimeDependencies(pluginKeys: string | string[]): Promise<any>;
importRuntime(specifier: string): Promise<any>;
};
setCtx(ctx: AccessContext) { async importRuntime(specifier: string) {
if (!this.runtimeDepsService) {
return await import(specifier);
}
return await this.runtimeDepsService.importRuntime(specifier);
}
async setCtx(ctx: AccessContext) {
this.ctx = ctx; this.ctx = ctx;
if (!this.runtimeDepsService && this.ctx.serviceGetter) {
this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService");
}
if (this.runtimeDepsService && this.ctx.define?.name) {
await this.runtimeDepsService.ensureRuntimeDependencies(`access:${this.ctx.define.name}`);
}
} }
async onRequest(req: AccessRequestHandleReq) { async onRequest(req: AccessRequestHandleReq) {
@@ -67,7 +67,9 @@ export async function newAccess(type: string, input: any, accessService: IAccess
accessService, accessService,
}; };
} }
access.setCtx(ctx); ctx.define = ctx.define || register.define;
access.runtimeDepsService = (accessService as any).runtimeDepsService;
await access.setCtx(ctx);
access._type = type; access._type = type;
return access; return access;
} }
+1 -1
View File
@@ -387,7 +387,7 @@ export class Executor {
}), }),
serviceGetter: this.options.serviceGetter, serviceGetter: this.options.serviceGetter,
}; };
instance.setCtx(taskCtx); await instance.setCtx(taskCtx);
await instance.onInstance(); await instance.onInstance();
const result = await instance.execute(); const result = await instance.execute();
+23 -2
View File
@@ -3,7 +3,7 @@ import { Registrable } from "../registry/index.js";
import { FormItemProps, HistoryResult, Pipeline } from "../dt/index.js"; import { FormItemProps, HistoryResult, Pipeline } from "../dt/index.js";
import { HttpClient, ILogger, utils } from "@certd/basic"; import { HttpClient, ILogger, utils } from "@certd/basic";
import * as _ from "lodash-es"; import * as _ from "lodash-es";
import { IEmailService } from "../service/index.js"; import { IEmailService, IServiceGetter } from "../service/index.js";
export type NotificationBody = { export type NotificationBody = {
userId?: number; userId?: number;
@@ -39,6 +39,8 @@ export type NotificationInputDefine = FormItemProps & {
}; };
export type NotificationDefine = Registrable & { export type NotificationDefine = Registrable & {
needPlus?: boolean; needPlus?: boolean;
dependPlugins?: Record<string, string>;
dependPackages?: Record<string, string>;
input?: { input?: {
[key: string]: NotificationInputDefine; [key: string]: NotificationInputDefine;
}; };
@@ -78,6 +80,8 @@ export type NotificationContext = {
logger: ILogger; logger: ILogger;
utils: typeof utils; utils: typeof utils;
emailService: IEmailService; emailService: IEmailService;
serviceGetter?: IServiceGetter;
define?: NotificationDefine;
}; };
export abstract class BaseNotification implements INotification { export abstract class BaseNotification implements INotification {
@@ -85,6 +89,17 @@ export abstract class BaseNotification implements INotification {
ctx!: NotificationContext; ctx!: NotificationContext;
http!: HttpClient; http!: HttpClient;
logger!: ILogger; logger!: ILogger;
runtimeDepsService?: {
ensureRuntimeDependencies(pluginKeys: string | string[]): Promise<any>;
importRuntime(specifier: string): Promise<any>;
};
async importRuntime(specifier: string) {
if (!this.runtimeDepsService) {
return await import(specifier);
}
return await this.runtimeDepsService.importRuntime(specifier);
}
async doSend(body: NotificationBody) { async doSend(body: NotificationBody) {
return await this.send(body); return await this.send(body);
@@ -93,10 +108,16 @@ export abstract class BaseNotification implements INotification {
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
async onInstance() {} async onInstance() {}
setCtx(ctx: NotificationContext) { async setCtx(ctx: NotificationContext) {
this.ctx = ctx; this.ctx = ctx;
this.http = ctx.http; this.http = ctx.http;
this.logger = ctx.logger; this.logger = ctx.logger;
if (!this.runtimeDepsService && this.ctx.serviceGetter) {
this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService");
}
if (this.runtimeDepsService && this.ctx.define?.name) {
await this.runtimeDepsService.ensureRuntimeDependencies(`notification:${this.ctx.define.name}`);
}
} }
setDefine = (define: NotificationDefine) => { setDefine = (define: NotificationDefine) => {
this.define = define; this.define = define;
@@ -61,7 +61,8 @@ export async function newNotification(type: string, input: any, ctx: Notificatio
throw new Error("ctx is required"); throw new Error("ctx is required");
} }
plugin.setDefine(register.define); plugin.setDefine(register.define);
plugin.setCtx(ctx); ctx.define = ctx.define || register.define;
await plugin.setCtx(ctx);
await plugin.onInstance(); await plugin.onInstance();
return plugin; return plugin;
} }
+22 -1
View File
@@ -46,6 +46,8 @@ export type PluginDefine = Registrable & {
default?: any; default?: any;
group?: string; group?: string;
icon?: string; icon?: string;
dependPlugins?: Record<string, string>;
dependPackages?: Record<string, string>;
input?: { input?: {
[key: string]: TaskInputDefine; [key: string]: TaskInputDefine;
}; };
@@ -73,6 +75,8 @@ export type ITaskPlugin = {
onInstance(): Promise<void>; onInstance(): Promise<void>;
execute(): Promise<void | string>; execute(): Promise<void | string>;
onRequest(req: PluginRequestHandleReq<any>): Promise<any>; onRequest(req: PluginRequestHandleReq<any>): Promise<any>;
setCtx(ctx: TaskInstanceContext): Promise<void>;
importRuntime?(specifier: string): Promise<any>;
[key: string]: any; [key: string]: any;
}; };
@@ -146,6 +150,17 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
logger!: ILogger; logger!: ILogger;
http!: HttpClient; http!: HttpClient;
accessService!: IAccessService; accessService!: IAccessService;
runtimeDepsService?: {
ensureRuntimeDependencies(pluginKeys: string | string[]): Promise<any>;
importRuntime(specifier: string): Promise<any>;
};
async importRuntime(specifier: string) {
if (!this.runtimeDepsService) {
return await import(specifier);
}
return await this.runtimeDepsService.importRuntime(specifier);
}
clearLastStatus() { clearLastStatus() {
this._result.clearLastStatus = true; this._result.clearLastStatus = true;
@@ -161,11 +176,17 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
} }
} }
setCtx(ctx: TaskInstanceContext) { async setCtx(ctx: TaskInstanceContext) {
this.ctx = ctx; this.ctx = ctx;
this.logger = ctx.logger; this.logger = ctx.logger;
this.accessService = ctx.accessService; this.accessService = ctx.accessService;
this.http = ctx.http; this.http = ctx.http;
if (!this.runtimeDepsService && this.ctx.serviceGetter) {
this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService");
}
if (this.runtimeDepsService && this.ctx.define?.name) {
await this.runtimeDepsService.ensureRuntimeDependencies(`plugin:${this.ctx.define.name}`);
}
// 将证书加入secret // 将证书加入secret
// @ts-ignore // @ts-ignore
if (this.cert && this.cert.crt && this.cert.key) { if (this.cert && this.cert.crt && this.cert.key) {
@@ -7,6 +7,7 @@ export type Registrable = {
group?: string; group?: string;
deprecated?: string; deprecated?: string;
order?: number; order?: number;
icon?: string;
}; };
export type TargetGetter<T> = () => Promise<T>; export type TargetGetter<T> = () => Promise<T>;
export type RegistryItem<T> = { export type RegistryItem<T> = {
+14
View File
@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
### Bug Fixes
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/lib-huawei
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/lib-huawei
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/lib-huawei **Note:** Version bump only for package @certd/lib-huawei
+4 -3
View File
@@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-huawei", "name": "@certd/lib-huawei",
"private": false, "private": false,
"version": "1.41.1", "version": "1.41.4",
"main": "./dist/bundle.js", "main": "./dist/bundle.js",
"module": "./dist/bundle.js", "module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts", "types": "./dist/d/index.d.ts",
@@ -12,7 +12,8 @@
"dev-build": "npm run build", "dev-build": "npm run build",
"preview": "vite preview", "preview": "vite preview",
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests", "test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
"pub": "npm publish" "pub": "npm publish",
"compile": "npm run build"
}, },
"dependencies": { "dependencies": {
"axios": "^1.9.0", "axios": "^1.9.0",
@@ -27,5 +28,5 @@
"prettier": "^2.8.8", "prettier": "^2.8.8",
"tslib": "^2.8.1" "tslib": "^2.8.1"
}, },
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c" "gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
} }
+14
View File
@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
### Bug Fixes
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/lib-iframe
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/lib-iframe
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/lib-iframe **Note:** Version bump only for package @certd/lib-iframe
+4 -3
View File
@@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-iframe", "name": "@certd/lib-iframe",
"private": false, "private": false,
"version": "1.41.1", "version": "1.41.4",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@@ -15,7 +15,8 @@
"build2": "vue-tsc --noEmit && vite build", "build2": "vue-tsc --noEmit && vite build",
"preview": "vite preview", "preview": "vite preview",
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests", "test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
"pub": "npm publish" "pub": "npm publish",
"compile": "npm run build"
}, },
"dependencies": { "dependencies": {
"nanoid": "^4.0.0" "nanoid": "^4.0.0"
@@ -34,5 +35,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c" "gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
} }
+14
View File
@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
### Bug Fixes
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/jdcloud
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/jdcloud
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/jdcloud **Note:** Version bump only for package @certd/jdcloud
+4 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@certd/jdcloud", "name": "@certd/jdcloud",
"version": "1.41.1", "version": "1.41.4",
"description": "jdcloud openApi sdk", "description": "jdcloud openApi sdk",
"main": "./dist/bundle.js", "main": "./dist/bundle.js",
"module": "./dist/bundle.js", "module": "./dist/bundle.js",
@@ -10,7 +10,8 @@
"build": "npm run before-build && rollup -c ", "build": "npm run before-build && rollup -c ",
"dev-build": "npm run build", "dev-build": "npm run build",
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests", "test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
"pub": "npm publish" "pub": "npm publish",
"compile": "npm run build"
}, },
"author": "", "author": "",
"license": "Apache", "license": "Apache",
@@ -59,5 +60,5 @@
"fetch" "fetch"
] ]
}, },
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c" "gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
} }
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
**Note:** Version bump only for package @certd/lib-k8s
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/lib-k8s
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/lib-k8s
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/lib-k8s **Note:** Version bump only for package @certd/lib-k8s
+3 -3
View File
@@ -1,7 +1,7 @@
{ {
"name": "@certd/lib-k8s", "name": "@certd/lib-k8s",
"private": false, "private": false,
"version": "1.41.1", "version": "1.41.4",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.js", "module": "./dist/index.js",
@@ -19,7 +19,7 @@
"compile": "tsc --skipLibCheck --watch" "compile": "tsc --skipLibCheck --watch"
}, },
"dependencies": { "dependencies": {
"@certd/basic": "^1.41.1", "@certd/basic": "^1.41.4",
"@kubernetes/client-node": "0.21.0" "@kubernetes/client-node": "0.21.0"
}, },
"devDependencies": { "devDependencies": {
@@ -36,5 +36,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c" "gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
} }
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
**Note:** Version bump only for package @certd/lib-server
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/lib-server
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/lib-server
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
### Performance Improvements ### Performance Improvements
+7 -7
View File
@@ -1,6 +1,6 @@
{ {
"name": "@certd/lib-server", "name": "@certd/lib-server",
"version": "1.41.1", "version": "1.41.4",
"description": "midway with flyway, sql upgrade way ", "description": "midway with flyway, sql upgrade way ",
"private": false, "private": false,
"type": "module", "type": "module",
@@ -29,11 +29,11 @@
], ],
"license": "AGPL", "license": "AGPL",
"dependencies": { "dependencies": {
"@certd/acme-client": "^1.41.1", "@certd/acme-client": "^1.41.4",
"@certd/basic": "^1.41.1", "@certd/basic": "^1.41.4",
"@certd/pipeline": "^1.41.1", "@certd/pipeline": "^1.41.4",
"@certd/plugin-lib": "^1.41.1", "@certd/plugin-lib": "^1.41.4",
"@certd/plus-core": "^1.41.1", "@certd/plus-core": "^1.41.4",
"@midwayjs/cache": "3.14.0", "@midwayjs/cache": "3.14.0",
"@midwayjs/core": "3.20.11", "@midwayjs/core": "3.20.11",
"@midwayjs/i18n": "3.20.13", "@midwayjs/i18n": "3.20.13",
@@ -69,5 +69,5 @@
"typeorm": "^0.3.11", "typeorm": "^0.3.11",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c" "gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
} }
@@ -173,7 +173,6 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
if (privateSetting.dnsResultOrder) { if (privateSetting.dnsResultOrder) {
dns.setDefaultResultOrder(privateSetting.dnsResultOrder as any); dns.setDefaultResultOrder(privateSetting.dnsResultOrder as any);
} }
if (privateSetting.pipelineMaxRunningCount) { if (privateSetting.pipelineMaxRunningCount) {
executorQueue.setMaxRunningCount(privateSetting.pipelineMaxRunningCount); executorQueue.setMaxRunningCount(privateSetting.pipelineMaxRunningCount);
} }
@@ -1,20 +1,32 @@
import { IAccessService } from "@certd/pipeline"; import { IAccessService } from "@certd/pipeline";
export type AccessRuntimeDepsService = {
ensureRuntimeDependencies(pluginKeys: string | string[]): Promise<any>;
importRuntime(specifier: string): Promise<any>;
};
export class AccessGetter implements IAccessService { export class AccessGetter implements IAccessService {
userId: number; userId: number;
projectId?: number; projectId?: number;
getter: <T>(id: any, userId?: number, projectId?: number, ignorePermission?: boolean) => Promise<T>; runtimeDepsService?: AccessRuntimeDepsService;
constructor(userId: number, projectId: number, getter: (id: any, userId: number, projectId?: number, ignorePermission?: boolean) => Promise<any>) { getter: <T>(id: any, userId?: number, projectId?: number, ignorePermission?: boolean, runtimeDepsService?: AccessRuntimeDepsService) => Promise<T>;
constructor(
userId: number,
projectId: number,
getter: (id: any, userId: number, projectId?: number, ignorePermission?: boolean, runtimeDepsService?: AccessRuntimeDepsService) => Promise<any>,
runtimeDepsService?: AccessRuntimeDepsService
) {
this.userId = userId; this.userId = userId;
this.projectId = projectId; this.projectId = projectId;
this.getter = getter; this.getter = getter;
this.runtimeDepsService = runtimeDepsService;
} }
async getById<T = any>(id: any) { async getById<T = any>(id: any) {
return await this.getter<T>(id, this.userId, this.projectId); return await this.getter<T>(id, this.userId, this.projectId, false, this.runtimeDepsService);
} }
async getCommonById<T = any>(id: any) { async getCommonById<T = any>(id: any) {
return await this.getter<T>(id, 0, null); return await this.getter<T>(id, 0, null, false, this.runtimeDepsService);
} }
} }
@@ -2,10 +2,11 @@ import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
import { InjectEntityModel } from "@midwayjs/typeorm"; import { InjectEntityModel } from "@midwayjs/typeorm";
import { In, Repository } from "typeorm"; import { In, Repository } from "typeorm";
import { AccessGetter, BaseService, PageReq, PermissionException, ValidateException } from "../../../index.js"; import { AccessGetter, BaseService, PageReq, PermissionException, ValidateException } from "../../../index.js";
import type { AccessRuntimeDepsService } from "./access-getter.js";
import { AccessEntity } from "../entity/access.js"; import { AccessEntity } from "../entity/access.js";
import { AccessDefine, accessRegistry, newAccess } from "@certd/pipeline"; import { AccessDefine, accessRegistry, newAccess } from "@certd/pipeline";
import { EncryptService } from "./encrypt-service.js"; import { EncryptService } from "./encrypt-service.js";
import { logger, utils } from "@certd/basic"; import { http, logger, utils } from "@certd/basic";
/** /**
* *
@@ -160,7 +161,7 @@ export class AccessService extends BaseService<AccessEntity> {
}; };
} }
async getAccessById(id: any, checkUserId: boolean, userId?: number, projectId?: number): Promise<any> { async getAccessById(id: any, checkUserId: boolean, userId?: number, projectId?: number, runtimeDepsService?: AccessRuntimeDepsService): Promise<any> {
const entity = await this.info(id); const entity = await this.info(id);
if (entity == null) { if (entity == null) {
throw new Error(`该授权配置不存在,请确认是否已被删除:id=${id}`); throw new Error(`该授权配置不存在,请确认是否已被删除:id=${id}`);
@@ -183,12 +184,20 @@ export class AccessService extends BaseService<AccessEntity> {
id: entity.id, id: entity.id,
...setting, ...setting,
}; };
const accessGetter = new AccessGetter(userId, projectId, this.getById.bind(this)); const getAccessById = this.getById.bind(this);
return await newAccess(entity.type, input, accessGetter); const accessGetter = new AccessGetter(userId, projectId, getAccessById, runtimeDepsService);
const accessContext = {
logger,
http,
utils,
accessService: accessGetter,
} as any;
const access = await newAccess(entity.type, input, accessGetter, accessContext);
return access;
} }
async getById(id: any, userId: number, projectId?: number): Promise<any> { async getById(id: any, userId: number, projectId?: number, _ignorePermission?: boolean, runtimeDepsService?: AccessRuntimeDepsService): Promise<any> {
return await this.getAccessById(id, true, userId, projectId); return await this.getAccessById(id, true, userId, projectId, runtimeDepsService);
} }
decryptAccessEntity(entity: AccessEntity): any { decryptAccessEntity(entity: AccessEntity): any {
@@ -27,6 +27,8 @@ export type AddonInputDefine = FormItemProps & {
export type AddonDefine = Registrable & { export type AddonDefine = Registrable & {
addonType: string; addonType: string;
needPlus?: boolean; needPlus?: boolean;
dependPlugins?: Record<string, string>;
dependPackages?: Record<string, string>;
input?: { input?: {
[key: string]: AddonInputDefine; [key: string]: AddonInputDefine;
}; };
@@ -64,6 +66,17 @@ export abstract class BaseAddon implements IAddon {
ctx!: AddonContext; ctx!: AddonContext;
http!: HttpClient; http!: HttpClient;
logger!: ILogger; logger!: ILogger;
runtimeDepsService?: {
ensureRuntimeDependencies(pluginKeys: string | string[]): Promise<any>;
importRuntime(specifier: string): Promise<any>;
};
async importRuntime(specifier: string) {
if (!this.runtimeDepsService) {
return await import(specifier);
}
return await this.runtimeDepsService.importRuntime(specifier);
}
title!: string; title!: string;
@@ -107,10 +120,16 @@ export abstract class BaseAddon implements IAddon {
} }
setCtx(ctx: AddonContext) { async setCtx(ctx: AddonContext) {
this.ctx = ctx; this.ctx = ctx;
this.http = ctx.http; this.http = ctx.http;
this.logger = ctx.logger; this.logger = ctx.logger;
if (!this.runtimeDepsService && this.ctx.serviceGetter) {
this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService");
}
if (this.runtimeDepsService && this.define?.addonType && this.define?.name) {
await this.runtimeDepsService.ensureRuntimeDependencies(`addon:${this.define.addonType}:${this.define.name}`);
}
} }
setDefine = (define:AddonDefine) => { setDefine = (define:AddonDefine) => {
this.define = define; this.define = define;
@@ -63,9 +63,7 @@ export async function newAddon(addonType:string,type: string, input: any, ctx: A
throw new Error("ctx is required"); throw new Error("ctx is required");
} }
plugin.setDefine(register.define); plugin.setDefine(register.define);
plugin.setCtx(ctx); await plugin.setCtx(ctx);
await plugin.onInstance(); await plugin.onInstance();
return plugin; return plugin;
} }
@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
### Bug Fixes
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/midway-flyway-js
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/midway-flyway-js **Note:** Version bump only for package @certd/midway-flyway-js
+4 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@certd/midway-flyway-js", "name": "@certd/midway-flyway-js",
"version": "1.41.1", "version": "1.41.4",
"description": "midway with flyway, sql upgrade way ", "description": "midway with flyway, sql upgrade way ",
"private": false, "private": false,
"type": "module", "type": "module",
@@ -16,7 +16,8 @@
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests", "test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
"cov": "midway-bin cov --ts", "cov": "midway-bin cov --ts",
"prepublish": "npm run build", "prepublish": "npm run build",
"pub": "npm publish" "pub": "npm publish",
"compile": "npm run build"
}, },
"keywords": [], "keywords": [],
"author": "greper", "author": "greper",
@@ -49,5 +50,5 @@
"typeorm": "^0.3.11", "typeorm": "^0.3.11",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c" "gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
} }
+12
View File
@@ -3,6 +3,18 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
**Note:** Version bump only for package @certd/plugin-cert
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/plugin-cert
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
**Note:** Version bump only for package @certd/plugin-cert
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/plugin-cert **Note:** Version bump only for package @certd/plugin-cert
+6 -6
View File
@@ -1,7 +1,7 @@
{ {
"name": "@certd/plugin-cert", "name": "@certd/plugin-cert",
"private": false, "private": false,
"version": "1.41.1", "version": "1.41.4",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@@ -18,10 +18,10 @@
"compile": "tsc --skipLibCheck --watch" "compile": "tsc --skipLibCheck --watch"
}, },
"dependencies": { "dependencies": {
"@certd/acme-client": "^1.41.1", "@certd/acme-client": "^1.41.4",
"@certd/basic": "^1.41.1", "@certd/basic": "^1.41.4",
"@certd/pipeline": "^1.41.1", "@certd/pipeline": "^1.41.4",
"@certd/plugin-lib": "^1.41.1", "@certd/plugin-lib": "^1.41.4",
"psl": "^1.9.0", "psl": "^1.9.0",
"punycode.js": "^2.3.1" "punycode.js": "^2.3.1"
}, },
@@ -41,5 +41,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c" "gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
} }
+14
View File
@@ -3,6 +3,20 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
**Note:** Version bump only for package @certd/plugin-lib
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/plugin-lib
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Performance Improvements
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
**Note:** Version bump only for package @certd/plugin-lib **Note:** Version bump only for package @certd/plugin-lib
+6 -6
View File
@@ -1,7 +1,7 @@
{ {
"name": "@certd/plugin-lib", "name": "@certd/plugin-lib",
"private": false, "private": false,
"version": "1.41.1", "version": "1.41.4",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",
@@ -23,10 +23,10 @@
"@alicloud/pop-core": "^1.7.10", "@alicloud/pop-core": "^1.7.10",
"@alicloud/tea-util": "^1.4.11", "@alicloud/tea-util": "^1.4.11",
"@aws-sdk/client-s3": "^3.964.0", "@aws-sdk/client-s3": "^3.964.0",
"@certd/acme-client": "^1.41.1", "@certd/acme-client": "^1.41.4",
"@certd/basic": "^1.41.1", "@certd/basic": "^1.41.4",
"@certd/pipeline": "^1.41.1", "@certd/pipeline": "^1.41.4",
"@certd/plus-core": "^1.41.1", "@certd/plus-core": "^1.41.4",
"@kubernetes/client-node": "0.21.0", "@kubernetes/client-node": "0.21.0",
"ali-oss": "^6.22.0", "ali-oss": "^6.22.0",
"basic-ftp": "^5.0.5", "basic-ftp": "^5.0.5",
@@ -61,5 +61,5 @@
"tslib": "^2.8.1", "tslib": "^2.8.1",
"typescript": "^5.4.2" "typescript": "^5.4.2"
}, },
"gitHead": "d368f9666abf71d7f56891b6cbedeb618b82701c" "gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
} }
@@ -132,7 +132,7 @@ export class CertConverter {
if (!fs.existsSync(dir)) { if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true }); fs.mkdirSync(dir, { recursive: true });
} }
await this.exec(`keytool -importkeystore -srckeystore ${p12Path} -srcstoretype PKCS12 -srcstorepass "${jksPassword}" -destkeystore ${jksPath} -deststoretype PKCS12 -deststorepass "${jksPassword}" `); await this.exec(`keytool -importkeystore -srckeystore ${p12Path} -srcstoretype PKCS12 -srcstorepass "${jksPassword}" -destkeystore ${jksPath} -deststoretype JKS -deststorepass "${jksPassword}" `);
fs.unlinkSync(p12Path); fs.unlinkSync(p12Path);
const fileBuffer = fs.readFileSync(jksPath); const fileBuffer = fs.readFileSync(jksPath);
@@ -4,6 +4,8 @@ import { IAccess, IAccessService, IServiceGetter, PageRes, PageSearch, Registrab
export type DnsProviderDefine = Registrable & { export type DnsProviderDefine = Registrable & {
accessType: string; accessType: string;
icon?: string; icon?: string;
dependPlugins?: Record<string, string>;
dependPackages?: Record<string, string>;
}; };
export type CreateRecordOptions = { export type CreateRecordOptions = {
@@ -27,6 +29,7 @@ export type DnsProviderContext = {
domainParser: IDomainParser; domainParser: IDomainParser;
serviceGetter: IServiceGetter; serviceGetter: IServiceGetter;
accessGetter?: IAccessService; accessGetter?: IAccessService;
define?: DnsProviderDefine;
}; };
export type DomainRecord = { export type DomainRecord = {
@@ -34,6 +37,14 @@ export type DomainRecord = {
domain: string; domain: string;
}; };
export type DnsResolveRecord = {
id: string;
hostRecord: string;
fullRecord: string;
type: string;
value: string;
};
export interface IDnsProvider<T = any> { export interface IDnsProvider<T = any> {
onInstance(): Promise<void>; onInstance(): Promise<void>;
@@ -53,12 +64,14 @@ export interface IDnsProvider<T = any> {
removeRecord(options: RemoveRecordOptions<T>): Promise<void>; removeRecord(options: RemoveRecordOptions<T>): Promise<void>;
setCtx(ctx: DnsProviderContext): void; setCtx(ctx: DnsProviderContext): Promise<void>;
//中文域名是否需要punycode转码,如果返回True,则使用punycode来添加解析记录,否则使用中文域名添加解析记录 //中文域名是否需要punycode转码,如果返回True,则使用punycode来添加解析记录,否则使用中文域名添加解析记录
usePunyCode(): boolean; usePunyCode(): boolean;
getDomainListPage(pager: PageSearch): Promise<PageRes<DomainRecord>>; getDomainListPage(pager: PageSearch): Promise<PageRes<DomainRecord>>;
getRecordListPage?(domain: string, pager: PageSearch): Promise<PageRes<DnsResolveRecord>>;
} }
export interface ISubDomainsGetter { export interface ISubDomainsGetter {
@@ -1,12 +1,23 @@
import { HttpClient, ILogger } from "@certd/basic"; import { HttpClient, ILogger } from "@certd/basic";
import { IAccessService, PageRes, PageSearch } from "@certd/pipeline"; import { IAccessService, PageRes, PageSearch } from "@certd/pipeline";
import punycode from "punycode.js"; import punycode from "punycode.js";
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, DomainRecord, IDnsProvider, RemoveRecordOptions } from "./api.js"; import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, DnsResolveRecord, DomainRecord, IDnsProvider, RemoveRecordOptions } from "./api.js";
import { dnsProviderRegistry } from "./registry.js"; import { dnsProviderRegistry } from "./registry.js";
export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> { export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
ctx!: DnsProviderContext; ctx!: DnsProviderContext;
http!: HttpClient; http!: HttpClient;
logger!: ILogger; logger!: ILogger;
runtimeDepsService?: {
ensureRuntimeDependencies(pluginKeys: string | string[]): Promise<any>;
importRuntime(specifier: string): Promise<any>;
};
async importRuntime(specifier: string) {
if (!this.runtimeDepsService) {
return await import(specifier);
}
return await this.runtimeDepsService.importRuntime(specifier);
}
usePunyCode(): boolean { usePunyCode(): boolean {
//是否使用punycode来添加解析记录 //是否使用punycode来添加解析记录
@@ -30,10 +41,16 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
return punycode.toUnicode(domain); return punycode.toUnicode(domain);
} }
setCtx(ctx: DnsProviderContext) { async setCtx(ctx: DnsProviderContext) {
this.ctx = ctx; this.ctx = ctx;
this.logger = ctx.logger; this.logger = ctx.logger;
this.http = ctx.http; this.http = ctx.http;
if (!this.runtimeDepsService && this.ctx.serviceGetter) {
this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService");
}
if (this.runtimeDepsService && this.ctx.define?.name) {
await this.runtimeDepsService.ensureRuntimeDependencies(`dnsProvider:${this.ctx.define.name}`);
}
} }
async parseDomain(fullDomain: string) { async parseDomain(fullDomain: string) {
@@ -49,6 +66,10 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
async getDomainListPage(req: PageSearch): Promise<PageRes<DomainRecord>> { async getDomainListPage(req: PageSearch): Promise<PageRes<DomainRecord>> {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
async getRecordListPage(domain: string, req: PageSearch): Promise<PageRes<DnsResolveRecord>> {
throw new Error("Method not implemented.");
}
} }
export async function createDnsProvider(opts: { dnsProviderType: string; context: DnsProviderContext }): Promise<IDnsProvider> { export async function createDnsProvider(opts: { dnsProviderType: string; context: DnsProviderContext }): Promise<IDnsProvider> {
@@ -64,9 +85,10 @@ export async function createDnsProvider(opts: { dnsProviderType: string; context
const accessGetter: IAccessService = await context.serviceGetter.get("accessService"); const accessGetter: IAccessService = await context.serviceGetter.get("accessService");
context.accessGetter = accessGetter; context.accessGetter = accessGetter;
} }
context.define = dnsProviderDefine;
// @ts-ignore // @ts-ignore
const dnsProvider: IDnsProvider = new DnsProviderClass(); const dnsProvider: IDnsProvider = new DnsProviderClass();
dnsProvider.setCtx(context); await dnsProvider.setCtx(context);
await dnsProvider.onInstance(); await dnsProvider.onInstance();
return dnsProvider; return dnsProvider;
} }
+37 -30
View File
@@ -1,12 +1,9 @@
FROM node:22-alpine3.21 AS builder # 根据目标平台选择基础镜像:amd64/arm64 用 trixie-slimarm/v7 没有 trixie-slim 发布,回退到 alpine
FROM --platform=linux/amd64 node:22-trixie-slim AS base-amd64
# RUN apk add build-base FROM --platform=linux/arm64 node:22-trixie-slim AS base-arm64
# RUN wget -O - https://github.com/jemalloc/jemalloc/releases/download/5.3.0/jemalloc-5.3.0.tar.bz2 | tar -xj && \ FROM --platform=linux/arm/v7 node:22-alpine AS base-arm-v7
# cd jemalloc-5.3.0 && \
# ./configure && \
# make && \
# make install
FROM base-${TARGETARCH}${TARGETVARIANT:+-}${TARGETVARIANT} AS builder
WORKDIR /workspace/ WORKDIR /workspace/
COPY . /workspace/ COPY . /workspace/
@@ -14,34 +11,44 @@ COPY . /workspace/
# https://pnpm.io/zh/migration # https://pnpm.io/zh/migration
RUN npm install -g pnpm@10.33.4 RUN npm install -g pnpm@10.33.4
#RUN cd /workspace/certd-client && pnpm install && npm run build
RUN cp /workspace/certd-client/dist/* /workspace/certd-server/public/ -rf RUN cp /workspace/certd-client/dist/* /workspace/certd-server/public/ -rf
RUN cd /workspace/certd-server && pnpm install && npm run build-on-docker RUN cd /workspace/certd-server && pnpm install && npm run build-on-docker
# RUN cd /workspace/certd-server && \
# pnpm install --ignore-scripts && \
# yes | pnpm approve-builds && \
# pnpm rebuild && \
# npm run build-on-docker
FROM base-${TARGETARCH}${TARGETVARIANT:+-}${TARGETVARIANT}
FROM node:22-alpine3.21
EXPOSE 7001 EXPOSE 7001
EXPOSE 7002 EXPOSE 7002
# 安装jemalloc内存分配器,优化内存占用 -- 基本没用,反而更高了 # 根据基础镜像发行版选择包管理器
# COPY --from=builder /usr/local/lib/libjemalloc.so.2 /usr/local/lib/ # trixie-slim -> apt-get, alpine -> apk
# ENV LD_PRELOAD=/usr/local/lib/libjemalloc.so.2 RUN if [ -f /etc/debian_version ]; then \
apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
gnupg \
wget \
openssl \
netcat-openbsd \
iputils-ping \
dnsutils \
iproute2 \
&& wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | tee /usr/share/keyrings/adoptium.gpg > /dev/null \
&& echo "deb [signed-by=/usr/share/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb bookworm main" | tee /etc/apt/sources.list.d/adoptium.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends temurin-8-jre \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*; \
elif [ -f /etc/alpine-release ]; then \
apk add --no-cache \
openssl \
openjdk8-jre; \
else \
echo "Unsupported base image"; exit 1; \
fi
RUN apk add --no-cache openssl
RUN apk add --no-cache openjdk8
RUN apk add --no-cache gcompat
WORKDIR /app/ WORKDIR /app/
COPY --from=builder /workspace/certd-server/ /app/
COPY ./patch/ssh2/*.js /app/node_modules/.pnpm/node_modules/ssh2/lib/protocol/
ENV TERM=xterm
ENV LEGO_VERSION=4.30.1 ENV LEGO_VERSION=4.30.1
ENV LEGO_DOWNLOAD_DIR=/app/tools/lego ENV LEGO_DOWNLOAD_DIR=/app/tools/lego
@@ -57,14 +64,14 @@ RUN ARCH=$(uname -m) && \
elif [ "$ARCH" = "aarch64" ]; then \ elif [ "$ARCH" = "aarch64" ]; then \
wget -O $LEGO_DOWNLOAD_DIR/lego_v${LEGO_VERSION}_linux_arm64.tar.gz https://github.com/go-acme/lego/releases/download/v${LEGO_VERSION}/lego_v${LEGO_VERSION}_linux_arm64.tar.gz; \ wget -O $LEGO_DOWNLOAD_DIR/lego_v${LEGO_VERSION}_linux_arm64.tar.gz https://github.com/go-acme/lego/releases/download/v${LEGO_VERSION}/lego_v${LEGO_VERSION}_linux_arm64.tar.gz; \
else \ else \
# armv7 不支持lego 不要再尝试了
echo "Unsupported architecture: $ARCH"; \ echo "Unsupported architecture: $ARCH"; \
fi fi
ENV TZ=Asia/Shanghai ENV TZ=Asia/Shanghai
ENV NODE_ENV=production ENV NODE_ENV=production
ENV MIDWAY_SERVER_ENV=production ENV MIDWAY_SERVER_ENV=production
COPY --from=builder /workspace/certd-server/ /app/
COPY ./patch/ssh2/*.js /app/node_modules/.pnpm/node_modules/ssh2/lib/protocol/
CMD ["node", "--optimize-for-size", "./bootstrap.js"] CMD ["node", "--optimize-for-size", "./bootstrap.js"]
+2 -2
View File
@@ -4,8 +4,8 @@ VITE_APP_PM_ENABLED=true
VITE_APP_TITLE=Certd VITE_APP_TITLE=Certd
VITE_APP_SLOGAN=让你的证书永不过期 VITE_APP_SLOGAN=让你的证书永不过期
VITE_APP_COPYRIGHT_YEAR=2021-2026 VITE_APP_COPYRIGHT_YEAR=2021-2026
VITE_APP_COPYRIGHT_NAME=handsfree.work VITE_APP_COPYRIGHT_NAME=handfree.work
VITE_APP_COPYRIGHT_URL=https://certd.handsfree.work VITE_APP_COPYRIGHT_URL=https://certd.handfree.work
VITE_APP_LOGO=static/images/logo/logo.svg VITE_APP_LOGO=static/images/logo/logo.svg
VITE_APP_LOGIN_LOGO=static/images/logo/rect-black.svg VITE_APP_LOGIN_LOGO=static/images/logo/rect-black.svg
VITE_APP_PROJECT_PATH=https://github.com/certd/certd VITE_APP_PROJECT_PATH=https://github.com/certd/certd
+22
View File
@@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
### Bug Fixes
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
### Performance Improvements
* 首页夜间模式主图切换为黑色背景 ([15484bc](https://github.com/certd/certd/commit/15484bc119fef7a0ca7f3fdab01d665fde47e688))
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Bug Fixes
* **cert-plugin:** 修复DNS提供商授权无法回显的bug ([016ae86](https://github.com/certd/certd/commit/016ae865b1d914fe5792e77a08e3ab5358df5f89))
### Performance Improvements
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
### Performance Improvements ### Performance Improvements
+3 -3
View File
@@ -1,6 +1,6 @@
{ {
"name": "@certd/ui-client", "name": "@certd/ui-client",
"version": "1.41.1", "version": "1.41.4",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "vite --open", "dev": "vite --open",
@@ -106,8 +106,8 @@
"zod-defaults": "^0.1.3" "zod-defaults": "^0.1.3"
}, },
"devDependencies": { "devDependencies": {
"@certd/lib-iframe": "^1.41.1", "@certd/lib-iframe": "^1.41.4",
"@certd/pipeline": "^1.41.1", "@certd/pipeline": "^1.41.4",
"@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3", "@rollup/plugin-node-resolve": "^15.2.3",
"@types/chai": "^4.3.12", "@types/chai": "^4.3.12",
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

@@ -14,7 +14,7 @@ export default {
default: undefined, default: undefined,
}, },
}, },
emits: ["update:modelValue", "selected-change"], emits: ["update:modelValue", "selected-change", "change"],
setup(props: any, ctx: any) { setup(props: any, ctx: any) {
const options = ref<any[]>([]); const options = ref<any[]>([]);
@@ -33,7 +33,8 @@ export default {
// if (props.modelValue == null && options.value.length > 0) { // if (props.modelValue == null && options.value.length > 0) {
// ctx.emit("update:modelValue", options.value[0].value); // ctx.emit("update:modelValue", options.value[0].value);
// } // }
onSelectedChange(props.modelValue); //selected-changeoption
onSelectedChange(props.modelValue, true);
} }
onCreate(); onCreate();
@@ -41,9 +42,12 @@ export default {
ctx.emit("update:modelValue", value); ctx.emit("update:modelValue", value);
onSelectedChange(value); onSelectedChange(value);
} }
function onSelectedChange(value: any) { function onSelectedChange(value: any, isFirst: boolean = false) {
if (value) { if (value) {
const option = options.value.find(item => item.value == value); const option = options.value.find(item => item.value == value);
if (!isFirst) {
ctx.emit("change", value);
}
if (option) { if (option) {
ctx.emit("selected-change", option); ctx.emit("selected-change", option);
return; return;
@@ -18,6 +18,7 @@ export default {
subdomainConfirmTitle: "Subdomain Confirmation", subdomainConfirmTitle: "Subdomain Confirmation",
subdomainConfirmContent: "{domain} appears to be a subdomain. Only delegated subdomains and free second-level subdomains need to be maintained here. Otherwise certificate application may fail. Continue?", subdomainConfirmContent: "{domain} appears to be a subdomain. Only delegated subdomains and free second-level subdomains need to be maintained here. Otherwise certificate application may fail. Continue?",
importFromProvider: "Import from Domain Provider", importFromProvider: "Import from Domain Provider",
importFromResolveRecords: "Import from DNS Records",
syncExpirationDate: "Sync Domain Expiration Time", syncExpirationDate: "Sync Domain Expiration Time",
syncTaskSubmitted: "Sync task submitted", syncTaskSubmitted: "Sync task submitted",
syncExpirationProgress: "Sync Domain Expiration Progress", syncExpirationProgress: "Sync Domain Expiration Progress",
@@ -4,7 +4,7 @@ export default {
"The domain name configured here serves as a proxy for verifying other domains. When other domains apply for certificates, they map to this domain via CNAME for ownership verification. The advantage is that any domain can apply for a certificate this way without providing an AccessSecret.", "The domain name configured here serves as a proxy for verifying other domains. When other domains apply for certificates, they map to this domain via CNAME for ownership verification. The advantage is that any domain can apply for a certificate this way without providing an AccessSecret.",
cnameLinkText: "CNAME principle and usage instructions", cnameLinkText: "CNAME principle and usage instructions",
cnameDomain: "CNAME Domain", cnameDomain: "CNAME Domain",
cnameDomainPlaceholder: "cname.handsfree.work", cnameDomainPlaceholder: "cname.handfree.work",
cnameDomainHelper: cnameDomainHelper:
"Requires a domain registered with a DNS provider on the right (or you can transfer other domain DNS servers here).\nOnce the CNAME domain is set, it cannot be changed. It is recommended to use a first-level subdomain.", "Requires a domain registered with a DNS provider on the right (or you can transfer other domain DNS servers here).\nOnce the CNAME domain is set, it cannot be changed. It is recommended to use a first-level subdomain.",
cnameDomainPattern: "Domain name cannot contain *", cnameDomainPattern: "Domain name cannot contain *",
@@ -18,6 +18,7 @@ export default {
subdomainConfirmTitle: "子域名确认", subdomainConfirmTitle: "子域名确认",
subdomainConfirmContent: "检测到{domain}为子域名,只有托管子域名和免费二级子域名才需要在此处维护,否则会导致申请证书失败,请确认是否继续?", subdomainConfirmContent: "检测到{domain}为子域名,只有托管子域名和免费二级子域名才需要在此处维护,否则会导致申请证书失败,请确认是否继续?",
importFromProvider: "从域名提供商导入", importFromProvider: "从域名提供商导入",
importFromResolveRecords: "从解析记录导入",
syncExpirationDate: "同步域名过期时间", syncExpirationDate: "同步域名过期时间",
syncTaskSubmitted: "同步任务已提交", syncTaskSubmitted: "同步任务已提交",
syncExpirationProgress: "同步域名过期时间进度", syncExpirationProgress: "同步域名过期时间进度",
@@ -3,7 +3,7 @@ export default {
cnameDescription: "此处配置的域名作为其他域名校验的代理,当别的域名需要申请证书时,通过CNAME映射到此域名上来验证所有权。好处是任何域名都可以通过此方式申请证书,也无需填写AccessSecret。", cnameDescription: "此处配置的域名作为其他域名校验的代理,当别的域名需要申请证书时,通过CNAME映射到此域名上来验证所有权。好处是任何域名都可以通过此方式申请证书,也无需填写AccessSecret。",
cnameLinkText: "CNAME功能原理及使用说明", cnameLinkText: "CNAME功能原理及使用说明",
cnameDomain: "CNAME域名", cnameDomain: "CNAME域名",
cnameDomainPlaceholder: "cname.handsfree.work", cnameDomainPlaceholder: "cname.handfree.work",
cnameDomainHelper: "需要一个右边DNS提供商注册的域名(也可以将其他域名的dns服务器转移到这几家来)。\nCNAME域名一旦确定不可修改,建议使用一级子域名", cnameDomainHelper: "需要一个右边DNS提供商注册的域名(也可以将其他域名的dns服务器转移到这几家来)。\nCNAME域名一旦确定不可修改,建议使用一级子域名",
cnameDomainPattern: "域名不能使用星号", cnameDomainPattern: "域名不能使用星号",
cnameProviderSubdomain: "托管子域名", cnameProviderSubdomain: "托管子域名",
@@ -8,4 +8,45 @@
.vben-normal-menu__item.is-active { .vben-normal-menu__item.is-active {
background-color: #3b3b3b !important; background-color: #3b3b3b !important;
} }
.cd-table {
th,
td {
border-bottom: 1px solid #303030;
border-left: 1px solid #303030;
}
th {
background: #1f1f1f;
color: rgba(255, 255, 255, 0.85);
border-top: 1px solid #303030;
&:last-child {
border-right: 1px solid #303030;
}
}
td {
&:last-child {
border-right: 1px solid #303030;
}
}
td.position-sticky-right {
background-color: #141414;
}
.position-sticky-right::before {
background: #303030;
}
tr.hover-color:hover td {
background: #262626;
}
.status-active {
background: #1f3a23;
color: #81c784;
}
}
} }
@@ -109,7 +109,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
}, },
rowHandle: { rowHandle: {
fixed: "right", fixed: "right",
width: 120, width: 200,
buttons: { buttons: {
edit: { edit: {
click: ({ row }) => openForm(row), click: ({ row }) => openForm(row),
@@ -11,9 +11,9 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
defineOptions({ defineOptions({
name: "DnsPersistRecord", name: "DnsPersistRecord",
@@ -24,10 +24,8 @@ const context: any = {
}; };
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context }); const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
onMounted(() => { //
// crudExpose.doRefresh(); useMounted(async () => {
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
@@ -19,13 +19,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api"; import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales"; import { useI18n } from "/src/locales";
import { useCrudPermission } from "/@/plugin/permission"; import { useCrudPermission } from "/@/plugin/permission";
import { useMounted } from "/@/use/use-mounted";
const { t } = useI18n(); const { t } = useI18n();
@@ -61,10 +62,7 @@ const handleBatchDelete = () => {
}; };
// //
onMounted(() => { useMounted(async () => {
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
@@ -21,13 +21,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api"; import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales"; import { useI18n } from "/src/locales";
import { useCrudPermission } from "/@/plugin/permission"; import { useCrudPermission } from "/@/plugin/permission";
import { useMounted } from "/@/use/use-mounted";
const { t } = useI18n(); const { t } = useI18n();
@@ -61,10 +62,7 @@ const handleBatchDelete = () => {
}; };
// //
onMounted(() => { useMounted(async () => {
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
@@ -149,7 +149,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, nextTick, onActivated, onMounted, reactive, ref } from "vue"; import { computed, nextTick, reactive, ref } from "vue";
import { FsIcon, useFs } from "@fast-crud/fast-crud"; import { FsIcon, useFs } from "@fast-crud/fast-crud";
import { notification } from "ant-design-vue"; import { notification } from "ant-design-vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
@@ -158,6 +158,7 @@ import createInviteesCrudOptions from "./crud-invitees";
import createLogsCrudOptions from "./crud-logs"; import createLogsCrudOptions from "./crud-logs";
import { useSettingStore } from "/@/store/settings"; import { useSettingStore } from "/@/store/settings";
import { util } from "/@/utils"; import { util } from "/@/utils";
import { useMounted } from "/@/use/use-mounted";
defineOptions({ name: "InviteCommission" }); defineOptions({ name: "InviteCommission" });
@@ -314,16 +315,10 @@ async function refreshInvitePage(autoOpenAgreement = true) {
await refreshActiveList(); await refreshActiveList();
} }
onMounted(async () => { //
useMounted(async () => {
await refreshInvitePage(true); await refreshInvitePage(true);
}); });
onActivated(async () => {
if (!loaded.value) {
return;
}
await refreshInvitePage();
});
</script> </script>
<style lang="less"> <style lang="less">
@@ -72,6 +72,36 @@ export const siteInfoApi = {
}); });
}, },
async ImportTaskSave(body: any) {
return await request({
url: apiPrefix + "/import/save",
method: "post",
data: body,
});
},
async ImportTaskStatus() {
return await request({
url: apiPrefix + "/import/status",
method: "post",
});
},
async ImportTaskDelete(key: string) {
return await request({
url: apiPrefix + "/import/delete",
method: "post",
data: { key },
});
},
async ImportTaskStart(key: string) {
return await request({
url: apiPrefix + "/import/start",
method: "post",
data: { key },
});
},
async DisabledChange(id: number, disabled: boolean) { async DisabledChange(id: number, disabled: boolean) {
return await request({ return await request({
url: apiPrefix + "/disabledChange", url: apiPrefix + "/disabledChange",
@@ -9,7 +9,7 @@ import { useSettingStore } from "/@/store/settings";
import { mySuiteApi } from "/@/views/certd/suite/mine/api"; import { mySuiteApi } from "/@/views/certd/suite/mine/api";
import { mitter } from "/@/utils/util.mitt"; import { mitter } from "/@/utils/util.mitt";
import { useSiteIpMonitor } from "./ip/use"; import { useSiteIpMonitor } from "./ip/use";
import { useSiteImport } from "/@/views/certd/monitor/site/use"; import { useSiteImport, useSiteImportTaskManage } from "/@/views/certd/monitor/site/use";
import { ref } from "vue"; import { ref } from "vue";
import GroupSelector from "../../basic/group/group-selector.vue"; import GroupSelector from "../../basic/group/group-selector.vue";
import { createGroupDictRef } from "../../basic/group/api"; import { createGroupDictRef } from "../../basic/group/api";
@@ -53,6 +53,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
const { openSiteIpMonitorDialog } = useSiteIpMonitor(); const { openSiteIpMonitorDialog } = useSiteIpMonitor();
const { openSiteImportDialog } = useSiteImport(); const { openSiteImportDialog } = useSiteImport();
const openSiteImportTaskManageDialog = useSiteImportTaskManage();
const certValidDaysRef = ref(10); const certValidDaysRef = ref(10);
@@ -200,6 +201,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
actionbar: { actionbar: {
buttons: { buttons: {
add: { add: {
icon: "ion:add-circle-outline",
async click() { async click() {
if (!settingsStore.isPlus) { if (!settingsStore.isPlus) {
// 非plus // 非plus
@@ -236,6 +238,7 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
show: hasActionPermission("write"), show: hasActionPermission("write"),
text: t("monitor.bulkImport"), text: t("monitor.bulkImport"),
type: "primary", type: "primary",
icon: "ion:cloud-upload-outline",
async click() { async click() {
const defaultGroupId = getDefaultGroupId(); const defaultGroupId = getDefaultGroupId();
openSiteImportDialog({ openSiteImportDialog({
@@ -246,10 +249,27 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat
}); });
}, },
}, },
importFromProvider: {
show: hasActionPermission("write"),
title: t("certd.domain.importFromResolveRecords"),
text: t("certd.domain.importFromResolveRecords"),
type: "primary",
needPlus: true,
color: "gold",
icon: "mingcute:vip-1-line",
click: async () => {
await openSiteImportTaskManageDialog({
afterSubmit: () => {
crudExpose.doRefresh();
},
});
},
},
checkAll: { checkAll: {
show: true, show: true,
text: t("monitor.checkAll"), text: t("monitor.checkAll"),
type: "primary", type: "primary",
icon: "ion:play-circle-outline",
click() { click() {
checkAll(); checkAll();
}, },
@@ -0,0 +1,139 @@
<template>
<div class="site-info-import-task-status min-h-[300px]">
<div class="action mb-5">
<fs-button type="primary" icon="mingcute:vip-1-line" @click="addTask">{{ t("certd.domain.addImportTask") }}</fs-button>
<fs-button type="primary" icon="ion:refresh-outline" class="ml-2" @click="loadImportTaskStatus">{{ t("certd.domain.refresh") }}</fs-button>
</div>
<div class="table-container overflow-auto mb-10">
<table class="cd-table border-gray-300 w-full">
<thead>
<tr>
<th class="w-[220px]">{{ t("certd.sourcee") }}</th>
<th class="">{{ t("certd.domain.progress") }}</th>
<th class="w-[220px]">{{ t("certd.domain.operation") }}</th>
</tr>
</thead>
<tbody>
<tr v-for="item in list" :key="item.key">
<td class="ellipsis">
<span class="flex items-center pointer" @click="editTask(item)">
<span class="flex-1 ellipsis flex items-center">
<fs-icon :icon="item.icon" class="mr-2"></fs-icon>
{{ item.title }}
</span>
<fs-icon icon="ant-design:edit-outlined" class="ml-2" />
</span>
</td>
<td>
<div v-if="item.task">
<div>
<a-tag color="blue">{{ t("certd.domain.total") }}{{ item.task?.total }}</a-tag>
<a-tag color="success" class="ml-2">{{ t("certd.success") }}{{ item.task?.successCount }}</a-tag>
<a-tag type="info" class="ml-2">{{ t("certd.domain.skipped") }}{{ item.task?.skipCount }}</a-tag>
<a-tooltip v-if="item.task?.errors.length > 0">
<template #title>
<div v-for="error in item.task?.errors" :key="error">{{ error }}</div>
</template>
<a-tag color="red" class="ml-2">{{ t("certd.domain.failed") }}{{ item.task?.errors.length }}</a-tag>
</a-tooltip>
</div>
<a-progress :percent="item.task?.progress" size="small" status="active" />
</div>
<div v-else>{{ t("certd.domain.notExecuted") }}</div>
</td>
<td>
<fs-button type="primary" icon="ion:play-outline" :disabled="item.task?.status === 'running'" @click="startTask(item)">{{ t("certd.domain.execute") }}</fs-button>
<fs-button type="primary" class="ml-2" danger icon="ion:trash-outline" @click="deleteTask(item)">{{ t("certd.domain.delete") }}</fs-button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script setup lang="ts">
import { Modal } from "ant-design-vue";
import { onMounted, onUnmounted, ref } from "vue";
import * as api from "./api";
import { useSiteImportTask } from "./use";
import { useSettingStore } from "/@/store/settings";
import { useI18n } from "/@/locales";
defineOptions({
name: "SiteInfoImportTaskStatus",
});
const list = ref([]);
const { t } = useI18n();
async function loadImportTaskStatus() {
const res = await api.siteInfoApi.ImportTaskStatus();
list.value = res || [];
}
async function startTask(item: any) {
settingStore.checkPlus();
await api.siteInfoApi.ImportTaskStart(item.key);
await loadImportTaskStatus();
}
async function deleteTask(item: any) {
Modal.confirm({
title: t("certd.domain.confirmDelete"),
okText: t("common.confirm"),
okType: "danger",
onOk: async () => {
await api.siteInfoApi.ImportTaskDelete(item.key);
await loadImportTaskStatus();
},
});
}
const openSiteImportTaskDialog = useSiteImportTask();
const settingStore = useSettingStore();
async function addTask() {
settingStore.checkPlus();
await openSiteImportTaskDialog({
afterSubmit: async (res?: any) => {
if (res) {
await api.siteInfoApi.ImportTaskStart(res.key);
}
await loadImportTaskStatus();
},
});
}
async function editTask(item: any) {
settingStore.checkPlus();
await openSiteImportTaskDialog({
afterSubmit: async () => {
await loadImportTaskStatus();
},
form: item,
});
}
const checkIntervalRef = ref();
onMounted(async () => {
await loadImportTaskStatus();
checkIntervalRef.value = setInterval(async () => {
await loadImportTaskStatus();
}, 3000);
});
onUnmounted(() => {
clearInterval(checkIntervalRef.value);
});
</script>
<style lang="less">
.site-info-import-task-status {
.table-container {
height: 50vh;
}
.ant-progress {
margin-bottom: 0px;
}
}
</style>
@@ -1,7 +1,11 @@
import { useFormWrapper } from "@fast-crud/fast-crud"; import { useFormWrapper, compute } from "@fast-crud/fast-crud";
import { siteInfoApi } from "./api"; import { siteInfoApi } from "./api";
import { useI18n } from "/src/locales"; import { useI18n } from "/@/locales";
import { useSettingStore } from "/@/store/settings";
import { useFormDialog } from "/@/use/use-dialog";
import GroupSelector from "../../basic/group/group-selector.vue"; import GroupSelector from "../../basic/group/group-selector.vue";
import SiteInfoImportTaskStatus from "./import.vue";
export function useSiteImport() { export function useSiteImport() {
const { t } = useI18n(); const { t } = useI18n();
const { openCrudFormDialog } = useFormWrapper(); const { openCrudFormDialog } = useFormWrapper();
@@ -13,7 +17,7 @@ export function useSiteImport() {
columns: { columns: {
text: { text: {
type: "textarea", type: "textarea",
title: t("certd.domainList.title"), // 域名列表 title: t("certd.domainList.title"),
form: { form: {
helper: t("certd.domainList.helper"), helper: t("certd.domainList.helper"),
rules: [{ required: true, message: t("certd.domainList.required") }], rules: [{ required: true, message: t("certd.domainList.required") }],
@@ -21,9 +25,7 @@ export function useSiteImport() {
placeholder: t("certd.domainList.placeholder"), placeholder: t("certd.domainList.placeholder"),
rows: 8, rows: 8,
}, },
col: { col: { span: 24 },
span: 24,
},
}, },
}, },
groupId: { groupId: {
@@ -36,13 +38,10 @@ export function useSiteImport() {
vModel: "modelValue", vModel: "modelValue",
type: "site", type: "site",
}, },
col: { col: { span: 24 },
span: 24,
}, },
}, },
}, },
},
form: { form: {
async doSubmit({ form }) { async doSubmit({ form }) {
return siteInfoApi.Import(form); return siteInfoApi.Import(form);
@@ -53,7 +52,99 @@ export function useSiteImport() {
}); });
} }
return { return { openSiteImportDialog };
openSiteImportDialog, }
export function useSiteImportTask() {
const { openFormDialog } = useFormDialog();
const { t } = useI18n();
const columns = {
dnsProviderType: {
title: t("certd.domain.domainProvider"),
type: "text",
form: {
component: {
name: "dns-provider-selector",
on: {
selectedChange: ({ form, $event }: any) => {
form.dnsProviderAccessType = $event.accessType;
},
},
},
valueChange({ form }: any) {
form.dnsProviderAccessId = null;
},
},
},
dnsProviderAccessType: {
title: t("certd.domain.domainProviderAccessType"),
type: "text",
form: { show: false },
},
dnsProviderAccessId: {
title: t("certd.domain.domainProviderAccess"),
type: "text",
form: {
component: {
name: "access-selector",
vModel: "modelValue",
type: compute(({ form }: any) => form.dnsProviderAccessType || form.dnsProviderType),
},
},
},
groupId: {
title: t("certd.fields.group"),
type: "text",
form: {
component: {
name: GroupSelector,
vModel: "modelValue",
type: "site",
},
},
},
};
return function openSiteImportTaskDialog(req: { afterSubmit?: (res?: any) => void; form?: any }) {
openFormDialog({
title: t("certd.domain.importFromProvider"),
columns,
initialForm: { ...req.form },
onSubmit: async (form: any) => {
const res = await siteInfoApi.ImportTaskSave({
key: form.key,
dnsProviderType: form.dnsProviderType,
dnsProviderAccessId: form.dnsProviderAccessId,
groupId: form.groupId,
});
if (req.afterSubmit) {
req.afterSubmit(res);
}
},
});
};
}
export function useSiteImportTaskManage() {
const { openFormDialog } = useFormDialog();
const { t } = useI18n();
const settingStore = useSettingStore();
return async function openSiteImportTaskManageDialog(req: {
afterSubmit?: (res?: any) => void;
form?: any;
zIndex?: number;
}) {
settingStore.checkPlus();
await openFormDialog({
title: t("certd.domain.importFromProvider"),
body: () => <SiteInfoImportTaskStatus />,
zIndex: req.zIndex,
onSubmit: async (form: any) => {
if (req.afterSubmit) {
req.afterSubmit(form);
}
},
});
}; };
} }
@@ -55,7 +55,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onActivated, onMounted, provide, ref } from "vue"; import { computed, provide, ref } from "vue";
import { dict, useFs } from "@fast-crud/fast-crud"; import { dict, useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import ChangeGroup from "./components/change-group.vue"; import ChangeGroup from "./components/change-group.vue";
@@ -67,7 +67,7 @@ import BatchRerun from "./components/batch-rerun.vue";
import { Modal, notification } from "ant-design-vue"; import { Modal, notification } from "ant-design-vue";
import * as api from "./api"; import * as api from "./api";
import { useI18n } from "/src/locales"; import { useI18n } from "/src/locales";
import { useMounted } from "/@/use/use-mounted";
const { t } = useI18n(); const { t } = useI18n();
import ChangeNotification from "/@/views/certd/pipeline/components/change-notification.vue"; import ChangeNotification from "/@/views/certd/pipeline/components/change-notification.vue";
import { useSettingStore } from "/@/store/settings"; import { useSettingStore } from "/@/store/settings";
@@ -128,11 +128,7 @@ context.hasActionPermission = hasActionPermission;
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context }); const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
// //
onMounted(() => { useMounted(async () => {
// crudExpose.doRefresh();
});
onActivated(async () => {
await groupDictRef.reloadDict(); await groupDictRef.reloadDict();
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
@@ -172,9 +172,9 @@ function useStepForm() {
const stepTypeSelected = (item: any) => { const stepTypeSelected = (item: any) => {
if (item.needPlus && !settingStore.isPlus) { if (item.needPlus && !settingStore.isPlus) {
message.warn("此插件需要开通专业版才能使用"); message.warn("此插件需要开通Certd专业版才能使用");
mitter.emit("openVipModal"); mitter.emit("openVipModal");
throw new Error("此插件需要开通专业版才能使用"); throw new Error("此插件需要开通Certd专业版才能使用");
} }
currentStep.value.type = item.name; currentStep.value.type = item.name;
currentStep.value.title = item.title; currentStep.value.title = item.title;
@@ -26,13 +26,13 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api"; import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales"; import { useI18n } from "/src/locales";
import { useCrudPermission } from "/@/plugin/permission"; import { useCrudPermission } from "/@/plugin/permission";
import { useMounted } from "/@/use/use-mounted";
const { t } = useI18n(); const { t } = useI18n();
@@ -67,10 +67,7 @@ const handleBatchDelete = () => {
}; };
// //
onMounted(() => { useMounted(async () => {
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
@@ -27,7 +27,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted, Ref, ref } from "vue"; import { onMounted, ref } from "vue";
import { useMounted } from "/@/use/use-mounted"; import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
@@ -18,12 +18,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api"; import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales"; import { useI18n } from "/src/locales";
import { useMounted } from "/@/use/use-mounted";
const { t } = useI18n(); const { t } = useI18n();
@@ -52,10 +52,7 @@ const handleBatchDelete = () => {
}; };
// //
onMounted(() => { useMounted(async () => {
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
@@ -8,9 +8,9 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
defineOptions({ defineOptions({
name: "MyTrade", name: "MyTrade",
@@ -18,10 +18,7 @@ defineOptions({
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions }); const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
// //
onMounted(() => { useMounted(async () => {
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
@@ -27,7 +27,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, h, onActivated, onMounted, reactive, ref } from "vue"; import { computed, h, reactive, ref } from "vue";
import { compute, dict, useFs } from "@fast-crud/fast-crud"; import { compute, dict, useFs } from "@fast-crud/fast-crud";
import { Button, notification } from "ant-design-vue"; import { Button, notification } from "ant-design-vue";
import * as api from "./api"; import * as api from "./api";
@@ -36,6 +36,7 @@ import createWithdrawCrudOptions from "./crud-withdraw";
import { util } from "/@/utils"; import { util } from "/@/utils";
import { useFormDialog } from "/@/use/use-dialog"; import { useFormDialog } from "/@/use/use-dialog";
import { useUserStore } from "/@/store/user"; import { useUserStore } from "/@/store/user";
import { useMounted } from "/@/use/use-mounted";
defineOptions({ name: "MyWallet" }); defineOptions({ name: "MyWallet" });
@@ -257,14 +258,8 @@ async function refreshWalletPage() {
loaded.value = true; loaded.value = true;
} }
onMounted(refreshWalletPage); //
useMounted(refreshWalletPage);
onActivated(async () => {
if (!loaded.value) {
return;
}
await refreshWalletPage();
});
</script> </script>
<style lang="less"> <style lang="less">
@@ -41,7 +41,7 @@
</div> </div>
</div> </div>
<div class="hero-image-wrapper"> <div class="hero-image-wrapper">
<img src="/static/images/certd-intro.png" alt="Certd Intro" class="hero-image" /> <img :src="isDark ? '/static/images/certd-intro-dark.png' : '/static/images/certd-intro.png'" alt="Certd Intro" class="hero-image" />
</div> </div>
</div> </div>
</section> </section>
@@ -121,7 +121,8 @@ import { useAccessStore } from "/@/vben/stores";
import { SiteInfo, SysPublicSetting } from "/@/store/settings/api.basic"; import { SiteInfo, SysPublicSetting } from "/@/store/settings/api.basic";
import ThemeToggle from "/@/vben/layouts/widgets/theme-toggle/theme-toggle.vue"; import ThemeToggle from "/@/vben/layouts/widgets/theme-toggle/theme-toggle.vue";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { usePreferences } from "/@/vben/preferences";
const { isDark } = usePreferences();
const envRef = ref(env); const envRef = ref(env);
const settingStore = useSettingStore(); const settingStore = useSettingStore();
const userStore = useUserStore(); const userStore = useUserStore();
@@ -381,7 +382,7 @@ onMounted(() => {
padding: 0 24px; padding: 0 24px;
display: grid; display: grid;
grid-template-columns: 1.1fr 0.9fr; grid-template-columns: 1.1fr 0.9fr;
gap: 60px; gap: 10px;
align-items: center; align-items: center;
} }
@@ -440,7 +441,7 @@ onMounted(() => {
.hero-image { .hero-image {
width: 100%; width: 100%;
height: auto; height: auto;
max-width: 550px; max-width: 600px;
} }
.section-header { .section-header {
@@ -14,27 +14,25 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onActivated, onMounted, ref } from "vue"; import { defineComponent, ref} from "vue";
import createCrudOptions from "./crud.js"; import createCrudOptions from "./crud.js";
import FsPermissionTree from "./fs-permission-tree.vue"; import FsPermissionTree from "./fs-permission-tree.vue";
import { usePermission } from "/src/plugin/permission"; import { usePermission } from "/src/plugin/permission";
import { useFs, useUi } from "@fast-crud/fast-crud"; import { useFs, useUi } from "@fast-crud/fast-crud";
import { useI18n } from "/src/locales"; import { useI18n } from "/src/locales";
import { useMounted } from "/@/use/use-mounted";
export default defineComponent({ export default defineComponent({
name: "PermissionManager", name: "PermissionManager",
components: { FsPermissionTree }, components: { FsPermissionTree },
setup() { setup() {
// permissioncommonOptionsactionbarrowHandleshow // permissioncommonOptionsactionbarrowHandleshow
// ./src/plugin/fast-crud/index.js 75-77 // ./src/plugin/fast-crud/index.js 75-77
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { permission: "sys:auth:per" } }); const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { permission: "sys:auth:per" } });
const { t } = useI18n(); const { t } = useI18n();
// //
onMounted(async () => { useMounted(async () => {
// await crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
@@ -11,14 +11,15 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, onActivated, onMounted, ref } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud";
import * as permissionApi from "../permission/api";
import * as api from "./api";
import { message } from "ant-design-vue"; import { message } from "ant-design-vue";
import { defineComponent, ref } from "vue";
import * as permissionApi from "../permission/api";
import FsPermissionTree from "../permission/fs-permission-tree.vue"; import FsPermissionTree from "../permission/fs-permission-tree.vue";
import * as api from "./api";
import createCrudOptions from "./crud";
import { UseCrudPermissionCompProps, UseCrudPermissionExtraProps } from "/@/plugin/permission"; import { UseCrudPermissionCompProps, UseCrudPermissionExtraProps } from "/@/plugin/permission";
import { useMounted } from "/@/use/use-mounted";
import { useI18n } from "/src/locales"; import { useI18n } from "/src/locales";
function useAuthz() { function useAuthz() {
@@ -8,9 +8,10 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, onMounted, onActivated } from "vue"; import { useFs } from "@fast-crud/fast-crud";
import { useCrud, useExpose, useFs } from "@fast-crud/fast-crud"; import { defineComponent } from "vue";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
export default defineComponent({ export default defineComponent({
name: "UserManager", name: "UserManager",
setup() { setup() {
@@ -22,12 +22,13 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue"; import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api"; import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales"; import { useI18n } from "/src/locales";
import { useCrudPermission } from "/@/plugin/permission";
const { t } = useI18n(); const { t } = useI18n();
@@ -55,10 +56,7 @@ const handleBatchDelete = () => {
}; };
// //
onMounted(() => { useMounted(async () => {
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
@@ -22,12 +22,13 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue"; import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api"; import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales"; import { useI18n } from "/src/locales";
import { useCrudPermission } from "/@/plugin/permission";
const { t } = useI18n(); const { t } = useI18n();
@@ -55,10 +56,7 @@ const handleBatchDelete = () => {
}; };
// //
onMounted(() => { useMounted(async () => {
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
@@ -19,12 +19,13 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue"; import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api"; import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales"; import { useI18n } from "/src/locales";
import { useCrudPermission } from "/@/plugin/permission";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
const { t } = useI18n(); const { t } = useI18n();
@@ -61,10 +62,7 @@ const handleBatchDelete = () => {
}; };
// //
onMounted(() => { useMounted(async () => {
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
@@ -17,14 +17,16 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue"; import { useMounted } from "/@/use/use-mounted";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api"; import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales"; import { useI18n } from "/src/locales";
import { useProjectStore } from "/@/store/project"; import { useCrudPermission } from "/@/plugin/permission";
import AdminModeIntro from "./intro.vue"; import AdminModeIntro from "./intro.vue";
import { useProjectStore } from "/@/store/project";
const { t } = useI18n(); const { t } = useI18n();
defineOptions({ defineOptions({
@@ -52,10 +54,7 @@ const handleBatchDelete = () => {
}; };
// //
onMounted(() => { useMounted(async () => {
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
@@ -17,12 +17,12 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { message, Modal } from "ant-design-vue"; import { message, Modal } from "ant-design-vue";
import { DeleteBatch } from "./api"; import { DeleteBatch } from "./api";
import { useI18n } from "/src/locales"; import { useI18n } from "/src/locales";
import { useMounted } from "/@/use/use-mounted";
const { t } = useI18n(); const { t } = useI18n();
@@ -31,15 +31,11 @@ defineOptions({
}); });
const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions }); const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions });
onActivated(async () => {
await crudExpose.doRefresh();
});
const selectedRowKeys = context.selectedRowKeys; const selectedRowKeys = context.selectedRowKeys;
const handleBatchDelete = () => { const handleBatchDelete = () => {
if (selectedRowKeys.value?.length > 0) { if (selectedRowKeys.value?.length > 0) {
Modal.confirm({ Modal.confirm({
title: t("certd.confirm"), title: t("certd.pluginManagement"),
content: t("certd.batchDeleteConfirm", { count: selectedRowKeys.value.length }), content: t("certd.batchDeleteConfirm", { count: selectedRowKeys.value.length }),
async onOk() { async onOk() {
await DeleteBatch(selectedRowKeys.value); await DeleteBatch(selectedRowKeys.value);
@@ -49,13 +45,13 @@ const handleBatchDelete = () => {
}, },
}); });
} else { } else {
message.error(t("certd.pleaseSelectRecord")); message.error(t("certd.selectRecordFirst"));
} }
}; };
// //
onMounted(() => { useMounted(async () => {
crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
<style lang="less"></style> <style lang="less"></style>
@@ -8,10 +8,10 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { useSettingStore } from "/@/store/settings"; import { useSettingStore } from "/@/store/settings";
import { useMounted } from "/@/use/use-mounted";
defineOptions({ defineOptions({
name: "SettingsHeaderMenus", name: "SettingsHeaderMenus",
@@ -20,10 +20,7 @@ const { crudBinding, crudRef, crudExpose, context } = useFs({ createCrudOptions
const settingStore = useSettingStore(); const settingStore = useSettingStore();
// //
onMounted(() => { useMounted(async () => {
// crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
@@ -11,9 +11,9 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { onActivated, onMounted } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
defineOptions({ defineOptions({
name: "SysProductActivationCode", name: "SysProductActivationCode",
@@ -21,10 +21,8 @@ defineOptions({
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions }); const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions });
onMounted(() => { //
// crudExpose.doRefresh(); useMounted(async () => {
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
@@ -3,9 +3,10 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineEmits, onActivated, onMounted, ref } from "vue"; import { ref } from "vue";
import { useFs } from "@fast-crud/fast-crud"; import { useFs } from "@fast-crud/fast-crud";
import createCrudOptions from "./crud"; import createCrudOptions from "./crud";
import { useMounted } from "/@/use/use-mounted";
defineOptions({ defineOptions({
name: "ProductManager", name: "ProductManager",
@@ -16,10 +17,7 @@ const context: any = { emit };
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context }); const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context });
// //
onMounted(() => { useMounted(async () => {
crudExpose.doRefresh();
});
onActivated(async () => {
await crudExpose.doRefresh(); await crudExpose.doRefresh();
}); });
</script> </script>
+1 -1
View File
@@ -13,7 +13,7 @@ typeorm:
#plus: #plus:
# server: # server:
# baseUrl: 'https://api.ai.handsfree.work' # baseUrl: 'https://api.ai.handfree.work'
plus: plus:
server: server:
+1 -1
View File
@@ -14,7 +14,7 @@ typeorm:
#plus: #plus:
# server: # server:
# baseUrl: 'https://api.ai.handsfree.work' # baseUrl: 'https://api.ai.handfree.work'
plus: plus:
server: server:
+31
View File
@@ -3,6 +3,37 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
### Bug Fixes
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
### Performance Improvements
* **plugin:** 增加 Dynadot DNS and access 插件 ([a3a215b](https://github.com/certd/certd/commit/a3a215b7ae2b90efcde91270ce4165bbfe77dc64))
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
**Note:** Version bump only for package @certd/ui-server
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
### Bug Fixes
* **cert-plugin:** 修复DNS提供商授权无法回显的bug ([016ae86](https://github.com/certd/certd/commit/016ae865b1d914fe5792e77a08e3ab5358df5f89))
* Parse PEM chain and import certificate chain ([#747](https://github.com/certd/certd/issues/747)) ([454912d](https://github.com/certd/certd/commit/454912d31407d350cbd170953ccbd0564e74fd6c))
### Performance Improvements
* 添加AWS Rate Limit应对措施 ([#748](https://github.com/certd/certd/issues/748)) ([56b8c68](https://github.com/certd/certd/commit/56b8c689ec2b5cff49010a8c765483dd36803e9d))
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
* 优化 HiPM DNSMgr 插件,添加域名查询双层策略 ([#744](https://github.com/certd/certd/issues/744)) @WUHINS ([0f3f851](https://github.com/certd/certd/commit/0f3f8519e04d95cb848e28b98a3d4fcbed481fce))
### Reverts
* Revert "perf: 添加AWS Rate Limit应对措施 (#748)" (#749) ([5e8bdac](https://github.com/certd/certd/commit/5e8bdac00850bed4f5f2a272bee42c490730ec21)), closes [#748](https://github.com/certd/certd/issues/748) [#749](https://github.com/certd/certd/issues/749)
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05) ## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
### Performance Improvements ### Performance Improvements
@@ -0,0 +1,36 @@
name: dynadot
title: Dynadot授权
desc: ''
icon: simple-icons:dynatrace
input:
apiKey:
title: API Key
component:
placeholder: api key
helper: >-
前往 [Dynadot
API设置](https://www.dynadot.cn/zh/account/domain/setting/api.html) 获取API
Key
required: true
encrypt: true
apiSecret:
title: API Secret
component:
name: a-input-password
vModel: value
placeholder: api secret
helper: >-
前往 [Dynadot
API设置](https://www.dynadot.cn/zh/account/domain/setting/api.html) 获取API
Secret
required: true
encrypt: true
testRequest:
title: 测试
component:
name: api-test
action: TestRequest
helper: 点击测试接口是否正常
pluginType: access
type: builtIn
scriptFilePath: /plugins/plugin-dynadot/access.js
@@ -106,9 +106,13 @@ input:
onSelectedChange: ctx.compute(({form})=>{ onSelectedChange: ctx.compute(({form})=>{
return ($event)=>{ return ($event)=>{
form.dnsProviderAccessType = $event.accessType form.dnsProviderAccessType = $event.accessType
}
}),
onChange: ctx.compute(({form})=>{
return ($event)=>{
form.dnsProviderAccess = null form.dnsProviderAccess = null
} }
}) }),
}, },
} }
@@ -71,7 +71,7 @@ input:
}, },
} }
helper: 你在七牛云上配置的CDN加速域名,比如:certd.handsfree.work helper: 你在七牛云上配置的CDN加速域名,比如:certd.handfree.work
order: 0 order: 0
output: {} output: {}
pluginType: deploy pluginType: deploy

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