Compare commits

..

132 Commits

Author SHA1 Message Date
xiaojunnuo 67b05e2d75 feat: 支持dns-persist-01持久化验证方式申请证书,优化Acme账号的存储方式 2026-05-24 05:42:51 +08:00
xiaojunnuo 8edb6f8727 perf: 新增阿里云直播证书部署插件 2026-05-22 19:06:55 +08:00
xiaojunnuo b30f02a1fb chore: 1 2026-05-22 00:06:55 +08:00
xiaojunnuo 7e2333a63a chore: 1 2026-05-22 00:06:18 +08:00
xiaojunnuo 0be66cccbc build: release 2026-05-21 23:48:39 +08:00
xiaojunnuo ed26ed196d build: publish 2026-05-21 23:26:57 +08:00
xiaojunnuo a204f270dd build: trigger build image 2026-05-21 23:26:51 +08:00
xiaojunnuo 7585d7bbd0 build: publish 2026-05-21 23:25:32 +08:00
xiaojunnuo 2981f086c8 build: trigger build image 2026-05-21 23:25:21 +08:00
xiaojunnuo 784ef8a6a4 chore: 1 2026-05-21 23:23:20 +08:00
xiaojunnuo 01c91ba294 v1.40.3 2026-05-21 22:59:22 +08:00
xiaojunnuo 05629acfa9 build: prepare to build 2026-05-21 22:57:04 +08:00
xiaojunnuo 12a3afe15b chore: 1 2026-05-21 22:56:16 +08:00
xiaojunnuo 3bb68e3111 chore: 1 2026-05-21 22:52:19 +08:00
xiaojunnuo 5ba33be30f fix: 修复暗黑模式下注册页面验证码看不清的问题 2026-05-20 18:16:42 +08:00
xiaojunnuo 1e7b057946 chore: 1 2026-05-20 16:00:14 +08:00
xiaojunnuo fb5b00d73f perf: 修复商业版套餐添加和修改时的字段显示 2026-05-20 14:52:46 +08:00
xiaojunnuo 09ccfa2624 chore: 删除pnpm-lock文件 2026-05-20 14:27:00 +08:00
xiaojunnuo 5c80c99b94 chore: 优化fix 2026-05-20 12:25:56 +08:00
xiaojunnuo adcf570b15 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-05-20 12:07:58 +08:00
xiaojunnuo 5ccd6e64bc build: release 2026-05-20 01:06:32 +08:00
xiaojunnuo ff7e7858c0 build: publish 2026-05-20 00:40:40 +08:00
xiaojunnuo 37bcd8ce08 build: trigger build image 2026-05-20 00:40:28 +08:00
xiaojunnuo 678b70cee8 v1.40.2 2026-05-20 00:38:50 +08:00
xiaojunnuo c0ca8da4ed build: prepare to build 2026-05-20 00:35:35 +08:00
xiaojunnuo a07dcb1cf5 chore: 1 2026-05-20 00:34:20 +08:00
xiaojunnuo cb4a86d1d5 build: prepare to build 2026-05-20 00:26:45 +08:00
xiaojunnuo 0499347588 fix(certd-server): 调整首页缓存控制头的判断逻辑
新增对首页路径 / 和 /index.html 的强制不缓存处理,确保首页始终获取最新内容
2026-05-20 00:21:42 +08:00
xiaojunnuo cb08e061d2 perf(controller): 更换版本获取源并添加版本标准化处理
将npm镜像源的版本查询替换为AtomGit仓库的最新release接口,新增normalizeReleaseVersion方法标准化版本号格式,移除v前缀
2026-05-19 22:58:03 +08:00
xiaojunnuo 787b52bef7 docs: 更新文档内容,完善证书复用相关说明
1. 重命名前置任务文档标题为证书复用
2. 在常见问题中添加同证书多云上传的复用指引
3. 在侧边栏新增证书复用导航入口
2026-05-19 14:39:58 +08:00
xiaojunnuo 9bf7ca400f build: release 2026-05-19 00:58:34 +08:00
xiaojunnuo 9f75e30af3 Merge branch 'v2' of https://github.com/certd/certd into v2 2026-05-19 00:55:41 +08:00
xiaojunnuo 3f0243ba9a build: publish 2026-05-19 00:38:56 +08:00
xiaojunnuo 1a9b367c9d build: trigger build image 2026-05-19 00:38:44 +08:00
xiaojunnuo 73996f055b v1.40.1 2026-05-19 00:37:16 +08:00
xiaojunnuo 57ea2f317a build: prepare to build 2026-05-19 00:34:11 +08:00
xiaojunnuo c66c05b442 chore: 1 2026-05-19 00:32:24 +08:00
xiaojunnuo 6450d2bd56 chore: 商业版也支持企业模式 2026-05-19 00:23:32 +08:00
xiaojunnuo b5368d120d chore: tsconfig.build 支持sourcemap控制 2026-05-19 00:22:48 +08:00
Robert Silén a88d14a3bd dos: Document MariaDB support in README (#737)
Mention MariaDB in the database feature list. Install docs and TypeORM
already support type=mariadb; the app uses the mysql2 driver.
2026-05-17 00:40:41 +08:00
xiaojunnuo bdb3d09c09 perf(suite): 商业版用户已购套餐支持修改 2026-05-17 00:29:36 +08:00
xiaojunnuo 667e7b185b fix(email-service): 优化商业版测试邮件内容中带的url链接 2026-05-16 02:54:15 +08:00
xiaojunnuo 8483ee0d41 refactor(util.cache): 优化定时清理过期缓存的定时器行为
新增定时器变量保存intervalId,并调用unref方法避免阻塞进程退出
2026-05-16 02:46:28 +08:00
xiaojunnuo c3baaf3ac7 chore: auto fix 存储标记,确保fix只运行一次 2026-05-16 02:23:21 +08:00
xiaojunnuo c63745d1ba perf: 商业版支持限制泛域名数量 2026-05-16 02:18:06 +08:00
xiaojunnuo 59b9ffadd0 fix: 固化华为云sdk版本,避免华为云调用报错 2026-05-15 23:18:51 +08:00
xiaojunnuo d131ea3790 build: release 2026-05-15 01:14:12 +08:00
xiaojunnuo b849d34be5 build: publish 2026-05-15 00:50:09 +08:00
xiaojunnuo 58fc9a551c build: trigger build image 2026-05-15 00:49:57 +08:00
xiaojunnuo 5801f34b3a v1.40.0 2026-05-15 00:48:37 +08:00
xiaojunnuo 17cf16ca92 build: prepare to build 2026-05-15 00:44:39 +08:00
xiaojunnuo 7015b1b232 perf: 头像增加缓存时间 2026-05-15 00:39:35 +08:00
xiaojunnuo 3b72ca09c6 chore: 支持设置初始化密码 2026-05-15 00:26:24 +08:00
xiaojunnuo a815d0245b perf: 第三方登录自动注册的用户支持设置初始化密码 2026-05-15 00:25:28 +08:00
xiaojunnuo 229f22d5a9 docs: 自动升级docker-compose 2026-05-15 00:12:44 +08:00
xiaojunnuo 22f5cfcfd8 fix: 修复clogin登录丢失state问题 2026-05-14 23:44:04 +08:00
xiaojunnuo 90ba55c043 chore: 1 2026-05-14 19:05:49 +08:00
xiaojunnuo 9f878a353c fix: 修复clogin多选类型登录失败的bug 2026-05-14 19:04:20 +08:00
xiaojunnuo af7297d671 chore: 1 2026-05-14 18:47:28 +08:00
xiaojunnuo 2f172b56e9 fix: 修复群晖授权没有显示设备id输入框的bug 2026-05-14 18:42:35 +08:00
xiaojunnuo 9076c8b20e Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-05-14 18:31:34 +08:00
xiaojunnuo 639756dfcd chore: sdk 2026-05-14 18:31:25 +08:00
xiaojunnuo 7aa0c7e491 feat: 彩虹登录支持选择多种登录方式 2026-05-14 01:39:22 +08:00
xiaojunnuo 45dedf5bc7 fix: 修复第三方登录丢失state时无法在用户信息页面绑定第三方账号的bug 2026-05-13 23:47:31 +08:00
xiaojunnuo 4681ec9008 fix: 修复自动注册后没有跳转到控制台的bug 2026-05-13 22:47:46 +08:00
xiaojunnuo b91826c6e6 chore: 1 2026-05-13 17:29:49 +08:00
xiaojunnuo 686856d0ae build: release 2026-05-13 17:29:19 +08:00
xiaojunnuo 9b09d2578d chore: 1 2026-05-13 17:14:14 +08:00
xiaojunnuo f8f51adf88 build: publish 2026-05-13 14:22:46 +08:00
xiaojunnuo f8ce639717 build: trigger build image 2026-05-13 14:22:33 +08:00
xiaojunnuo 1c6dc169ac v1.39.16 2026-05-13 14:21:21 +08:00
xiaojunnuo 3e5366c74e build: prepare to build 2026-05-13 14:13:01 +08:00
xiaojunnuo b49ddbfef9 v1.39.15 2026-05-13 13:49:09 +08:00
xiaojunnuo b92fd73f53 build: prepare to build 2026-05-13 13:33:12 +08:00
xiaojunnuo 41b8f51a6a chore: 1 2026-05-13 13:32:01 +08:00
xiaojunnuo aad9045de5 perf(network): 新增全局公共http请求 headers设置
1. 新增公共请求头配置项,支持在系统设置中配置全局请求头
2. 实现请求头解析工具方法,支持多行KEY=VALUE格式配置
3. 在请求发起时自动附加全局公共请求头,且不会覆盖请求中已存在的同名Header
4. 添加多语言配置与前端表单组件,完善配置界面
5. 新增单元测试验证全局请求头合并逻辑
2026-05-13 12:09:01 +08:00
xiaojunnuo fdd5848df4 perf: icon选择器增加一套logo集 2026-05-13 11:20:55 +08:00
xiaojunnuo 118c15d046 perf(monitor/site): 新增站点监控页面禁用启用、检查状态两个筛选条件 2026-05-13 11:14:00 +08:00
xiaojunnuo bae4f8e320 fix: 修复第三方登录彩虹登录不上的bug 2026-05-13 11:03:10 +08:00
xiaojunnuo e0189a566e docs(donate): update privilege comparison table to add commercial edition details 2026-05-12 15:08:46 +08:00
xiaojunnuo 1cd8d73cdb Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-05-12 10:51:41 +08:00
xiaojunnuo d6e9e5987b perf: 优化申请时报错日志增加对应域名打印 2026-05-12 10:51:34 +08:00
xiaojunnuo 8c5aa37745 build: release 2026-05-12 00:51:59 +08:00
xiaojunnuo a18a871ac3 build: publish 2026-05-12 00:22:37 +08:00
xiaojunnuo 90cbff9cf9 build: trigger build image 2026-05-12 00:22:25 +08:00
xiaojunnuo bae5a04dcc v1.39.14 2026-05-12 00:21:06 +08:00
xiaojunnuo 7146570392 build: prepare to build 2026-05-12 00:06:37 +08:00
xiaojunnuo ae88f85d8e chore: 1 2026-05-12 00:06:02 +08:00
xiaojunnuo a362860137 fix: 修复阿里云订阅流水线创建对话框无法获取阿里订单列表的bug 2026-05-11 23:47:48 +08:00
xiaojunnuo c966896522 fix: 修复启动时报密钥备份不存在的问题 2026-05-11 22:31:11 +08:00
xiaojunnuo 5f88da1985 build: release 2026-05-11 10:26:12 +08:00
xiaojunnuo 043b80a298 chore: 1 2026-05-11 09:41:47 +08:00
xiaojunnuo ed0da28896 Merge branch 'v2-dev' of https://github.com/certd/certd into v2-dev 2026-05-11 09:40:22 +08:00
xiaojunnuo 61a0d69d58 chore: 打包报错, 固定pnpm@10.33.4 2026-05-11 09:39:04 +08:00
xiaojunnuo 3833a9216e chore: 1 2026-05-11 01:21:44 +08:00
xiaojunnuo e59566b5e2 chore: 1 2026-05-11 01:20:00 +08:00
xiaojunnuo e4be0ce464 chore: 1 2026-05-11 01:13:44 +08:00
xiaojunnuo 022dbf0cab chore: 1 2026-05-11 00:54:50 +08:00
xiaojunnuo 1e6b559b89 chore: 1 2026-05-11 00:48:05 +08:00
xiaojunnuo 74bae2005d build: publish 2026-05-11 00:28:04 +08:00
xiaojunnuo 1731a35d94 build: trigger build image 2026-05-11 00:27:48 +08:00
xiaojunnuo 9f7d766cb3 v1.39.13 2026-05-11 00:26:11 +08:00
xiaojunnuo 6c0d0b00c9 build: prepare to build 2026-05-11 00:21:28 +08:00
xiaojunnuo a82a38421d chore: sql 2026-05-11 00:20:25 +08:00
xiaojunnuo c4b01da384 fix(plugin-aliyun): 过滤非CAS证书并优化日志信息
过滤证书列表仅保留CAS类型证书,并更新相关日志信息以更准确反映操作情况
2026-05-11 00:07:48 +08:00
xiaojunnuo 3e1473dba5 chore: 1 2026-05-11 00:03:16 +08:00
xiaojunnuo d383706554 chore(用户资料): 添加头像和昵称的编辑按钮
- 在头像和昵称旁边添加编辑按钮,提升用户操作便捷性
- 移除原有的更新资料按钮,优化界面布局
- 调整角色信息显示位置至用户名旁边
2026-05-10 23:58:23 +08:00
xiaojunnuo e0eb0e21f6 perf(用户资料): 新增手机号邮箱绑定功能
实现用户邮箱和手机号的绑定与修改功能,包括:
1. 添加联系方式绑定API接口
2. 实现身份验证流程
3. 添加前端绑定对话框组件
4. 完善用户资料页面的联系方式展示和编辑入口
5. 添加联系方式冲突检测逻辑
6. 实现验证码校验功能
2026-05-10 23:51:45 +08:00
xiaojunnuo 7266af1749 fix: cnameProvider域名支持设置子域名托管 2026-05-10 22:41:07 +08:00
xiaojunnuo f93bc09438 chore(plugin-cert): 更新EAB选项标签和生成按钮文本
- 移除EAB选项中的免费说明文字
- 将"刷新"按钮文本改为"生成"
- 更新相关提示信息以保持一致性
2026-05-10 18:28:03 +08:00
xiaojunnuo fe3bb7c1b4 chore(plugin-cert): 修改刷新按钮文本为更简洁的表述 2026-05-10 17:15:47 +08:00
xiaojunnuo 923676c7d5 build: 添加 cross-env 依赖并更新单元测试脚本
在 package.json 和 pnpm-lock.yaml 中添加 cross-env 依赖,用于在单元测试时设置 NODE_ENV=unittest 环境变量。同时更新 AGENTS.md 文档说明单元测试环境要求。
2026-05-10 17:06:00 +08:00
xiaojunnuo 4755216505 perf: 重构自动加载模块并优化EAB授权处理
refactor(ui): 将分散的auto-*模块整合为统一命名的auto-register模块
perf(plugin-cert): 增强EAB授权功能,支持账号私钥刷新和类型选择
test: 添加EAB授权服务和ACME账号配置的单元测试
docs: 更新AGENTS.md补充ACME/EAB使用注意事项
chore: 统一各package.json中的测试脚本配置
2026-05-10 16:57:12 +08:00
xiaojunnuo 37d03c10f9 chore: 1 2026-05-08 22:28:10 +08:00
xiaojunnuo 490b724808 chore: 更新腾讯云证书(Id不变)插件下架 2026-05-08 22:19:31 +08:00
xiaojunnuo d8f132919d chore: 1 2026-05-08 15:50:28 +08:00
xiaojunnuo b8a64a6b5b perf(plugin-volcengine): 支持火山引擎VKE部署插件
- 改进Kubernetes错误处理,添加RBAC权限不足的友好提示
- 添加集群ID验证和kubeconfig解码验证
- 优化Ingress不存在时的错误提示,显示可用选项
- 移除未使用的TargetCluster选项
- 修复kubeconfig请求中集群ID未验证的问题
2026-05-08 00:25:00 +08:00
xiaojunnuo 25ad1e6f86 perf(设置): 添加首页启用开关配置
在系统设置中添加首页启用开关配置项,包括前端路由守卫检查、多语言支持和表单配置,以控制是否显示首页
2026-05-07 23:15:50 +08:00
xiaojunnuo 6b6f1604e9 style(ui): 修复批量操作区域样式溢出问题
为批量操作区域添加 overflow-x-auto 和 whitespace-nowrap 样式,防止文本溢出
2026-05-07 23:01:42 +08:00
xiaojunnuo 63be1c1cbd perf(证书流水线): 添加批量更新证书申请参数功能
实现批量更新证书申请参数功能,包括前端界面和后端处理逻辑
- 添加批量修改证书申请参数的按钮和对话框
- 实现后端批量更新证书申请参数的接口和服务
- 添加相关测试用例验证功能正确性
2026-05-07 22:54:29 +08:00
xiaojunnuo b75c625ddc fix(aliyun-access): 添加阿里云密钥校验失败的错误处理
在测试阿里云访问密钥时,增加对STS错误响应的处理逻辑
添加相关测试用例验证错误处理和正常情况
2026-05-07 22:18:34 +08:00
xiaojunnuo 7083e7aff7 test(tld-client): 添加对rdap.ss回退机制的测试
验证当rdap和whoiser都失败时,TldClient能正确委托RdapSsClient处理
2026-05-07 22:03:33 +08:00
xiaojunnuo 9d2937dd4b perf(domain): 添加域名过期时间同步进度显示功能
添加同步进度对话框,展示同步任务的实时进度和状态
更新中英文翻译文件,添加相关文案
修改AGENTS.md文档中的格式化命令说明
2026-05-07 00:22:41 +08:00
xiaojunnuo a7e281e278 chore: rdap-ss client 重构 2026-05-05 22:53:22 +08:00
xiaojunnuo 72b6597817 chore: agents update 2026-05-05 22:17:09 +08:00
xiaojunnuo 91a1b97550 perf: 域名注册过期时间获取再次优化 2026-05-05 21:56:08 +08:00
xiaojunnuo 9951ab678f chore: rm app.log 2026-05-05 19:26:36 +08:00
xiaojunnuo 930aa355e8 refactor(acme-client): 将acme-client改造成ts包并优化项目结构
重构acme-client模块,将原有JavaScript代码迁移至TypeScript
添加类型定义文件(.d.ts)和类型检查
更新构建配置和脚本以支持TypeScript编译
优化项目目录结构和模块导出方式
更新相关依赖和开发工具配置
2026-05-05 19:17:44 +08:00
xiaojunnuo e0143fa540 chore: 补充单元测试 2026-05-05 18:44:43 +08:00
xiaojunnuo 7c1d92ff4b chore: 为多个package.json添加test:unit脚本并更新全局测试命令
在多个库和插件的package.json中添加统一的test:unit脚本
同时在根package.json中添加全局test:unit命令以并行运行所有子项目的单元测试
2026-05-01 09:25:53 +08:00
xiaojunnuo 0a0f1e90e1 chore: 补充单元测试 2026-05-01 09:16:46 +08:00
xiaojunnuo 80092823db docs(AGENTS): 更新文档以明确文件读取时的编码设置
添加关于在PowerShell中读取中文和文档文件时使用UTF-8编码的说明,并更新示例命令
2026-05-01 00:21:28 +08:00
324 changed files with 11456 additions and 28587 deletions
+1 -1
View File
@@ -44,7 +44,7 @@ jobs:
# cache: 'npm'
# working-directory: ./packages/ui/certd-client
- run: |
npm install -g pnpm
npm install -g pnpm@10.33.4
pnpm install
npm run build
working-directory: ./packages/ui/certd-client
+1 -1
View File
@@ -46,7 +46,7 @@ jobs:
console.log("certd_version:",pkg.version);
return pkg.version
- run: |
npm install -g pnpm
npm install -g pnpm@10.33.4
pnpm install
npm run build
working-directory: ./packages/ui/certd-client
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
run: |
export GITEE_TOKEN=${{ secrets.GITEE_TOKEN }}
rm -rf ./pnpm*.yaml
npm install -g pnpm
npm install -g pnpm@10.33.4
pnpm install
npm run publish_to_gitee
working-directory: ./
+1 -1
View File
@@ -35,7 +35,7 @@ jobs:
run: |
export GITHUB_TOKEN=${{ secrets.GH_TOKEN }}
rm -rf ./pnpm*.yaml
npm install -g pnpm
npm install -g pnpm@10.33.4
pnpm install
npm run publish_to_github
working-directory: ./
+1 -1
View File
@@ -50,7 +50,7 @@ jobs:
# cache: 'npm'
# working-directory: ./packages/ui/certd-client
- run: |
npm install -g pnpm
npm install -g pnpm@10.33.4
pnpm install
npm run build
working-directory: ./packages/ui/certd-client
+4 -1
View File
@@ -31,4 +31,7 @@ test/**/*.js
/packages/pro/
test.js
.history
/logs
/logs
.pnpm-lock.yaml
pnpm-lock.yaml
.studio/
+93
View File
@@ -0,0 +1,93 @@
---
name: fast-crud-page-dev
description: 用于开发或重构 Certd 前端列表管理、后台管理、记录查询、CRUD 表格页面,优先使用 Fast Crud(@fast-crud/fast-crud、fs-crud、useFs、createCrudOptions)实现。当用户要求列表页、管理页、审核页、记录页或表格 CRUD 页面时触发。
version: 1.0.0
---
# Fast Crud 页面开发技能
## 角色定义
你是一名 Certd 前端列表管理页面开发专家,熟悉 Vue 3、Ant Design Vue、Fast Crud 和本仓库现有页面拆分方式。你的目标是让管理页面保持统一的表格、搜索、分页、操作列和弹窗体验。
## 核心规则
- 列表管理、后台管理、记录查询、审核记录、CRUD 表格类页面,默认优先使用 Fast Crud 实现。
- 只有轻量只读展示、强交互自定义界面、复杂可视化或已有页面模式明确不适合 Fast Crud 时,才手写 `a-table` / 自定义列表,并在回复中说明原因。
- 设置表单、概览卡片、向导流程等非列表主体可以保留自定义 Vue;如果同一功能同时包含设置和列表,优先拆成独立页面,或把设置放入对话框。
## 推荐文件拆分
- `api.ts`:封装接口请求,保持页面和 CRUD 配置里不直接散落 URL。
- `crud.tsx` / `crud-*.tsx`:导出 `createCrudOptions`,集中定义请求映射、搜索项、列、表单、操作列、工具栏和字典。
- `index.vue`:承载 `fs-page``fs-crud`、页面头部、弹窗和生命周期,使用 `useFs({ createCrudOptions, context })` 创建绑定。
## 实现流程
1. 先在 `packages/ui/certd-client/src/views` 下找 1-2 个相近 Fast Crud 页面,沿用它们的导入、布局、命名和权限写法。
2.`index.vue` 中使用 `fs-crud ref="crudRef" v-bind="crudBinding"`,并在 `onMounted` / `onActivated` 时调用 `crudExpose.doRefresh()`
3.`crud.tsx` 中配置 `request.pageRequest``columns``search``form``rowHandle``actionbar``toolbar` 等,接口分页参数和返回值按现有页面适配。
4. 操作按钮优先放在 Fast Crud 的 `rowHandle.buttons``actionbar.buttons` 中;审核、保存设置、批量操作等复杂交互可通过 `context` 调用 `index.vue` 中的方法。
5. 金额、状态、时间、枚举等字段优先复用项目已有组件、字典和格式化工具;避免在模板里重复堆格式化逻辑。
6. 表格查询条件使用 Fast Crud 的 `search` 配置;新增/编辑表单使用 Fast Crud 的 `form` 配置,复杂设置项可以用 Ant Design Vue 对话框承载。
7. 删除、审核通过、拒绝等危险操作必须保留确认弹窗和错误提示,成功后刷新当前 CRUD 列表。
8. 对话框里只做纯确认时可以使用 `Modal.confirm`;只要需要字段输入、表单校验或提交字段,统一使用 `useFormDialog` / `openFormDialog`,不要在 `Modal.confirm``content` 里手写输入框。
## crud 配置
const crudOptions ={
id: string, //表格唯一标识,同一个页面的多个表格的列设置和字段设置会根据id进行区分保存
request:{}, //http请求
columns:{ //字段配置
key:{ //字段key
column:{}, //对应table-column配置
form:{}, //表单中该字段的公共配置,viewForm、addForm、editForm、search会集成此配置,支持对应ui的form-item配置
viewForm:{}, //查看表单中该字段的配置,支持对应ui的form-item配置
addForm:{}, // 添加表单中该字段的配置,支持对应ui的form-item配置
editForm:{}, //编辑表单中该字段的配置,支持对应ui的form-item配置
search:{} //对应查询表单的form-item配置
}
},
search:{ //查询框配置 ,对应fs-search组件
options:{} //查询表单配置 ,对应el-from, a-form配置
},
actionbar:{}, //动作条,添加按钮,对应fs-actionbar组件
toolbar:{}, //工具条 ,对应fs-toolbar组件
table:{ //表格配置,对应fs-table
// 对应 el-table / a-table的配置
slots:{} // 对应el-table ,a-table的插槽
},
data:{}, //列表数据,无需配置,自动从pageRequest中获取
// 如果你要手动改变表格数据,可以通过crudBinding.value.data直接赋值修改表格数据
rowHandle:{}, //操作列配置,对应fs-row-handle
form:{ //表单的公共配置,对应el-forma-form配置
wrapper:{} //表单外部容器(对话框)的配置,对应el-dialog,el-drawer,a-model,a-drawer的配置
},
viewForm:{}, //查看表单的独立配置
editForm:{}, //编辑表单的独立配置
addForm:{}, //添加表单的独立配置
pagination:{}, //分页配置 ,对应el-pagination / a-pagination
container:{}, //容器配置 ,对应fs-container
}
## 布局高度
- Fast Crud 表格依赖外部容器高度计算。虽然表格本身有默认约 200px 高度,但页面内嵌 `fs-crud` 时,为了获得稳定可用的列表区域,必须让外层容器提供明确高度或剩余高度。
- 独立列表页通常可直接让 `fs-page` / 页面内容区撑满;如果表格嵌在 tabs、详情页、上下分区或弹窗里,要从页面根容器到 `fs-crud` 建立完整的 flex 高度链路:父容器 `display: flex; flex-direction: column; min-height: 0`,中间内容区和 tab pane 使用 `flex: 1; min-height: 0``fs-crud` 本身也使用 `flex: 1; min-height: 0`
- 有固定操作栏、统计区、说明区时,这些区域应 `flex: none`,把剩余空间交给表格区域。
- 修改嵌入式 Fast Crud 页面后,要检查空数据、少量数据和多页数据时表格高度、分页器和空状态是否仍在预期区域内。
## 代码习惯
- 页面命名、API 命名、权限标识和路由结构要贴近同目录已有页面。
- CRUD 配置中不要写大段业务流程;复杂逻辑放回 `index.vue` 方法或 `api.ts`
- 能用 `dict``compute``valueBuilder``valueResolve``component` 配置表达的表格/表单行为,不要改成手写模板。
- 保持列表页密度和操作入口克制,不要做营销式布局、嵌套卡片或大块说明文字。
- 如果页面有“设置 + 列表”,管理端优先拆成两个路由页面;用户端提现设置这类低频配置优先使用对话框保存。
## 验证方式
- 前端改动后,只对本次改动的 Vue / TS / TSX / locale 文件运行项目现有 Prettier / ESLint。
- 不运行 `vue-tsc` / `pnpm tsc`,因为当前依赖组合下 `vue-tsc` 已知会抛内部错误。
- 若只是新增或修改本 skill / 文档,不需要运行前端格式化和测试。
+2 -1
View File
@@ -20,5 +20,6 @@
"scm.repositories.visible": 9,
"scm.repositories.explorer": false,
"scm.repositories.selectionMode": "multiple",
"scm.repositories.sortOrder": "discovery time"
"scm.repositories.sortOrder": "discovery time",
"git.ignoreLimitWarning": true
}
+27 -11
View File
@@ -1,4 +1,4 @@
# Certd 开发 Agent 上下文
# Certd 开发 Agent 上下文
这个文件是给在本仓库工作的开发 agent 看的常驻项目说明。后续会话进入仓库后,应先读取它,再按任务需要查看具体代码,避免每次都重新全量扫描项目。
@@ -105,8 +105,8 @@ Certd 是一个支持私有化部署的 SSL/TLS 证书自动化管理平台。
- 前端 `pnpm dev`:启动 Vite 开发服务
- 前端 `pnpm build`:生产构建
- 前端 `pnpm tsc`类型检查
- 前端 `pnpm test:unit`Vitest 单元测试
- 不要运行前端 `pnpm tsc` / `vue-tsc`:当前依赖组合中 `vue-tsc@1.8.27` 会直接抛内部错误 `Search string not found: "/supportedTSExtensions = .*(?=;)/"`,不是有效的项目类型检查结果。
- 前端暂不跑单元测试;当前 `test:unit` 只是占位脚本
## 流水线与插件模型
@@ -142,6 +142,12 @@ Certd 是一个支持私有化部署的 SSL/TLS 证书自动化管理平台。
如果只是某个服务商或部署目标的问题,不要轻易修改共享 pipeline/core 行为,除非确实是可复用的公共能力。
### ACME / EAB 注意事项
- 公共 EAB(尤其是 Google EAB)可能只能创建一次 ACME 账号。要跨用户复用公共 EAB,应保存并复用同一个 ACME account private key`accountUrl` 如果存到 `userContext` 里,只能视为当前用户缓存,因为 `userContext` 跟用户 id 走。
- ACME 协议的 `newAccount` 支持 `onlyReturnExisting`。使用同一个 account private key 调用 `newAccount({ onlyReturnExisting: true })` 可以取回已创建账号的 URL,且不会再次消费 EAB。
- 修改 EAB 的 `kid` 后,应重新生成绑定该 `kid` 的 account private key;否则应阻止继续申请并提示用户刷新账号私钥。
## 数据与迁移
后端使用 TypeORM 实体加 SQL 迁移。
@@ -161,6 +167,7 @@ Certd 是一个支持私有化部署的 SSL/TLS 证书自动化管理平台。
- 优先沿用现有模块、插件、服务模式,再考虑新增抽象。
- `packages/ui/certd-server/data/``logs/`、生成的 metadata/dist 等通常视为运行时或构建产物,除非任务明确要求处理它们。
- 注意本地数据和配置里可能包含凭据、证书材料等敏感信息。
- 本仓库代码注释优先使用中文,尤其是解释业务规则、兼容逻辑、协议细节和隐藏风险时;除非文件已有明确英文注释风格或引用外部英文术语,否则不要新增英文说明性注释。
## 插件开发技能
@@ -185,12 +192,12 @@ Certd 是一个支持私有化部署的 SSL/TLS 证书自动化管理平台。
进入项目后,优先使用这些有目标的读取命令,而不是立刻全仓库扫描:
```powershell
Get-Content package.json
Get-Content pnpm-workspace.yaml
Get-Content lerna.json
Get-Content README_en.md -TotalCount 180
Get-Content packages\ui\certd-server\package.json
Get-Content packages\ui\certd-client\package.json
Get-Content -Encoding utf8 package.json
Get-Content -Encoding utf8 pnpm-workspace.yaml
Get-Content -Encoding utf8 lerna.json
Get-Content -Encoding utf8 README_en.md -TotalCount 180
Get-Content -Encoding utf8 packages\ui\certd-server\package.json
Get-Content -Encoding utf8 packages\ui\certd-client\package.json
Get-ChildItem packages\ui\certd-server\src\modules
Get-ChildItem packages\ui\certd-server\src\plugins
Get-ChildItem packages\ui\certd-client\src\views\certd
@@ -199,9 +206,18 @@ Get-ChildItem packages\ui\certd-client\src\views\certd
## 本仓库 Agent 工作方式
- 先读本文件,再按用户任务查看相关 package/module。
- 在 PowerShell 中读取中文、Markdown、locale、文档类文件时,显式使用 `Get-Content -Encoding utf8`;如果仍然显示乱码,再先执行 `[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()` 后重试。
- 做后端任务时,先定位 `packages/ui/certd-server/src/modules` 下的模块,以及相关 entity/service/controller。
- 做前端任务时,先定位 `packages/ui/certd-client/src/views/certd` 下的页面,再找对应 `src/api`
- 做服务商、DNS、部署、通知相关任务时,先看 `packages/ui/certd-server/src/plugins`,再看 `packages/plugins/plugin-lib` 里的共享辅助能力。
- 做数据库结构变更时,添加或更新迁移脚本,不要依赖 TypeORM 自动同步。
- 前端 TS/Vue/locale 等文件改动后,优先只对本次改动文件运行项目现有自动格式化/修复,例如 `corepack pnpm --dir packages\ui\certd-client exec prettier --write <files>``corepack pnpm --dir packages\ui\certd-client exec eslint --fix <files>`;不要为了格式化无关文件而扩大 diff。项目保留了 `tslint` 依赖,但当前主要使用 ESLint + Prettier
- 优先对改动包运行聚焦的测试或类型检查;只有跨包影响明显时再考虑全 monorepo 构建
- 实现新功能或修复行为缺陷前,先补对应单元测试,并先运行测试确认它处于失败状态;再实现功能或修复代码,反复运行聚焦单元测试直到通过。若某项改动确实不适合先写单元测试,应在回复中说明原因和替代验证方式
- 后补单元测试时,应先基于对正确行为的实际预期编写测试,而不是为了迎合现有实现改写预期;如果运行后出现红灯,且通过测试需要修改已有实现,应先向用户确认这是确实的 bug,还是原本需求/既有行为就是如此;确认后再修改原始实现,避免把测试补充变成未经确认的行为改动
- 后端纯单元测试用例放在 `src` 目录内,并尽量与被测文件相邻,例如 `src/utils/random.test.ts`;对应 `test:unit` 只跑 `src/**/*.test.ts`,构建/打包配置应排除这些 `*.test.ts` 文件。
- 单元测试需要 mock ESM 静态 import 时,优先使用 `esmock`,不要为了测试把业务代码改成构造函数注入或把逻辑挪到调用方;各包 `test:unit` 脚本应显式设置 `NODE_ENV=unittest`
- 单个 monorepo 包运行单元测试时,优先使用 `corepack pnpm --dir <包目录> test:unit`,例如 `corepack pnpm --dir packages\ui\certd-server test:unit``corepack pnpm --dir packages\core\basic test:unit``corepack pnpm --dir packages\plugins\plugin-lib test:unit`;也可以用包名过滤,例如 `corepack pnpm --filter @certd/ui-server test:unit`。前端 `packages\ui\certd-client` 暂时不跑单元测试。
- 前端 TS/Vue/locale 等文件改动后,优先只对本次改动文件运行项目现有自动格式化/修复;Windows/PowerShell 下 Prettier 已验证可用命令为 `packages\ui\certd-client\node_modules\.bin\prettier.cmd --write <files>`ESLint 可用命令为 `packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix <files>`;不要运行 `vue-tsc` / `pnpm tsc`;不要为了格式化无关文件而扩大 diff。项目保留了 `tslint` 依赖,但当前主要使用 ESLint + Prettier。
- 优先对改动包运行聚焦的测试;后端可按包运行单元测试,前端优先使用 Prettier/ESLint 做改动文件验证。只有跨包影响明显时再考虑全 monorepo 构建。
- 不要主动运行 `pnpm install` 安装依赖:用户会事先准备好 `node_modules`。如果 `pnpm install``test:unit` 因缺少依赖、TTY 或网络问题失败,立即停止尝试,告知用户解决环境问题。
+95
View File
@@ -3,6 +3,101 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.40.3](https://github.com/certd/certd/compare/v1.40.2...v1.40.3) (2026-05-21)
### Bug Fixes
* 修复暗黑模式下注册页面验证码看不清的问题 ([5ba33be](https://github.com/certd/certd/commit/5ba33be30f765f06cafbfcc04f5e25320db01449))
### Performance Improvements
* 修复商业版套餐添加和修改时的字段显示 ([fb5b00d](https://github.com/certd/certd/commit/fb5b00d73f925036a65ce5003c57c1199578c34d))
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
### Bug Fixes
* **certd-server:** 调整首页缓存控制头的判断逻辑 ([0499347](https://github.com/certd/certd/commit/0499347588ee544862420ab9a5afd2546d61bc6c))
### Performance Improvements
* **controller:** 更换版本获取源并添加版本标准化处理 ([cb08e06](https://github.com/certd/certd/commit/cb08e061d257ba23a0fefdbfb046a8c759def828))
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
### Bug Fixes
* 固化华为云sdk版本,避免华为云调用报错 ([59b9ffa](https://github.com/certd/certd/commit/59b9ffadd05faf3982151c48f8d83cbd97419865))
* **email-service:** 优化商业版测试邮件内容中带的url链接 ([667e7b1](https://github.com/certd/certd/commit/667e7b185bf26558972be01720872f48352b5876))
### Performance Improvements
* 商业版支持限制泛域名数量 ([c63745d](https://github.com/certd/certd/commit/c63745d1ba30904428ba6b13ab0785298baa5cae))
* **suite:** 商业版用户已购套餐支持修改 ([bdb3d09](https://github.com/certd/certd/commit/bdb3d09c09fc73e7e5e3401f6ef5588bf8ad5088))
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
### Bug Fixes
* 修复第三方登录丢失state时无法在用户信息页面绑定第三方账号的bug ([45dedf5](https://github.com/certd/certd/commit/45dedf5bc779fea852e1f33dda4f31db2765633c))
* 修复群晖授权没有显示设备id输入框的bug ([2f172b5](https://github.com/certd/certd/commit/2f172b56e9411303ca15138d827bdb9bafdae4d1))
* 修复自动注册后没有跳转到控制台的bug ([4681ec9](https://github.com/certd/certd/commit/4681ec90088a3eb665427b2ac4047ec5ccefd7b3))
* 修复clogin登录丢失state问题 ([22f5cfc](https://github.com/certd/certd/commit/22f5cfcfd8462ca74128329eefb3f48b3ee0b7ea))
* 修复clogin多选类型登录失败的bug ([9f878a3](https://github.com/certd/certd/commit/9f878a353cd49b7b10bb0a95610ad236bc920dd2))
### Features
* 彩虹登录支持选择多种登录方式 ([7aa0c7e](https://github.com/certd/certd/commit/7aa0c7e491fe660abb62e68792ff5474f19bd5b8))
### Performance Improvements
* 第三方登录自动注册的用户支持设置初始化密码 ([a815d02](https://github.com/certd/certd/commit/a815d0245b97efbb948b33d6fc9d49862ce06889))
* 头像增加缓存时间 ([7015b1b](https://github.com/certd/certd/commit/7015b1b232602e5168a3eb8bee6d7f1776ae1e74))
## [1.39.16](https://github.com/certd/certd/compare/v1.39.15...v1.39.16) (2026-05-13)
**Note:** Version bump only for package root
## [1.39.15](https://github.com/certd/certd/compare/v1.39.14...v1.39.15) (2026-05-13)
### Bug Fixes
* 修复第三方登录彩虹登录不上的bug ([bae4f8e](https://github.com/certd/certd/commit/bae4f8e3209d9f9869ecbd7c01655383bac2fe21))
### Performance Improvements
* 优化申请时报错日志增加对应域名打印 ([d6e9e59](https://github.com/certd/certd/commit/d6e9e5987bd52ea12ee18745615486eadd4c87ff))
* icon选择器增加一套logo集 ([fdd5848](https://github.com/certd/certd/commit/fdd5848df4055a6ee07dc5eabaaf6b718672882d))
* **monitor/site:** 新增站点监控页面禁用启用、检查状态两个筛选条件 ([118c15d](https://github.com/certd/certd/commit/118c15d04633a6ef06f2d9e7a7849d20f596e02c))
* **network:** 新增全局公共http请求 headers设置 ([aad9045](https://github.com/certd/certd/commit/aad9045de55e76cb2ad09cac74a7bd60a4b47124))
## [1.39.14](https://github.com/certd/certd/compare/v1.39.13...v1.39.14) (2026-05-11)
### Bug Fixes
* 修复阿里云订阅流水线创建对话框无法获取阿里订单列表的bug ([a362860](https://github.com/certd/certd/commit/a362860137bfb7072893c844fe775edc46070ee1))
* 修复启动时报密钥备份不存在的问题 ([c966896](https://github.com/certd/certd/commit/c9668965226af6b54e0e576931dcba8b3d188ef3))
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
### Bug Fixes
* **aliyun-access:** 添加阿里云密钥校验失败的错误处理 ([b75c625](https://github.com/certd/certd/commit/b75c625ddcc0b3110699d8e6175681ef157b25df))
* cnameProvider域名支持设置子域名托管 ([7266af1](https://github.com/certd/certd/commit/7266af17491a98338022cfb18cfedfb93ca6ef8f))
* **plugin-aliyun:** 过滤非CAS证书并优化日志信息 ([c4b01da](https://github.com/certd/certd/commit/c4b01da384bc40a241a673ea8bc01ca733c04d83))
### Performance Improvements
* **设置:** 添加首页启用开关配置 ([25ad1e6](https://github.com/certd/certd/commit/25ad1e6f861e43288cc8bd90d4903628e6faec29))
* 新增agents.md ([aa176b0](https://github.com/certd/certd/commit/aa176b081a92837d2d6809d16546a8dfc2e5dd36))
* **用户资料:** 新增手机号邮箱绑定功能 ([e0eb0e2](https://github.com/certd/certd/commit/e0eb0e21f6dae24b639c944f9aba2c90496ab1c0))
* 域名注册过期时间获取再次优化 ([91a1b97](https://github.com/certd/certd/commit/91a1b9755066bf280e194dabf7c3a9f936e2643f))
* **证书流水线:** 添加批量更新证书申请参数功能 ([63be1c1](https://github.com/certd/certd/commit/63be1c1cbd9b09a3b48f26130c296b1cedcca1ac))
* 支持火山云vke ([bb46cb0](https://github.com/certd/certd/commit/bb46cb08f71f6ae921543f7e4a6c5f4e0190556e))
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
* **domain:** 添加域名过期时间同步进度显示功能 ([9d2937d](https://github.com/certd/certd/commit/9d2937dd4b14ffab73e9b096edd2aa8539811182))
* **plugin-volcengine:** 支持火山引擎VKE部署插件 ([b8a64a6](https://github.com/certd/certd/commit/b8a64a6b5bf3691a47177de42bc49b798e795feb))
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
### Bug Fixes
+18 -13
View File
@@ -34,10 +34,11 @@ Certd® 是一个免费的全自动证书管理系统,让你的网站证书永
* **多种通知方式**: 邮件通知、webhook通知、企微、钉钉、飞书、anpush等多种通知方式
* **私有化部署**: 数据保存本地,安装简单快捷,镜像由Github Actions构建,过程公开透明
* **多重安全保障**: 授权加密,站点隐藏,2FA,密码防爆破等多重安全保障
* **多数据库支持**:支持SQLite、PostgreSQL、MySQL
* **多数据库支持**:支持SQLite、PostgreSQL、MySQL、MariaDB
* **开放接口支持**: 提供RESTful API接口,方便集成到其他系统
* **站点证书监控**: 定时监控网站证书的过期时间
* **多用户管理**: 用户可以管理自己的证书流水线
* **项目管理**: 企业级项目管理模式
* **多语言支持** 中英双语切换
* **无忧升级** 版本向下兼容
@@ -179,19 +180,23 @@ https://certd.handfree.work/
[50元专业版优惠券限时领取](https://app.handfree.work/subject/#/app/certd/product)
专业版特权对比
| 功能 | 免费版 | 专业版 |
|---------|---------------------------------------|--------------------------------|
| 免费证书申请 | 免费无限制 | 免费无限制 |
| 证书域名数量 | 无限制 | 无限制 |
| 证书流水线条数 | 无限制 | 无限制 |
| 自动部署插件 | 阿里云CDN、腾讯云、七牛CDN、主机部署、宝塔、1Panel等大部分插件 | 群晖、威联通、proxmox等 |
| 通知 | 邮件通知、自定义webhook | 邮件免配置、企微、钉钉、飞书、anpush、server酱等 |
| 站点监控 | 限制1条 | 无限制 |
| 批量操作 | 无 | 流水线模版,流水线复制,批量运行,批量设置通知、定时等 |
| VIP群 | 无 | 可加,一对一技术支持,必要时可申请远程协助 |
专业版、商业版特权对比
| 功能&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 免费版 | 专业版 | 商业版 |
|---------|---------------------------------------|--------------------------------|---------------------------------|
| 证书申请 | 无限制 | 无限制 | 无限制 |
| 证书域名数量 | 无限制 | 无限制 | 无限制 |
| 证书流水线条数 | 无限制 | 无限制 | 无限制 |
| 自动部署插件 | 阿里云CDN、腾讯云、七牛CDN、主机部署、宝塔、1Panel等大部分插件 | 群晖、威联通、proxmox等 | 同专业版 |
| 通知 | 邮件通知、自定义webhook | 邮件免配置、企微、钉钉、飞书、anpush、server酱等 | 同专业版 |
| 站点监控 | 限制1条 | 无限制 | 无限制 |
| 批量操作 | 无 | 流水线模版,流水线复制,批量运行,批量设置通知、定时等 | 同专业版 |
| VIP群 | 无 | 可加,一对一技术支持,必要时可申请远程协助 | 商业版技术支持 |
| 站点个性化 | 无 | 无 | 可自定义站点名称、Logo等,移除Certd元素,首页警告等 |
| 套餐功能 | 无 | 无 | 支持配置套餐供用户购买 |
| 数据统计 | 无 | 无 | 支持站点各类统计数据 |
| 插件管理 | 无 | 无 | 支持公共EAB设置,插件选项配置 |
| 是否可商用 | 不允许 | 不允许 | 可对外运营 |
## 九、贡献代码
+1 -1
View File
@@ -31,7 +31,7 @@ This project not only supports automated certificate application but also automa
* Multiple notification methods, including email, webhook, WeChat Work, DingTalk, Lark, and anpush.
* On-premises deployment, local data storage, simple and quick installation. Images are built by Github Actions, with a transparent process.
* Multiple security measures, including authorization encryption, site hiding, 2FA, and password brute-force protection.
* Supports multiple databases such as SQLite, PostgreSQL, and MySQL.
* Supports multiple databases such as SQLite, PostgreSQL, MySQL, and MariaDB.
* Open API support.
* Site certificate monitoring.
* Multi-user management.
+95
View File
@@ -0,0 +1,95 @@
version: '3.3' # 兼容旧版docker-compose
services:
certd:
# 镜像 # ↓↓↓↓↓ ---- 镜像版本号,建议改成固定版本号,例如:certd:1.29.0
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
# image: ghcr.io/certd/certd:latest # --------- 如果 报镜像not found,可以尝试其他镜像源
# image: greper/certd:latest
container_name: certd # 容器名
restart: unless-stopped # 自动重启
volumes:
# ↓↓↓↓↓ -------------------------------------------------------- 数据库以及证书存储路径,默认存在宿主机的/data/certd/目录下,【您需要定时备份此目录,以保障数据容灾】
- /data/certd:/app/data # 只要修改冒号前面的,冒号后面的/app/data切记切记不要动
#- /volume1/docker/certd:/app/data:delegated #群晖使用这个配置
# ↓↓↓↓↓ -------------------------------------------------------- 如果走时不准,考虑挂载localtime文件
#- /etc/localtime:/etc/localtime
#- /etc/timezone:/etc/timezone
ports: # 端口映射
# ↓↓↓↓ ---------------------------------------------------------- 如果端口有冲突,可以修改第一个7001为其他不冲突的端口号,第二个7001不要动
- "7001:7001"
# ↓↓↓↓ ---------------------------------------------------------- https端口,可以根据实际情况,是否暴露该端口
- "7002:7002"
#↓↓↓↓ -------------------------------------------------------------- 如果出现getaddrinfo EAI_AGAIN 或 getaddrinfo ENOTFOUND 错误,可以尝试设置dns
# dns:
# - 223.5.5.5 # 阿里云公共dns
# - 223.6.6.6
# # ↓↓↓↓ --------------------------------------------------------- 如果你服务器在腾讯云,可以用这个替换上面阿里云的公共dns
# - 119.29.29.29 # 腾讯云公共dns
# - 182.254.116.116
# # ↓↓↓↓ --------------------------------------------------------- 如果你服务器部署在国外,可以用这个替换上面阿里云的公共dns
# - 8.8.8.8 # 谷歌公共dns
# - 8.8.4.4
# extra_hosts:
# # ↓↓↓↓ -------------------------------------------------------- 这里可以配置自定义hosts,外网域名可以指向本地局域网ip地址
# - "localdomain.com:192.168.1.3"
# # ↓↓↓↓ ------------------------------------------------ 直接使用主机的网络,如果网络问题实在找不到原因,可以尝试打开此参数
# network_mode: host
labels:
com.centurylinklabs.watchtower.enable: "true"
# ↓↓↓↓ -------------------------------------------------------------- 启用ipv6网络,还需要把下面networks的注释放开
# networks:
# - ip6net
environment:
# ↓↓↓↓ ----------------------------------------------------- 使用上海东八时区
- TZ=Asia/Shanghai
# 设置环境变量即可自定义certd配置
# 配置项见: packages/ui/certd-server/src/config/config.default.ts
# 配置规则: certd_ + 配置项, 点号用_代替
# #↓↓↓↓ ----------------------------- 如果忘记管理员密码,可以设置为truedocker compose up -d 重建容器之后,管理员密码将改成123456,然后请及时修改回false
- certd_system_resetAdminPasswd=false
# ↓↓↓ 要使用ipv6,将此配置修改为::
- certd_koa_hostname=0.0.0.0
# 默认使用sqlite文件数据库,如果需要使用其他数据库,请设置以下环境变量
# 注意: 选定使用一种数据库之后,不支持更换数据库。
# 数据库迁移方法:1、使用新数据库重新部署一套,然后将旧数据同步过去,注意flyway_history表的数据不要同步
# #↓↓↓↓ ----------------------------- 使用postgresql数据库,需要提前创建数据库
# - certd_flyway_scriptDir=./db/migration-pg # 升级脚本目录
# - certd_typeorm_dataSource_default_type=postgres # 数据库类型
# - certd_typeorm_dataSource_default_host=localhost # 数据库地址
# - certd_typeorm_dataSource_default_port=5433 # 数据库端口
# - certd_typeorm_dataSource_default_username=postgres # 用户名
# - certd_typeorm_dataSource_default_password=yourpasswd # 密码
# - certd_typeorm_dataSource_default_database=certd # 数据库名
# #↓↓↓↓ ----------------------------- 使用mysql8数据库,需要提前创建数据库 charset=utf8mb4, collation=utf8mb4_bin
# - certd_flyway_scriptDir=./db/migration-mysql # 升级脚本目录
# - certd_typeorm_dataSource_default_type=mysql # 数据库类型, 或者 mariadb
# - certd_typeorm_dataSource_default_host=localhost # 数据库地址
# - certd_typeorm_dataSource_default_port=3306 # 数据库端口
# - certd_typeorm_dataSource_default_username=root # 用户名
# - certd_typeorm_dataSource_default_password=yourpasswd # 密码
# - certd_typeorm_dataSource_default_database=certd # 数据库名
# ↓↓↓↓ --------------------------------------------------------- 自动升级,上面certd的版本号要保持为latest
certd-updater: # 添加 Watchtower 服务
image: containrrr/watchtower:latest
container_name: certd-updater
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# 配置 自动更新
environment:
- WATCHTOWER_CLEANUP=true # 自动清理旧版本容器
- WATCHTOWER_INCLUDE_STOPPED=false # 不更新已停止的容器
- WATCHTOWER_LABEL_ENABLE=true # 根据容器标签进行更新
- WATCHTOWER_POLL_INTERVAL=600 # 每 10 分钟检查一次更新
# ↓↓↓↓ -------------------------------------------------------------- 启用ipv6网络,还需要把上面networks的注释放开
#networks:
# ip6net:
# enable_ipv6: true
# ipam:
# config:
# - subnet: 2001:db8::/64
+1
View File
@@ -119,6 +119,7 @@ export default defineConfig({
{text: "Certd本身的证书更新", link: "/guide/use/https/index.md"},
{text: "js脚本插件使用", link: "/guide/use/custom-script/index.md"},
{text: "邮箱配置", link: "/guide/use/email/index.md"},
{text: "证书复用", link: "/guide/use/pretask/"},
{text: "IPv6支持", link: "/guide/use/setting/ipv6.md"},
{text: "ESXi", link: "/guide/use/ESXi/index.md"},
{text: "宝塔动态IP白名单", link: "/guide/use/baota/white_list.md"},
+95
View File
@@ -3,6 +3,101 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.40.3](https://github.com/certd/certd/compare/v1.40.2...v1.40.3) (2026-05-21)
### Bug Fixes
* 修复暗黑模式下注册页面验证码看不清的问题 ([5ba33be](https://github.com/certd/certd/commit/5ba33be30f765f06cafbfcc04f5e25320db01449))
### Performance Improvements
* 修复商业版套餐添加和修改时的字段显示 ([fb5b00d](https://github.com/certd/certd/commit/fb5b00d73f925036a65ce5003c57c1199578c34d))
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
### Bug Fixes
* **certd-server:** 调整首页缓存控制头的判断逻辑 ([0499347](https://github.com/certd/certd/commit/0499347588ee544862420ab9a5afd2546d61bc6c))
### Performance Improvements
* **controller:** 更换版本获取源并添加版本标准化处理 ([cb08e06](https://github.com/certd/certd/commit/cb08e061d257ba23a0fefdbfb046a8c759def828))
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
### Bug Fixes
* 固化华为云sdk版本,避免华为云调用报错 ([59b9ffa](https://github.com/certd/certd/commit/59b9ffadd05faf3982151c48f8d83cbd97419865))
* **email-service:** 优化商业版测试邮件内容中带的url链接 ([667e7b1](https://github.com/certd/certd/commit/667e7b185bf26558972be01720872f48352b5876))
### Performance Improvements
* 商业版支持限制泛域名数量 ([c63745d](https://github.com/certd/certd/commit/c63745d1ba30904428ba6b13ab0785298baa5cae))
* **suite:** 商业版用户已购套餐支持修改 ([bdb3d09](https://github.com/certd/certd/commit/bdb3d09c09fc73e7e5e3401f6ef5588bf8ad5088))
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
### Bug Fixes
* 修复第三方登录丢失state时无法在用户信息页面绑定第三方账号的bug ([45dedf5](https://github.com/certd/certd/commit/45dedf5bc779fea852e1f33dda4f31db2765633c))
* 修复群晖授权没有显示设备id输入框的bug ([2f172b5](https://github.com/certd/certd/commit/2f172b56e9411303ca15138d827bdb9bafdae4d1))
* 修复自动注册后没有跳转到控制台的bug ([4681ec9](https://github.com/certd/certd/commit/4681ec90088a3eb665427b2ac4047ec5ccefd7b3))
* 修复clogin登录丢失state问题 ([22f5cfc](https://github.com/certd/certd/commit/22f5cfcfd8462ca74128329eefb3f48b3ee0b7ea))
* 修复clogin多选类型登录失败的bug ([9f878a3](https://github.com/certd/certd/commit/9f878a353cd49b7b10bb0a95610ad236bc920dd2))
### Features
* 彩虹登录支持选择多种登录方式 ([7aa0c7e](https://github.com/certd/certd/commit/7aa0c7e491fe660abb62e68792ff5474f19bd5b8))
### Performance Improvements
* 第三方登录自动注册的用户支持设置初始化密码 ([a815d02](https://github.com/certd/certd/commit/a815d0245b97efbb948b33d6fc9d49862ce06889))
* 头像增加缓存时间 ([7015b1b](https://github.com/certd/certd/commit/7015b1b232602e5168a3eb8bee6d7f1776ae1e74))
## [1.39.16](https://github.com/certd/certd/compare/v1.39.15...v1.39.16) (2026-05-13)
**Note:** Version bump only for package root
## [1.39.15](https://github.com/certd/certd/compare/v1.39.14...v1.39.15) (2026-05-13)
### Bug Fixes
* 修复第三方登录彩虹登录不上的bug ([bae4f8e](https://github.com/certd/certd/commit/bae4f8e3209d9f9869ecbd7c01655383bac2fe21))
### Performance Improvements
* 优化申请时报错日志增加对应域名打印 ([d6e9e59](https://github.com/certd/certd/commit/d6e9e5987bd52ea12ee18745615486eadd4c87ff))
* icon选择器增加一套logo集 ([fdd5848](https://github.com/certd/certd/commit/fdd5848df4055a6ee07dc5eabaaf6b718672882d))
* **monitor/site:** 新增站点监控页面禁用启用、检查状态两个筛选条件 ([118c15d](https://github.com/certd/certd/commit/118c15d04633a6ef06f2d9e7a7849d20f596e02c))
* **network:** 新增全局公共http请求 headers设置 ([aad9045](https://github.com/certd/certd/commit/aad9045de55e76cb2ad09cac74a7bd60a4b47124))
## [1.39.14](https://github.com/certd/certd/compare/v1.39.13...v1.39.14) (2026-05-11)
### Bug Fixes
* 修复阿里云订阅流水线创建对话框无法获取阿里订单列表的bug ([a362860](https://github.com/certd/certd/commit/a362860137bfb7072893c844fe775edc46070ee1))
* 修复启动时报密钥备份不存在的问题 ([c966896](https://github.com/certd/certd/commit/c9668965226af6b54e0e576931dcba8b3d188ef3))
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
### Bug Fixes
* **aliyun-access:** 添加阿里云密钥校验失败的错误处理 ([b75c625](https://github.com/certd/certd/commit/b75c625ddcc0b3110699d8e6175681ef157b25df))
* cnameProvider域名支持设置子域名托管 ([7266af1](https://github.com/certd/certd/commit/7266af17491a98338022cfb18cfedfb93ca6ef8f))
* **plugin-aliyun:** 过滤非CAS证书并优化日志信息 ([c4b01da](https://github.com/certd/certd/commit/c4b01da384bc40a241a673ea8bc01ca733c04d83))
### Performance Improvements
* **设置:** 添加首页启用开关配置 ([25ad1e6](https://github.com/certd/certd/commit/25ad1e6f861e43288cc8bd90d4903628e6faec29))
* 新增agents.md ([aa176b0](https://github.com/certd/certd/commit/aa176b081a92837d2d6809d16546a8dfc2e5dd36))
* **用户资料:** 新增手机号邮箱绑定功能 ([e0eb0e2](https://github.com/certd/certd/commit/e0eb0e21f6dae24b639c944f9aba2c90496ab1c0))
* 域名注册过期时间获取再次优化 ([91a1b97](https://github.com/certd/certd/commit/91a1b9755066bf280e194dabf7c3a9f936e2643f))
* **证书流水线:** 添加批量更新证书申请参数功能 ([63be1c1](https://github.com/certd/certd/commit/63be1c1cbd9b09a3b48f26130c296b1cedcca1ac))
* 支持火山云vke ([bb46cb0](https://github.com/certd/certd/commit/bb46cb08f71f6ae921543f7e4a6c5f4e0190556e))
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
* **domain:** 添加域名过期时间同步进度显示功能 ([9d2937d](https://github.com/certd/certd/commit/9d2937dd4b14ffab73e9b096edd2aa8539811182))
* **plugin-volcengine:** 支持火山引擎VKE部署插件 ([b8a64a6](https://github.com/certd/certd/commit/b8a64a6b5bf3691a47177de42bc49b798e795feb))
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
### Bug Fixes
+16 -10
View File
@@ -16,16 +16,22 @@
****------------------****
## 专业版特权对比
| 功能&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 免费版 | 专业版 |
|---------|---------------------------------------|--------------------------------|
| 证书申请 | 无限制 | 无限制 |
| 证书域名数量 | 无限制 | 无限制 |
| 证书流水线条数 | 无限制 | 无限制 |
| 自动部署插件 | 阿里云CDN、腾讯云、七牛CDN、主机部署、宝塔、1Panel等大部分插件 | 群晖、威联通、proxmox等 |
| 通知 | 邮件通知、自定义webhook | 邮件免配置、企微、钉钉、飞书、anpush、server酱等 |
| 站点监控 | 限制1条 | 无限制 |
| 批量操作 | 无 | 流水线模版,流水线复制,批量运行,批量设置通知、定时等 |
| VIP群 | 无 | 可加,一对一技术支持,必要时可申请远程协助 |
| 功能&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; | 免费版 | 专业版 | 商业版 |
|---------|---------------------------------------|--------------------------------|---------------------------------|
| 证书申请 | 无限制 | 无限制 | 无限制 |
| 证书域名数量 | 无限制 | 无限制 | 无限制 |
| 证书流水线条数 | 无限制 | 无限制 | 无限制 |
| 自动部署插件 | 阿里云CDN、腾讯云、七牛CDN、主机部署、宝塔、1Panel等大部分插件 | 群晖、威联通、proxmox等 | 同专业版 |
| 通知 | 邮件通知、自定义webhook | 邮件免配置、企微、钉钉、飞书、anpush、server酱等 | 同专业版 |
| 站点监控 | 限制1条 | 无限制 | 无限制 |
| 批量操作 | 无 | 流水线模版,流水线复制,批量运行,批量设置通知、定时等 | 同专业版 |
| VIP群 | 无 | 可加,一对一技术支持,必要时可申请远程协助 | 商业版技术支持 |
| 站点个性化 | 无 | 无 | 可自定义站点名称、Logo等,移除Certd元素,首页警告等 |
| 套餐功能 | 无 | 无 | 支持配置套餐供用户购买 |
| 数据统计 | 无 | 无 | 支持站点各类统计数据 |
| 插件管理 | 无 | 无 | 支持公共EAB设置,插件选项配置 |
| 是否可商用 | 不允许 | 不允许 | 可对外运营 |
## 专业版激活方式
+60
View File
@@ -0,0 +1,60 @@
## 自动升级配置
### 1. 方法一:使用watchtower监控自动升级【推荐】
1. 修改docker-compose.yaml文件增加如下配置
或 [下载完整的自动升级docker-compose.yaml配置](https://gitee.com/certd/certd/raw/v2/docker/auto/docker-compose.yaml)
```yaml
services:
certd:
# 镜像 # ↓↓↓↓↓ ---- 镜像版本号 这里要保持为latest
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
... # 这里是你原来的docker-compose.yaml配置
# ↓↓↓↓ --------------------------------------------------------- 增加一个标签,表示certd需要自动升级
labels:
com.centurylinklabs.watchtower.enable: "true"
# ↓↓↓↓ --------------------------------------------------------- 自动升级watchtower配置,注意:上面certd的版本号要保持为latest
certd-updater: # 添加 Watchtower 服务
image: containrrr/watchtower:latest
container_name: certd-updater
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# 配置 自动更新
environment:
- WATCHTOWER_CLEANUP=true # 自动清理旧版本容器
- WATCHTOWER_INCLUDE_STOPPED=false # 不更新已停止的容器
- WATCHTOWER_LABEL_ENABLE=true # 根据容器标签进行更新
- WATCHTOWER_POLL_INTERVAL=600 # 每 10 分钟检查一次更新
```
2. 重启certd容器
```shell
cd certd
docker compose down
docker compose up -d
```
### 2. 方法二:使用Certd版本监控功能【不太稳定】
1. 选择Github-检查Release版本插件
![](./images/github-release.png)
按如下图填写配置
![](./images/github-release-2.png)
2. 检测到新版本后执行宿主机升级命令:
```shell
# 拉取最新镜像
docker pull registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
# 升级容器命令, 替换成你自己的certd更新命令
export RESTART_CERT='sleep 10; cd ~/deploy/certd/ ; docker compose down; docker compose up -d'
# 构造一个脚本10s后在后台执行,避免容器销毁时执行太快,导致流水线任务无法结束
nohup sh -c '$RESTART_CERT' >/dev/null 2>&1 & echo '10秒后重启' && exit
```
-48
View File
@@ -22,51 +22,3 @@
可以查看最新版本号,以及所有版本的更新日志
[CHANGELOG](../changelogs/CHANGELOG.md)
## 自动升级配置
### 1. 方法一:使用watchtower监控
修改docker-compose.yaml文件增加如下配置, 使用watchtower监控自动升级
```yaml
services:
certd:
...
labels:
com.centurylinklabs.watchtower.enable: "true"
# ↓↓↓↓ --------------------------------------------------------- 自动升级,上面certd的版本号要保持为latest
certd-updater: # 添加 Watchtower 服务
image: containrrr/watchtower:latest
container_name: certd-updater
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# 配置 自动更新
environment:
- WATCHTOWER_CLEANUP=true # 自动清理旧版本容器
- WATCHTOWER_INCLUDE_STOPPED=false # 不更新已停止的容器
- WATCHTOWER_LABEL_ENABLE=true # 根据容器标签进行更新
- WATCHTOWER_POLL_INTERVAL=600 # 每 10 分钟检查一次更新
```
### 2. 方法二:使用Certd版本监控功能
选择Github-检查Release版本插件
![](./images/github-release.png)
按如下图填写配置
![](./images/github-release-2.png)
检测到新版本后执行宿主机升级命令:
```shell
# 拉取最新镜像
docker pull registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
# 升级容器命令, 替换成你自己的certd更新命令
export RESTART_CERT='sleep 10; cd ~/deploy/certd/ ; docker compose down; docker compose up -d'
# 构造一个脚本10s后在后台执行,避免容器销毁时执行太快,导致流水线任务无法结束
nohup sh -c '$RESTART_CERT' >/dev/null 2>&1 & echo '10秒后重启' && exit
```
+3 -2
View File
@@ -134,8 +134,9 @@
| 4.| **火山引擎-部署证书至DCDN** | 部署至火山引擎全站加速 |
| 5.| **火山引擎-部署证书至Live** | 部署至火山引擎视频直播 |
| 6.| **火山引擎-部署证书至TOS自定义域名** | 仅限TOS自定义域名,加速域名请选择火山引擎的CDN插件 |
| 7.| **火山引擎-部署证书至VOD** | 部署至火山引擎视频点播 |
| 8.| **火山引擎-上传证书至证书中心** | 上传证书至火山引擎证书中心 |
| 7.| **火山引擎-替换VKE证书** | 替换火山引擎VKE集群中的TLS Secret证书 |
| 8.| **火山引擎-部署证书至VOD** | 部署至火山引擎视频点播 |
| 9.| **火山引擎-上传证书至证书中心** | 上传证书至火山引擎证书中心 |
## 9. 京东云
| 序号 | 名称 | 说明 |
+4 -1
View File
@@ -59,4 +59,7 @@ DNS problem: NXDOMAIN looking up TXT for _acme-challenge.xxxxx - check that a D
证书颁发机构向域名ns查询TXT验证记录失败,有以下几种可能
1、域名的ns服务器修改成别的了,但申请证书时的DNS提供商选择错误(检查确认,配置正确的DNS提供商)
2、证书颁发机构与ns域名服务器之间访问不通,无法查询到TXT记录(尝试更换证书颁发机构)
3、ns服务商解析值生效慢(尝试修改证书申请任务里面的等待生效时长600-1000s)
3、ns服务商解析值生效慢(尝试修改证书申请任务里面的等待生效时长600-1000s)
## 8. 同一份证书上传多次的问题
同一份证书在阿里云、腾讯云中上传多次,[请使用证书复用功能](../use/pretask/index.md),避免重复上传。
+3 -3
View File
@@ -1,13 +1,13 @@
# 带输出的前置任务
# 证书复用
前置任务输出可以在后续任务中使用
比如上传证书到阿里云,会返回阿里云的CertId,之后其他阿里云的部署任务可以选择复用这个证书
## 复用证书
## 使用方法
![img.png](images/pretask1.png)
在后续任务中可以选择前置任务的输出
![img.png](images/pretask2.png)
![img.png](images/pretask2.png)
+1 -1
View File
@@ -9,5 +9,5 @@
}
},
"npmClient": "pnpm",
"version": "1.39.12"
"version": "1.40.3"
}
+6 -3
View File
@@ -9,6 +9,7 @@
"@lerna-lite/run": "^3.9.3",
"@lerna-lite/version": "^3.9.3",
"axios": "^1.9.0",
"cross-env": "^7.0.3",
"medium-zoom": "^1.1.0",
"vitepress": "^2.0.0-alpha.4",
"vitepress-plugin-lightbox": "^1.0.2"
@@ -19,14 +20,15 @@
"devb": "lerna run dev-build",
"i-all": "lerna link && lerna exec npm install ",
"publish": "pnpm run prepublishOnly2 && lerna publish --force-publish=pro/plus-core --conventional-commits && pnpm run afterpublishOnly ",
"publish2":" npm run pub_all && pnpm run afterpublishOnly",
"afterpublishOnly": "pnpm run copylogs && time /t >trigger/build.trigger && git add ./trigger/build.trigger && git commit -m \"build: trigger build image\" && TIMEOUT /T 10 && pnpm run commitAll",
"transform-sql": "cd ./packages/ui/certd-server/db/ && node --experimental-json-modules transform.js",
"plugin-doc-gen": "cd ./packages/ui/certd-server/ && pnpm run export-metadata",
"commitAll": "git add . && git commit -m \"build: publish\" && git push && pnpm run commitPro",
"commitPro": "cd ./packages/pro/ && git add . && git commit -m \"build: publish\" && git push",
"copylogs": "copyfiles \"CHANGELOG.md\" ./docs/guide/changelogs/",
"prepublishOnly1": "pnpm run check && lerna run build ",
"prepublishOnly2": "pnpm run check && pnpm run before-build && lerna run build && pnpm run plugin-doc-gen",
"prepublishOnly1": "pnpm run test:unit && pnpm run check && lerna run build ",
"prepublishOnly2": "pnpm run test:unit && pnpm run check && pnpm run before-build && lerna run build && pnpm run plugin-doc-gen",
"before-build": "pnpm run transform-sql && cd ./packages/core/basic && time /t >build.md && git add ./build.md && git commit -m \"build: prepare to build\"",
"deploy1": "node --experimental-json-modules ./scripts/deploy.js ",
"check": "node --experimental-json-modules ./scripts/publish-check.js",
@@ -35,9 +37,10 @@
"docs:dev": "vitepress dev docs",
"docs:build": "pnpm run copylogs && vitepress build docs",
"docs:preview": "vitepress preview docs",
"test:unit": "cross-env NODE_ENV=unittest pnpm -r --workspace-concurrency=1 run test:unit",
"pub": "echo 1",
"dev": "pnpm run -r --parallel compile ",
"pub_all":"pnpm run -r --parallel pub ",
"pub_all": "node ./scripts/pub-all.js",
"release": "time /t >trigger/release.trigger && git add trigger/release.trigger && git commit -m \"build: release\" && git push",
"publish_to_atomgit": "node --experimental-json-modules ./scripts/publish-atomgit.js",
"publish_to_gitee": "node --experimental-json-modules ./scripts/publish-gitee.js",
+12
View File
@@ -1,4 +1,13 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2022,
"sourceType": "module"
},
"ignorePatterns": [
"dist",
"node_modules"
],
"extends": [
"plugin:prettier/recommended",
"prettier"
@@ -7,9 +16,12 @@
"eslint-plugin-import"
],
"env": {
"node": true,
"es2022": true,
"mocha": true
},
"rules": {
"prettier/prettier": "off",
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/ban-ts-ignore": "off",
+5
View File
@@ -4,3 +4,8 @@ node_modules/
npm-debug.log
package-lock.json
/.idea/
/dist/
/dist-test/
/logs/
/tsconfig.tsbuildinfo
/tsconfig.test.tsbuildinfo
+36
View File
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.40.3](https://github.com/publishlab/node-acme-client/compare/v1.40.2...v1.40.3) (2026-05-21)
**Note:** Version bump only for package @certd/acme-client
## [1.40.2](https://github.com/publishlab/node-acme-client/compare/v1.40.1...v1.40.2) (2026-05-19)
**Note:** Version bump only for package @certd/acme-client
## [1.40.1](https://github.com/publishlab/node-acme-client/compare/v1.40.0...v1.40.1) (2026-05-18)
**Note:** Version bump only for package @certd/acme-client
# [1.40.0](https://github.com/publishlab/node-acme-client/compare/v1.39.16...v1.40.0) (2026-05-14)
**Note:** Version bump only for package @certd/acme-client
## [1.39.16](https://github.com/publishlab/node-acme-client/compare/v1.39.15...v1.39.16) (2026-05-13)
**Note:** Version bump only for package @certd/acme-client
## [1.39.15](https://github.com/publishlab/node-acme-client/compare/v1.39.14...v1.39.15) (2026-05-13)
### Performance Improvements
* 优化申请时报错日志增加对应域名打印 ([d6e9e59](https://github.com/publishlab/node-acme-client/commit/d6e9e5987bd52ea12ee18745615486eadd4c87ff))
## [1.39.14](https://github.com/publishlab/node-acme-client/compare/v1.39.13...v1.39.14) (2026-05-11)
**Note:** Version bump only for package @certd/acme-client
## [1.39.13](https://github.com/publishlab/node-acme-client/compare/v1.39.12...v1.39.13) (2026-05-10)
### Performance Improvements
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/publishlab/node-acme-client/commit/4755216505ad18555a50da9d8008c2207c48df86))
## [1.39.12](https://github.com/publishlab/node-acme-client/compare/v1.39.11...v1.39.12) (2026-04-29)
### Performance Improvements
+18 -12
View File
@@ -3,22 +3,22 @@
"description": "Simple and unopinionated ACME client",
"private": false,
"author": "nmorsman",
"version": "1.39.12",
"version": "1.40.3",
"type": "module",
"module": "scr/index.js",
"main": "src/index.js",
"types": "types/index.d.ts",
"module": "./dist/index.js",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"license": "MIT",
"homepage": "https://github.com/publishlab/node-acme-client",
"engines": {
"node": ">= 18"
},
"files": [
"src",
"dist",
"types"
],
"dependencies": {
"@certd/basic": "^1.39.12",
"@certd/basic": "^1.40.3",
"@peculiar/x509": "^1.11.0",
"asn1js": "^3.0.5",
"axios": "^1.9.0",
@@ -35,10 +35,12 @@
"@typescript-eslint/parser": "^8.26.1",
"chai": "^4.4.1",
"chai-as-promised": "^7.1.2",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.29.1",
"eslint-plugin-prettier": "^4.2.1",
"esmock": "^2.7.5",
"jsdoc-to-markdown": "^8.0.1",
"mocha": "^10.6.0",
"nock": "^13.5.4",
@@ -47,13 +49,17 @@
"typescript": "^5.4.2"
},
"scripts": {
"build-docs": "jsdoc2md src/client.js > docs/client.md && jsdoc2md src/crypto/index.js > docs/crypto.md && jsdoc2md src/crypto/forge.js > docs/forge.md",
"lint": "eslint .",
"lint-types": "tsd",
"prepublishOnly": "npm run build-docs",
"before-build": "node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true});fs.rmSync('tsconfig.tsbuildinfo',{force:true});\"",
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
"build-docs": "jsdoc2md dist/client.js > docs/client.md && jsdoc2md dist/crypto/index.js > docs/crypto.md && jsdoc2md dist/crypto/forge.js > docs/forge.md",
"lint": "eslint \"src/**/*.ts\" \"types/**/*.ts\"",
"lint-types": "tsd --files \"types/index.test-d.ts\"",
"prepublishOnly": "npm run build && npm run build-docs",
"test": "mocha -t 60000 \"test/setup.js\" \"test/**/*.spec.js\"",
"before-test:unit": "node -e \"const fs=require('fs');fs.rmSync('dist-test',{recursive:true,force:true});fs.rmSync('tsconfig.test.tsbuildinfo',{force:true});\"",
"test:unit": "cross-env NODE_ENV=unittest npm run before-test:unit && cross-env NODE_ENV=unittest tsc -p tsconfig.test.json --skipLibCheck && cross-env NODE_ENV=unittest mocha -t 60000 \"dist-test/**/*.test.js\"",
"pub": "npm publish",
"compile": "echo '1'"
"compile": "tsc --skipLibCheck --watch"
},
"repository": {
"type": "git",
@@ -70,5 +76,5 @@
"bugs": {
"url": "https://github.com/publishlab/node-acme-client/issues"
},
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
"gitHead": "01c91ba294f88bd07fddf9358c4301bbb4027916"
}
@@ -1,3 +1,4 @@
// @ts-nocheck
/**
* ACME API client
*/
@@ -1,3 +1,4 @@
// @ts-nocheck
/**
* ACME auto helper
*/
@@ -166,6 +167,7 @@ export default async (client, userOpts) => {
await client.completeChallenge(challenge);
}catch (e) {
await deactivateAuth(e);
e.message = `[${d}] ${e.message || "completeChallenge error"}`;
throw e;
}
challengeCompleted = true;
@@ -177,6 +179,7 @@ export default async (client, userOpts) => {
} catch (e) {
log(`[auto] [${d}] challengeCreateFn threw error: ${e.message || e}`);
await deactivateAuth(e);
e.message = `[${d}] ${e.message || "challengeCreateFn error"}`;
throw e;
}
@@ -1,3 +1,4 @@
// @ts-nocheck
/**
* Axios instance
*/
@@ -1,3 +1,4 @@
// @ts-nocheck
/**
* ACME client
*
@@ -466,6 +467,10 @@ class AcmeClient {
return createHash('sha256').update(result).digest('base64url');
}
if (challenge.type === 'dns-persist-01') {
return '';
}
/* https://datatracker.ietf.org/doc/html/rfc8737 */
if (challenge.type === 'tls-alpn-01') {
return result;
@@ -570,7 +575,7 @@ class AcmeClient {
* ```
*/
async waitForValidStatus(item,d) {
async waitForValidStatus(item, d?) {
if (!item.url) {
throw new Error(`[${d}] Unable to verify status of item, URL not found`);
}
@@ -1,3 +1,4 @@
// @ts-nocheck
/**
* Legacy node-forge crypto interface
*
@@ -112,7 +113,7 @@ function parseDomains(obj) {
* ```
*/
export async function createPrivateKey(size = 2048) {
export async function createPrivateKey(size = 2048): Promise<Buffer> {
const keyPair = await generateKeyPair({ bits: size });
const pemKey = forge.pki.privateKeyToPem(keyPair.privateKey);
return Buffer.from(pemKey);
@@ -131,7 +132,7 @@ export async function createPrivateKey(size = 2048) {
* ```
*/
export const createPublicKey = async (key) => {
export const createPublicKey = async (key): Promise<Buffer> => {
const privateKey = forge.pki.privateKeyFromPem(key);
const publicKey = forge.pki.rsa.setPublicKey(privateKey.n, privateKey.e);
const pemKey = forge.pki.publicKeyToPem(publicKey);
@@ -174,7 +175,7 @@ export const splitPemChain = (str) => forge.pem.decode(str).map(forge.pem.encode
* ```
*/
export const getModulus = async (input) => {
export const getModulus = async (input): Promise<Buffer> => {
if (!Buffer.isBuffer(input)) {
input = Buffer.from(input);
}
@@ -197,7 +198,7 @@ export const getModulus = async (input) => {
* ```
*/
export const getPublicExponent = async (input) => {
export const getPublicExponent = async (input): Promise<Buffer> => {
if (!Buffer.isBuffer(input)) {
input = Buffer.from(input);
}
@@ -1,3 +1,4 @@
// @ts-nocheck
/**
* Native Node.js crypto interface
*
@@ -67,7 +68,7 @@ function getKeyInfo(keyPem) {
* ```
*/
export async function createPrivateRsaKey(modulusLength = 2048, encodingType = 'pkcs8') {
export async function createPrivateRsaKey(modulusLength = 2048, encodingType = 'pkcs8'): Promise<Buffer> {
const pair = await generateKeyPair('rsa', {
modulusLength,
privateKeyEncoding: {
@@ -105,7 +106,7 @@ export const createPrivateKey = createPrivateRsaKey;
* ```
*/
export const createPrivateEcdsaKey = async (namedCurve = 'P-256', encodingType = 'pkcs8') => {
export const createPrivateEcdsaKey = async (namedCurve = 'P-256', encodingType = 'pkcs8'): Promise<Buffer> => {
const pair = await generateKeyPair('ec', {
namedCurve,
privateKeyEncoding: {
@@ -129,7 +130,7 @@ export const createPrivateEcdsaKey = async (namedCurve = 'P-256', encodingType =
* ```
*/
export const getPublicKey = (keyPem) => {
export const getPublicKey = (keyPem): Buffer => {
const info = getKeyInfo(keyPem);
const publicKey = info.publicKey.export({
@@ -1,3 +1,4 @@
// @ts-nocheck
export class CancelError extends Error {
constructor(message) {
super(message);
@@ -1,3 +1,4 @@
// @ts-nocheck
/**
* ACME HTTP client
*/
@@ -0,0 +1,19 @@
import assert from "node:assert/strict";
import { directory, getAllSslProviderDomains, getDirectoryUrl } from "./index.js";
declare const describe: any;
declare const it: any;
describe("directory helpers", () => {
it("selects the provider specific directory endpoint", () => {
assert.equal(getDirectoryUrl({ sslProvider: "sslcom", pkType: "ec" }), directory.sslcom.ec);
assert.equal(getDirectoryUrl({ sslProvider: "letsencrypt", pkType: "rsa" }), directory.letsencrypt.production);
});
it("includes configured provider domains", () => {
const domains = getAllSslProviderDomains();
assert.ok(domains.includes("acme.litessl.com"));
assert.ok(domains.includes("acme.ssl.com"));
});
});
@@ -1,8 +1,9 @@
// @ts-nocheck
/**
* acme-client
*/
import AcmeClinet from './client.js'
export const Client = AcmeClinet
export { default as Client } from './client.js'
export type * from './types.js'
/**
* Directory URLs
@@ -103,4 +104,4 @@ export * from './logger.js'
export * from './verify.js'
export * from './error.js'
export * from './util.js'
export * from './util.js'
@@ -1,3 +1,4 @@
// @ts-nocheck
/**
* ACME logger
*/
+127
View File
@@ -0,0 +1,127 @@
/**
* Account
*
* https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.2
* https://datatracker.ietf.org/doc/html/rfc8555#section-7.3
* https://datatracker.ietf.org/doc/html/rfc8555#section-7.3.2
*/
export interface Account {
status: "valid" | "deactivated" | "revoked";
orders: string;
contact?: string[];
termsOfServiceAgreed?: boolean;
externalAccountBinding?: object;
}
export interface AccountCreateRequest {
contact?: string[];
termsOfServiceAgreed?: boolean;
onlyReturnExisting?: boolean;
externalAccountBinding?: object;
}
export interface AccountUpdateRequest {
status?: string;
contact?: string[];
termsOfServiceAgreed?: boolean;
}
/**
* Order
*
* https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.3
* https://datatracker.ietf.org/doc/html/rfc8555#section-7.4
*/
export interface Order {
status: "pending" | "ready" | "processing" | "valid" | "invalid";
identifiers: Identifier[];
authorizations: string[];
finalize: string;
expires?: string;
notBefore?: string;
notAfter?: string;
error?: object;
certificate?: string;
}
export interface OrderCreateRequest {
identifiers: Identifier[];
notBefore?: string;
notAfter?: string;
}
/**
* Authorization
*
* https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.4
*/
export interface Authorization {
identifier: Identifier;
status: "pending" | "valid" | "invalid" | "deactivated" | "expired" | "revoked";
challenges: Challenge[];
expires?: string;
wildcard?: boolean;
}
export interface Identifier {
type: string;
value: string;
}
/**
* Challenge
*
* https://datatracker.ietf.org/doc/html/rfc8555#section-8
* https://datatracker.ietf.org/doc/html/rfc8555#section-8.3
* https://datatracker.ietf.org/doc/html/rfc8555#section-8.4
*/
export interface ChallengeAbstract {
type: string;
url: string;
status: "pending" | "processing" | "valid" | "invalid";
validated?: string;
error?: object;
}
export interface HttpChallenge extends ChallengeAbstract {
type: "http-01";
token: string;
}
export interface DnsChallenge extends ChallengeAbstract {
type: "dns-01";
token: string;
}
export interface DnsPersistChallenge extends ChallengeAbstract {
type: "dns-persist-01";
}
export type Challenge = HttpChallenge | DnsChallenge | DnsPersistChallenge;
/**
* Certificate
*
* https://datatracker.ietf.org/doc/html/rfc8555#section-7.6
*/
export enum CertificateRevocationReason {
Unspecified = 0,
KeyCompromise = 1,
CACompromise = 2,
AffiliationChanged = 3,
Superseded = 4,
CessationOfOperation = 5,
CertificateHold = 6,
RemoveFromCRL = 8,
PrivilegeWithdrawn = 9,
AACompromise = 10,
}
export interface CertificateRevocationRequest {
reason?: CertificateRevocationReason;
}
+131
View File
@@ -0,0 +1,131 @@
import type * as rfc8555 from "./rfc8555.js";
import type { Challenge } from "./rfc8555.js";
export type * from "./rfc8555.js";
export type PrivateKeyBuffer = Buffer;
export type PublicKeyBuffer = Buffer;
export type CertificateBuffer = Buffer;
export type CsrBuffer = Buffer;
export type PrivateKeyString = string;
export type PublicKeyString = string;
export type CertificateString = string;
export type CsrString = string;
export interface Order extends rfc8555.Order {
url: string;
}
export interface Authorization extends rfc8555.Authorization {
url: string;
}
export type UrlMapping = {
enabled: boolean;
mappings: Record<string, string>;
};
export interface ClientExternalAccountBindingOptions {
kid: string;
hmacKey: string;
}
export interface ClientOptions {
sslProvider: string;
directoryUrl: string;
accountKey: PrivateKeyBuffer | PrivateKeyString;
accountUrl?: string;
externalAccountBinding?: ClientExternalAccountBindingOptions;
backoffAttempts?: number;
backoffMin?: number;
backoffMax?: number;
urlMapping?: UrlMapping;
signal?: AbortSignal;
logger?: any;
}
export interface ClientAutoOptions {
csr: CsrBuffer | CsrString;
challengeCreateFn: (
authz: Authorization,
keyAuthorization: (challenge: Challenge) => Promise<string>
) => Promise<{ recordReq?: any; recordRes?: any; dnsProvider?: any; challenge: Challenge; keyAuthorization: string }>;
challengeRemoveFn: (authz: Authorization, challenge: Challenge, keyAuthorization: string, recordReq: any, recordRes: any, dnsProvider: any, httpUploader: any) => Promise<any>;
email?: string;
termsOfServiceAgreed?: boolean;
skipChallengeVerification?: boolean;
challengePriority?: string[];
preferredChain?: string;
signal?: AbortSignal;
profile?: string;
waitDnsDiffuseTime?: number;
}
export interface CertificateDomains {
commonName: string;
altNames: string[];
}
export interface CertificateIssuer {
commonName: string;
}
export interface CertificateInfo {
issuer: CertificateIssuer;
domains: CertificateDomains;
notAfter: Date;
notBefore: Date;
}
export interface CsrOptions {
keySize?: number;
commonName?: string;
altNames?: string[];
country?: string;
state?: string;
locality?: string;
organization?: string;
organizationUnit?: string;
emailAddress?: string;
}
export interface RsaPublicJwk {
e: string;
kty: string;
n: string;
}
export interface EcdsaPublicJwk {
crv: string;
kty: string;
x: string;
y: string;
}
export interface CryptoInterface {
createPrivateKey(keySize?: number, encodingType?: string): Promise<PrivateKeyBuffer>;
createPrivateRsaKey(keySize?: number, encodingType?: string): Promise<PrivateKeyBuffer>;
createPrivateEcdsaKey(namedCurve?: "P-256" | "P-384" | "P-521", encodingType?: string): Promise<PrivateKeyBuffer>;
getPublicKey(keyPem: PrivateKeyBuffer | PrivateKeyString | PublicKeyBuffer | PublicKeyString): PublicKeyBuffer;
getJwk(keyPem: PrivateKeyBuffer | PrivateKeyString | PublicKeyBuffer | PublicKeyString): RsaPublicJwk | EcdsaPublicJwk;
splitPemChain(chainPem: CertificateBuffer | CertificateString): string[];
getPemBodyAsB64u(pem: CertificateBuffer | CertificateString): string;
readCsrDomains(csrPem: CsrBuffer | CsrString): CertificateDomains;
readCertificateInfo(certPem: CertificateBuffer | CertificateString): CertificateInfo;
createCsr(data: CsrOptions, keyPem?: PrivateKeyBuffer | PrivateKeyString, encodingType?: string): Promise<[PrivateKeyBuffer, CsrBuffer]>;
createAlpnCertificate(authz: Authorization, keyAuthorization: string, keyPem?: PrivateKeyBuffer | PrivateKeyString): Promise<[PrivateKeyBuffer, CertificateBuffer]>;
isAlpnCertificateAuthorizationValid(certPem: CertificateBuffer | CertificateString, keyAuthorization: string): boolean;
}
export interface CryptoLegacyInterface {
createPrivateKey(size?: number): Promise<PrivateKeyBuffer>;
createPublicKey(key: PrivateKeyBuffer | PrivateKeyString): Promise<PublicKeyBuffer>;
getPemBody(str: string): string;
splitPemChain(str: string): string[];
getModulus(input: PrivateKeyBuffer | PrivateKeyString | PublicKeyBuffer | PublicKeyString | CertificateBuffer | CertificateString | CsrBuffer | CsrString): Promise<Buffer>;
getPublicExponent(input: PrivateKeyBuffer | PrivateKeyString | PublicKeyBuffer | PublicKeyString | CertificateBuffer | CertificateString | CsrBuffer | CsrString): Promise<Buffer>;
readCsrDomains(csr: CsrBuffer | CsrString): Promise<CertificateDomains>;
readCertificateInfo(cert: CertificateBuffer | CertificateString): Promise<CertificateInfo>;
createCsr(data: CsrOptions, key?: PrivateKeyBuffer | PrivateKeyString): Promise<[PrivateKeyBuffer, CsrBuffer]>;
}
@@ -0,0 +1,58 @@
import assert from "node:assert/strict";
import { formatResponseError, parseRetryAfterHeader, retry } from "./util.js";
declare const describe: any;
declare const it: any;
describe("util helpers", () => {
it("parses retry-after values", () => {
assert.equal(parseRetryAfterHeader("120"), 120);
assert.equal(parseRetryAfterHeader("invalid"), 0);
assert.equal(parseRetryAfterHeader("Wed, 21 Oct 2015 07:28:00 GMT"), 0);
});
it("formats response errors without newlines", () => {
const error = formatResponseError({
data: {
error: {
detail: "line 1\nline 2",
},
},
});
assert.equal(error, "line 1line 2");
});
it("retries until success", async () => {
const delays: number[] = [];
const originalSetTimeout = globalThis.setTimeout;
let attempts = 0;
(globalThis as any).setTimeout = (fn: (...args: any[]) => void, delay?: number) => {
delays.push(Number(delay));
return originalSetTimeout(fn, 0);
};
try {
const result = await retry(
async () => {
attempts += 1;
if (attempts < 3) {
throw new Error(`boom-${attempts}`);
}
return "ok";
},
{ attempts: 3, min: 10, max: 20 },
() => {}
);
assert.equal(result, "ok");
assert.equal(attempts, 3);
assert.deepEqual(delays, [10, 20]);
} finally {
(globalThis as any).setTimeout = originalSetTimeout;
}
});
});
@@ -1,3 +1,4 @@
// @ts-nocheck
/**
* Utility methods
*/
@@ -1,3 +1,4 @@
// @ts-nocheck
/**
* ACME challenge verification
*/
@@ -169,7 +170,7 @@ export function createChallengeFn(opts = {}) {
if (txtRecords.length === 0) {
throw new Error(`没有找到TXT解析记录(${recordName}`);
throw new Error(`没有找到TXT解析记录(${recordName},请稍后重试`);
}
return txtRecords;
}
@@ -202,6 +203,24 @@ export function createChallengeFn(opts = {}) {
return true;
}
async function verifyDnsPersistChallenge(authz, challenge, keyAuthorization, prefix = '_validation-persist.') {
const recordName = `${prefix}${authz.identifier.value.replace(/^\*\./, '')}`;
log(`本地校验DNS持久验证TXT记录: ${recordName}`);
let recordValues = await walkTxtRecord(recordName, 0, walkFromAuthoritative);
recordValues = [...new Set(recordValues)];
const expected = challenge.expectedRecordValue;
if (!expected) {
log(`未提供dns-persist-01本地校验期望值,跳过精确匹配,仅确认TXT记录存在`);
return true;
}
log(`DNS查询成功, 找到 ${recordValues.length} 条TXT记录:${recordValues}`);
if (!recordValues.length || !recordValues.includes(expected)) {
throw new Error(`没有找到需要的DNS持久验证TXT记录: ${recordName},请稍后重试,期望:${expected},结果:${recordValues}`);
}
log(`DNS持久验证记录匹配成功(${challenge.type}/${recordName}:${expected}`);
return true;
}
/**
* Verify ACME TLS ALPN challenge
*
@@ -233,6 +252,7 @@ export function createChallengeFn(opts = {}) {
challenges: {
'http-01': verifyHttpChallenge,
'dns-01': verifyDnsChallenge,
'dns-persist-01': verifyDnsPersistChallenge,
'tls-alpn-01': verifyTlsAlpnChallenge,
},
walkTxtRecord,
@@ -1,3 +1,4 @@
// @ts-nocheck
export async function wait(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"inlineSourceMap": false
}
}
+29
View File
@@ -0,0 +1,29 @@
{
"compileOnSave": true,
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"inlineSourceMap": false,
"sourceMap": true,
"noImplicitThis": false,
"noUnusedLocals": false,
"stripInternal": true,
"skipLibCheck": true,
"pretty": true,
"declaration": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"outDir": "dist",
"rootDir": "src",
"composite": false,
"useDefineForClassFields": false,
"strict": false,
"resolveJsonModule": true,
"isolatedModules": false,
"lib": ["ESNext", "DOM"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.json"],
"exclude": ["dist", "node_modules", "src/**/*.test.ts", "test"]
}
@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "dist-test",
"declaration": false,
"declarationMap": false,
"emitDeclarationOnly": false
},
"include": ["src/**/*.ts"],
"exclude": ["dist", "dist-test", "node_modules", "test"]
}
+15 -6
View File
@@ -4,8 +4,6 @@
import { AxiosInstance } from 'axios';
import * as rfc8555 from './rfc8555';
import {CancelError} from '../src/error.js'
export * from '../src/error.js'
export type PrivateKeyBuffer = Buffer;
export type PublicKeyBuffer = Buffer;
@@ -59,7 +57,7 @@ export interface ClientExternalAccountBindingOptions {
export interface ClientAutoOptions {
csr: CsrBuffer | CsrString;
challengeCreateFn: (authz: Authorization, keyAuthorization: (challenge:rfc8555.Challenge)=>Promise<string>) => Promise<{recordReq?:any,recordRes?:any,dnsProvider?:any,challenge: rfc8555.Challenge,keyAuthorization:string}>;
challengeCreateFn: (authz: Authorization, keyAuthorization: (challenge:rfc8555.Challenge)=>Promise<string>) => Promise<{recordReq?:any,recordRes?:any,dnsProvider?:any,challenge: rfc8555.Challenge,keyAuthorization:string,httpUploader?:any}>;
challengeRemoveFn: (authz: Authorization, challenge: rfc8555.Challenge, keyAuthorization: string,recordReq:any, recordRes:any,dnsProvider:any,httpUploader:any) => Promise<any>;
email?: string;
termsOfServiceAgreed?: boolean;
@@ -115,6 +113,15 @@ export const directory: {
zerossl: {
staging: string,
production: string
},
sslcom: {
staging: string,
production: string,
ec: string
},
litessl: {
staging: string,
production: string
}
};
@@ -211,14 +218,16 @@ export const agents: any;
* Logger
*/
export class CancelError extends Error {
constructor(message?: string);
}
export function setLogger(fn: (message: any, ...args: any[]) => void): void;
export function createChallengeFn(opts?: {logger?:any}): any;
// export function walkTxtRecord(record: any): Promise<string[]>;
export function getAuthoritativeDnsResolver(record:string): Promise<any>;
export const CancelError: typeof CancelError;
export function resolveDomainBySoaRecord(domain: string): Promise<string>;
export function setWalkFromAuthoritative(value = true): void;
export function setWalkFromAuthoritative(value?: boolean): void;
@@ -2,7 +2,7 @@
* acme-client type definition tests
*/
import * as acme from 'acme-client';
import * as acme from '..';
(async () => {
/* Client */
@@ -10,6 +10,7 @@ import * as acme from 'acme-client';
const client = new acme.Client({
accountKey,
sslProvider: 'letsencrypt',
directoryUrl: acme.directory.letsencrypt.staging
});
@@ -52,7 +53,10 @@ import * as acme from 'acme-client';
/* Auto */
await client.auto({
csr: certCsr,
challengeCreateFn: async (authz, challenge, keyAuthorization) => {},
challengeCreateFn: async (authz, keyAuthorization) => ({
challenge: authz.challenges[0],
keyAuthorization: await keyAuthorization(authz.challenges[0])
}),
challengeRemoveFn: async (authz, challenge, keyAuthorization) => {}
});
@@ -63,7 +67,10 @@ import * as acme from 'acme-client';
skipChallengeVerification: false,
challengePriority: ['http-01', 'dns-01'],
preferredChain: 'DST Root CA X3',
challengeCreateFn: async (authz, challenge, keyAuthorization) => {},
challengeCreateFn: async (authz, keyAuthorization) => ({
challenge: authz.challenges[0],
keyAuthorization: await keyAuthorization(authz.challenges[0])
}),
challengeRemoveFn: async (authz, challenge, keyAuthorization) => {}
});
})();
+5 -1
View File
@@ -97,7 +97,11 @@ export interface DnsChallenge extends ChallengeAbstract {
token: string;
}
export type Challenge = HttpChallenge | DnsChallenge;
export interface DnsPersistChallenge extends ChallengeAbstract {
type: 'dns-persist-01';
}
export type Challenge = HttpChallenge | DnsChallenge | DnsPersistChallenge;
/**
* Certificate
+36
View File
@@ -3,6 +3,42 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.40.3](https://github.com/certd/certd/compare/v1.40.2...v1.40.3) (2026-05-21)
**Note:** Version bump only for package @certd/basic
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
**Note:** Version bump only for package @certd/basic
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
**Note:** Version bump only for package @certd/basic
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
**Note:** Version bump only for package @certd/basic
## [1.39.16](https://github.com/certd/certd/compare/v1.39.15...v1.39.16) (2026-05-13)
**Note:** Version bump only for package @certd/basic
## [1.39.15](https://github.com/certd/certd/compare/v1.39.14...v1.39.15) (2026-05-13)
### Performance Improvements
* **network:** 新增全局公共http请求 headers设置 ([aad9045](https://github.com/certd/certd/commit/aad9045de55e76cb2ad09cac74a7bd60a4b47124))
## [1.39.14](https://github.com/certd/certd/compare/v1.39.13...v1.39.14) (2026-05-11)
**Note:** Version bump only for package @certd/basic
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
### Performance Improvements
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
### Performance Improvements
+1 -1
View File
@@ -1 +1 @@
23:06
22:57
+8 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/basic",
"private": false,
"version": "1.39.12",
"version": "1.40.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -9,10 +9,11 @@
"scripts": {
"dev": "vite",
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
"build": "npm run before-build && tsc --skipLibCheck",
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
"dev-build": "npm run build",
"preview": "vite preview",
"test": "mocha --loader=ts-node/esm",
"test:unit": "cross-env NODE_ENV=unittest mocha --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
"pub": "npm publish",
"compile": "tsc --skipLibCheck --watch"
},
@@ -39,13 +40,17 @@
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"chai": "4.3.10",
"cross-env": "^7.0.3",
"eslint": "^8.41.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"esmock": "^2.7.5",
"mocha": "^10.2.0",
"prettier": "^2.8.8",
"rimraf": "^5.0.5",
"ts-node": "^10.9.2",
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
"gitHead": "01c91ba294f88bd07fddf9358c4301bbb4027916"
}
@@ -0,0 +1,31 @@
/// <reference types="mocha" />
import { expect } from "chai";
import { amountUtils } from "./util.amount.js";
describe("amountUtils", () => {
describe("toCent", () => {
it("converts yuan values to cents", () => {
expect(amountUtils.toCent(1)).to.equal(100);
expect(amountUtils.toCent(12.34)).to.equal(1234);
});
it("rounds to the nearest cent", () => {
expect(amountUtils.toCent(1.235)).to.equal(124);
expect(amountUtils.toCent(1.234)).to.equal(123);
});
});
describe("toYuan", () => {
it("converts cent values to yuan", () => {
expect(amountUtils.toYuan(100)).to.equal(1);
expect(amountUtils.toYuan(1234)).to.equal(12.34);
});
it("rounds yuan values to two decimal places", () => {
expect(amountUtils.toYuan(1235)).to.equal(12.35);
expect(amountUtils.toYuan(1)).to.equal(0.01);
});
});
});
+2 -1
View File
@@ -11,9 +11,10 @@ export class LocalCache<V = any> {
cache: Map<string, { value: V; expiresAt: number }>;
constructor(opts: { clearInterval?: number } = {}) {
this.cache = new Map();
setInterval(() => {
const intervalId = setInterval(() => {
this.clearExpires();
}, opts.clearInterval ?? 5 * 60 * 1000);
intervalId.unref?.();
}
get(key: string): V | undefined {
@@ -0,0 +1,43 @@
/// <reference types="mocha" />
import { expect } from "chai";
import { domainUtils } from "./util.domain.js";
describe("domainUtils", () => {
describe("match", () => {
it("matches exact domains", () => {
expect(domainUtils.match("example.com", ["example.com"])).to.equal(true);
expect(domainUtils.match("api.example.com", ["example.com"])).to.equal(false);
});
it("matches wildcard domains by suffix", () => {
expect(domainUtils.match("api.example.com", ["*.example.com"])).to.equal(true);
expect(domainUtils.match("deep.api.example.com", ["*.example.com"])).to.equal(false);
expect(domainUtils.match("example.com", ["*.example.com"])).to.equal(false);
});
it("requires every target domain to match", () => {
expect(domainUtils.match(["api.example.com", "admin.example.com"], ["*.example.com"])).to.equal(true);
expect(domainUtils.match(["api.example.com", "other.com"], ["*.example.com"])).to.equal(false);
});
});
describe("isIp", () => {
it("detects valid IPv4 addresses", () => {
expect(domainUtils.isIpv4("127.0.0.1")).to.equal(true);
expect(domainUtils.isIpv4("255.255.255.255")).to.equal(true);
});
it("rejects invalid IPv4 addresses", () => {
expect(domainUtils.isIpv4("999.1.1.1")).to.equal(false);
expect(domainUtils.isIpv4("1.2.3")).to.equal(false);
expect(domainUtils.isIpv4("example.com")).to.equal(false);
});
it("detects IPv6 addresses", () => {
expect(domainUtils.isIpv6("2001:db8::1")).to.equal(true);
expect(domainUtils.isIp("2001:db8::1")).to.equal(true);
});
});
});
+4 -1
View File
@@ -51,7 +51,10 @@ function isIpv4(d: string) {
return false;
}
const isIPv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
return isIPv4Regex.test(d);
if (!isIPv4Regex.test(d)) {
return false;
}
return d.split(".").every(item => Number(item) <= 255);
}
function isIpv6(d: string) {
@@ -0,0 +1,42 @@
/// <reference types="mocha" />
import { expect } from "chai";
import { isDev } from "./util.env.js";
describe("isDev", () => {
const originalNodeEnv = process.env.NODE_ENV;
afterEach(() => {
if (originalNodeEnv == null) {
delete process.env.NODE_ENV;
} else {
process.env.NODE_ENV = originalNodeEnv;
}
});
it("treats missing NODE_ENV as development", () => {
delete process.env.NODE_ENV;
expect(isDev()).to.equal(true);
});
it("detects development-like NODE_ENV values", () => {
process.env.NODE_ENV = "development";
expect(isDev()).to.equal(true);
process.env.NODE_ENV = "local";
expect(isDev()).to.equal(true);
process.env.NODE_ENV = "dev-test";
expect(isDev()).to.equal(true);
});
it("rejects production-like NODE_ENV values", () => {
process.env.NODE_ENV = "production";
expect(isDev()).to.equal(false);
process.env.NODE_ENV = "test";
expect(isDev()).to.equal(false);
});
});
@@ -0,0 +1,45 @@
/// <reference types="mocha" />
import { expect } from "chai";
import { hashUtils } from "./util.hash.js";
describe("hashUtils", () => {
describe("digest helpers", () => {
it("generates md5 and sha256 hex digests by default", () => {
expect(hashUtils.md5("certd")).to.equal("3f3d9f715fcc63d54a4a224e0939a233");
expect(hashUtils.sha256("certd")).to.equal("26a6366060d2a6477185c05075155769cb438c6c71f61f509535b8516594ad92");
});
it("supports alternate digest encodings", () => {
expect(hashUtils.md5("certd", "base64")).to.equal("Pz2fcV/MY9VKSiJOCTmiMw==");
expect(hashUtils.sha256("certd", "base64")).to.equal("JqY2YGDSpkdxhcBQdRVXactDjGxx9h9QlTW4UWWUrZI=");
});
});
describe("hmac helpers", () => {
it("signs data with a provided key", () => {
expect(hashUtils.hmacSha256WithKey("secret", "certd")).to.equal("kh/kUD/Ji8FHfpt4vYUHZx+1BZvKSyyklZIiuS+Rzlg=");
});
it("uses an empty payload when only the key is provided", () => {
expect(hashUtils.hmacSha256("secret")).to.equal("+eZuF5tnR65UEI+C+K3os8Jddv0wr95sOVgixTAZYWk=");
});
});
describe("encoding helpers", () => {
it("round trips base64 values", () => {
const encoded = hashUtils.base64("证书-certd");
expect(encoded).to.equal("6K+B5LmmLWNlcnRk");
expect(hashUtils.base64Decode(encoded)).to.equal("证书-certd");
});
it("converts strings and numbers to hex", () => {
expect(hashUtils.toHex("certd")).to.equal("6365727464");
expect(hashUtils.hexToStr("6365727464")).to.equal("certd");
expect(hashUtils.toHex(255)).to.equal("ff");
expect(hashUtils.hexToNumber("ff")).to.equal(255);
});
});
});
@@ -0,0 +1,18 @@
/// <reference types="mocha" />
import { expect } from "chai";
import { randomNumber, simpleNanoId } from "./util.id.js";
describe("id utils", () => {
it("generates a four digit random number string", () => {
expect(randomNumber()).to.match(/^\d{4}$/);
});
it("generates a twelve character simple nano id", () => {
const id = simpleNanoId();
expect(id).to.have.length(12);
expect(id).to.match(/^[0-9a-zA-Z]+$/);
});
});
@@ -0,0 +1,78 @@
/// <reference types="mocha" />
import { expect } from "chai";
import { mergeUtils, UnMergeable } from "./util.merge.js";
describe("mergeUtils", () => {
describe("merge", () => {
it("deep merges plain objects", () => {
const target = { acme: { email: "admin@example.com" }, deploy: { retries: 1 } };
const result = mergeUtils.merge(target, { acme: { dnsProvider: "cloudflare" } });
expect(result).to.equal(target);
expect(result).to.deep.equal({
acme: { email: "admin@example.com", dnsProvider: "cloudflare" },
deploy: { retries: 1 },
});
});
it("replaces arrays instead of merging them by index", () => {
const result = mergeUtils.merge({ domains: ["old.example.com", "legacy.example.com"] }, { domains: ["new.example.com"] });
expect(result).to.deep.equal({ domains: ["new.example.com"] });
});
it("allows null to clear nested values", () => {
const result = mergeUtils.merge({ cert: { name: "certd" } }, { cert: null });
expect(result).to.deep.equal({ cert: null });
});
it("keeps undefined sources from overwriting existing nested values", () => {
const result = mergeUtils.merge({ cert: { name: "certd" } }, { cert: undefined });
expect(result).to.deep.equal({ cert: { name: "certd" } });
});
it("returns an UnMergeable source directly when it is merged at the top level", () => {
const source = new UnMergeable();
const result = mergeUtils.merge({ enabled: true }, source);
expect(result).to.equal(source);
});
it("replaces nested values marked as UnMergeable", () => {
const source = new UnMergeable();
const result = mergeUtils.merge({ plugin: { enabled: true } }, { plugin: source });
expect(result.plugin).to.equal(source);
});
});
describe("cloneDeep", () => {
it("deep clones plain values", () => {
const source = { acme: { email: "admin@example.com" }, domains: ["example.com"] };
const result = mergeUtils.cloneDeep(source);
expect(result).to.deep.equal(source);
expect(result).not.to.equal(source);
expect(result.acme).not.to.equal(source.acme);
expect(result.domains).not.to.equal(source.domains);
});
it("preserves references marked as not cloneable", () => {
const uncloneable = new UnMergeable();
const source = { plugin: uncloneable };
const result = mergeUtils.cloneDeep(source);
expect(result).not.to.equal(source);
expect(result.plugin).to.equal(uncloneable);
});
});
});
@@ -0,0 +1,40 @@
/// <reference types="mocha" />
import { expect } from "chai";
import { optionsUtils } from "./util.options.js";
describe("optionsUtils", () => {
describe("groupByDomain", () => {
it("splits options by domain match", () => {
const matchedOption = { value: "matched", domain: "api.example.com" };
const wildcardMatchedOption = { value: "wildcard", domain: "admin.example.com" };
const unmatchedOption = { value: "unmatched", domain: "other.com" };
const result = optionsUtils.groupByDomain([matchedOption, wildcardMatchedOption, unmatchedOption], ["api.example.com", "*.example.com"]);
expect(result.matched).to.deep.equal([matchedOption, wildcardMatchedOption]);
expect(result.notMatched).to.deep.equal([unmatchedOption]);
});
it("treats options without matching domains as not matched", () => {
const optionWithoutDomain = { value: "empty" };
const result = optionsUtils.groupByDomain([optionWithoutDomain], ["example.com"]);
expect(result.matched).to.deep.equal([]);
expect(result.notMatched).to.deep.equal([optionWithoutDomain]);
});
});
describe("buildGroupOptions", () => {
it("builds disabled group labels around matched and unmatched options", () => {
const matchedOption = { value: "matched", domain: "api.example.com" };
const unmatchedOption = { value: "unmatched", domain: "other.com" };
const result = optionsUtils.buildGroupOptions([matchedOption, unmatchedOption], ["api.example.com"]);
expect(result).to.deep.equal([{ value: "matched", disabled: true, label: "----已匹配----" }, matchedOption, { value: "unmatched", disabled: true, label: "----未匹配----" }, unmatchedOption]);
});
});
});
@@ -0,0 +1,90 @@
/// <reference types="mocha" />
import { expect } from "chai";
import { logger } from "./util.log.js";
import { promises } from "./util.promise.js";
describe("promises", () => {
describe("TimeoutPromise", () => {
it("resolves when the callback finishes before the timeout", async () => {
let completed = false;
await promises.TimeoutPromise(async () => {
completed = true;
}, 50);
expect(completed).to.equal(true);
});
it("rejects when the callback exceeds the timeout", async () => {
try {
await promises.TimeoutPromise(() => new Promise<void>(resolve => setTimeout(resolve, 30)), 5);
expect.fail("expected TimeoutPromise to reject");
} catch (e: any) {
expect(e.message).to.equal("Task timeout in 5 ms");
}
});
});
describe("safePromise", () => {
it("resolves values provided by the callback", async () => {
const result = await promises.safePromise<string>(resolve => {
resolve("ok");
});
expect(result).to.equal("ok");
});
it("rejects synchronous errors thrown by the callback", async () => {
const oldLevel = logger.level;
logger.level = "fatal";
try {
await promises.safePromise(() => {
throw new Error("boom");
});
expect.fail("expected safePromise to reject");
} catch (e: any) {
expect(e.message).to.equal("boom");
} finally {
logger.level = oldLevel;
}
});
});
describe("promisify", () => {
it("resolves callback data", async () => {
const readValue = promises.promisify((prefix: string, callback: (err: Error | null, data?: string) => void) => {
callback(null, `${prefix}-value`);
});
expect(await readValue("certd")).to.equal("certd-value");
});
it("rejects callback errors", async () => {
const failing = promises.promisify((callback: (err: Error) => void) => {
callback(new Error("callback failed"));
});
try {
await failing();
expect.fail("expected promisified function to reject");
} catch (e: any) {
expect(e.message).to.equal("callback failed");
}
});
it("rejects synchronous errors", async () => {
const failing = promises.promisify(() => {
throw new Error("sync failed");
});
try {
await failing();
expect.fail("expected promisified function to reject");
} catch (e: any) {
expect(e.message).to.equal("sync failed");
}
});
});
});
@@ -0,0 +1,53 @@
import { expect } from "chai";
import { createAxiosService, HttpClient, setGlobalHeaders } from "./util.request.js";
import { ILogger } from "./util.log.js";
const testLogger = {
info() {},
error() {},
} as unknown as ILogger;
describe("util.request", () => {
afterEach(() => {
setGlobalHeaders({});
});
it("should merge global headers without overriding request headers", async () => {
setGlobalHeaders({
"X-Common": "common",
"X-Override": "global",
});
const http = createAxiosService({ logger: testLogger }) as HttpClient;
const res = await http.request({
url: "http://example.com",
method: "get",
logReq: false,
logRes: false,
headers: {
"X-Override": "request",
"X-Request": "request",
},
adapter: async config => {
const headers = config.headers;
return {
config,
data: {
common: headers.get("X-Common"),
override: headers.get("X-Override"),
request: headers.get("X-Request"),
},
headers: {},
status: 200,
statusText: "OK",
};
},
});
expect(res).to.deep.equal({
common: "common",
override: "request",
request: "request",
});
});
});
+18 -2
View File
@@ -26,7 +26,7 @@ export class HttpError extends Error {
return;
}
let message = error?.message || error?.response.statusText || error?.code;
let message = error?.message || error?.response?.statusText || error?.code;
if (message && typeof message === "string" && message.indexOf) {
for (const key in errorMap) {
if (message.indexOf(key) > -1) {
@@ -82,6 +82,7 @@ export class HttpError extends Error {
export const HttpCommonError = HttpError;
let defaultAgents = createAgent();
let defaultHeaders: Record<string, string> = {};
export function setGlobalProxy(opts: { httpProxy?: string; httpsProxy?: string }) {
logger.info("setGlobalProxy:", opts);
@@ -92,6 +93,15 @@ export function getGlobalAgents() {
return defaultAgents;
}
export function setGlobalHeaders(headers: Record<string, string> = {}) {
logger.info("setGlobalHeaders:", Object.keys(headers));
defaultHeaders = { ...headers };
}
export function getGlobalHeaders() {
return defaultHeaders;
}
/**
* @description
*/
@@ -148,6 +158,12 @@ export function createAxiosService({ logger }: { logger: ILogger }) {
config.httpsAgent = agents.httpsAgent;
config.httpAgent = agents.httpAgent;
if (Object.keys(defaultHeaders).length > 0) {
const headers = AxiosHeaders.from(defaultHeaders);
headers.set(config.headers || {});
config.headers = headers;
}
// const agent = new https.Agent({
// rejectUnauthorized: false // 允许自签名证书
// });
@@ -267,7 +283,7 @@ export function createAxiosService({ logger }: { logger: ILogger }) {
logger.error(`请求出错:${errorMessage} status:${status},statusText:${error.response?.statusText || error.code},url:${error.config?.url},method:${error.config?.method}`);
logger.error("返回数据:", JSON.stringify(error.response?.data));
if (error.response?.data) {
const message = error.response.data.message || error.response.data.msg || error.response.data.error;
const message = error.response?.data?.message || error.response?.data?.msg || error.response?.data?.error;
if (typeof message === "string") {
error.message = message;
}
@@ -0,0 +1,36 @@
/// <reference types="mocha" />
import { expect } from "chai";
import { stringUtils } from "./util.string.js";
describe("stringUtils", () => {
describe("maxLength", () => {
it("returns an empty string for empty input", () => {
expect(stringUtils.maxLength()).to.equal("");
expect(stringUtils.maxLength("")).to.equal("");
});
it("returns the original string when it is within the limit", () => {
expect(stringUtils.maxLength("certd", 5)).to.equal("certd");
expect(stringUtils.maxLength("certd", 6)).to.equal("certd");
});
it("truncates strings longer than the limit and appends ellipsis", () => {
expect(stringUtils.maxLength("certificate", 4)).to.equal("cert...");
});
});
describe("appendTimeSuffix", () => {
it("returns an empty string for empty input", () => {
expect(stringUtils.appendTimeSuffix()).to.equal("");
expect(stringUtils.appendTimeSuffix("")).to.equal("");
});
it("appends a millisecond timestamp suffix", () => {
const result = stringUtils.appendTimeSuffix("certd");
expect(result).to.match(/^certd-\d{17}$/);
});
});
});
+7
View File
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"inlineSourceMap": false
}
}
+6 -16
View File
@@ -7,8 +7,8 @@
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"inlineSourceMap":false,
"sourceMap": false,
"inlineSourceMap": false,
"sourceMap": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"stripInternal": true,
@@ -22,21 +22,11 @@
"composite": false,
"useDefineForClassFields": true,
"strict": true,
"typeRoots": [ "./typings", "./node_modules/@types"],
"typeRoots": ["./typings", "./node_modules/@types"],
"resolveJsonModule": true,
"isolatedModules": false,
"lib": ["ESNext", "DOM"],
"lib": ["ESNext", "DOM"]
},
"include": [
"src/**/*.ts",
"src/**/*.json"
],
"exclude": [
"*.js",
"*.ts",
"*.spec.ts",
"dist",
"node_modules",
"test"
],
"include": ["src/**/*.ts", "src/**/*.json"],
"exclude": ["*.js", "*.ts", "*.spec.ts", "dist", "node_modules", "src/**/*.test.ts", "test"]
}
+38
View File
@@ -3,6 +3,44 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.40.3](https://github.com/certd/certd/compare/v1.40.2...v1.40.3) (2026-05-21)
**Note:** Version bump only for package @certd/pipeline
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
**Note:** Version bump only for package @certd/pipeline
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
**Note:** Version bump only for package @certd/pipeline
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
**Note:** Version bump only for package @certd/pipeline
## [1.39.16](https://github.com/certd/certd/compare/v1.39.15...v1.39.16) (2026-05-13)
**Note:** Version bump only for package @certd/pipeline
## [1.39.15](https://github.com/certd/certd/compare/v1.39.14...v1.39.15) (2026-05-13)
**Note:** Version bump only for package @certd/pipeline
## [1.39.14](https://github.com/certd/certd/compare/v1.39.13...v1.39.14) (2026-05-11)
**Note:** Version bump only for package @certd/pipeline
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
### Bug Fixes
* cnameProvider域名支持设置子域名托管 ([7266af1](https://github.com/certd/certd/commit/7266af17491a98338022cfb18cfedfb93ca6ef8f))
### Performance Improvements
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
### Performance Improvements
+9 -5
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/pipeline",
"private": false,
"version": "1.39.12",
"version": "1.40.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -9,17 +9,18 @@
"scripts": {
"dev": "vite",
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
"build": "npm run before-build && tsc --skipLibCheck",
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
"dev-build": "npm run build",
"build3": "rollup -c",
"preview": "vite preview",
"test": "mocha --loader=ts-node/esm",
"test:unit": "cross-env NODE_ENV=unittest mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
"pub": "npm publish",
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/basic": "^1.39.12",
"@certd/plus-core": "^1.39.12",
"@certd/basic": "^1.40.3",
"@certd/plus-core": "^1.40.3",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"reflect-metadata": "^0.1.13"
@@ -36,14 +37,17 @@
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"chai": "4.3.10",
"cross-env": "^7.0.3",
"eslint": "^8.41.0",
"eslint-config-prettier": "^8.8.0",
"eslint-plugin-prettier": "^4.2.1",
"esmock": "^2.7.5",
"mocha": "^10.2.0",
"prettier": "^2.8.8",
"rimraf": "^5.0.5",
"ts-node": "^10.9.2",
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
"gitHead": "01c91ba294f88bd07fddf9358c4301bbb4027916"
}
+1
View File
@@ -19,6 +19,7 @@ export type AccessInputDefine = FormItemProps & {
};
export type AccessDefine = Registrable & {
icon?: string;
subtype?: string;
input?: {
[key: string]: AccessInputDefine;
};
@@ -0,0 +1,23 @@
/// <reference types="mocha" />
import { expect } from "chai";
import { PluginGroup, pluginGroups } from "./group.js";
describe("PluginGroup", () => {
it("initializes a group with defaults", () => {
const group = new PluginGroup("custom", "Custom");
expect(group.key).to.equal("custom");
expect(group.title).to.equal("Custom");
expect(group.order).to.equal(0);
expect(group.icon).to.equal("");
expect(group.plugins).to.deep.equal([]);
});
it("exposes built-in groups with stable keys", () => {
expect(pluginGroups.cert.key).to.equal("cert");
expect(pluginGroups.host.key).to.equal("host");
expect(pluginGroups.other.order).to.equal(10);
});
});
@@ -3,6 +3,7 @@ import { IAccess } from "../access/index.js";
export type CnameProvider = {
id: any;
domain: string;
subdomain?: string;
title?: string;
dnsProviderType?: string;
access?: IAccess;
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"inlineSourceMap": false
}
}
+6 -17
View File
@@ -7,8 +7,8 @@
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"inlineSourceMap":false,
"sourceMap": false,
"inlineSourceMap": false,
"sourceMap": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"importHelpers": true,
@@ -22,22 +22,11 @@
"composite": false,
"useDefineForClassFields": true,
"strict": true,
// "sourceMap": true,
// "sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": false,
"lib": ["ESNext", "DOM"],
"lib": ["ESNext", "DOM"]
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.json"
],
"exclude": [
"*.js",
"*.ts",
"*.spec.ts",
"dist",
"node_modules",
"test"
],
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.json"],
"exclude": ["*.js", "*.ts", "*.spec.ts", "dist", "node_modules", "src/**/*.test.ts", "test"]
}
+30
View File
@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.40.3](https://github.com/certd/certd/compare/v1.40.2...v1.40.3) (2026-05-21)
**Note:** Version bump only for package @certd/lib-huawei
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
**Note:** Version bump only for package @certd/lib-huawei
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
**Note:** Version bump only for package @certd/lib-huawei
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
**Note:** Version bump only for package @certd/lib-huawei
## [1.39.15](https://github.com/certd/certd/compare/v1.39.14...v1.39.15) (2026-05-13)
**Note:** Version bump only for package @certd/lib-huawei
## [1.39.14](https://github.com/certd/certd/compare/v1.39.13...v1.39.14) (2026-05-11)
**Note:** Version bump only for package @certd/lib-huawei
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
### Performance Improvements
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
**Note:** Version bump only for package @certd/lib-huawei
+5 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-huawei",
"private": false,
"version": "1.39.12",
"version": "1.40.3",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
@@ -11,6 +11,7 @@
"build": "npm run before-build && rollup -c ",
"dev-build": "npm run build",
"preview": "vite preview",
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
"pub": "npm publish"
},
"dependencies": {
@@ -21,8 +22,10 @@
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"cross-env": "^7.0.3",
"esmock": "^2.7.5",
"prettier": "^2.8.8",
"tslib": "^2.8.1"
},
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
"gitHead": "01c91ba294f88bd07fddf9358c4301bbb4027916"
}
@@ -9,6 +9,7 @@ module.exports = {
output: {
file: "dist/bundle.js",
format: "cjs",
sourcemap: false,
},
plugins: [
// 解析第三方依赖
@@ -22,10 +23,13 @@ module.exports = {
// ],
}),
Typescript({
tsconfig: "./tsconfig.build.json",
target: "esnext",
rootDir: "src",
declaration: true,
declarationDir: "dist/d",
sourceMap: false,
inlineSourceMap: false,
exclude: ["./node_modules/**", "./src/**/*.vue", "./src/**/*.spec.ts"],
allowSyntheticDefaultImports: true,
}),
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"inlineSourceMap": false
}
}
+1 -1
View File
@@ -21,7 +21,7 @@
"composite": false,
"useDefineForClassFields": true,
"strict": false,
"sourceMap": false,
"sourceMap": true,
"inlineSourceMap": false,
"resolveJsonModule": true,
"isolatedModules": false,
+30
View File
@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.40.3](https://github.com/certd/certd/compare/v1.40.2...v1.40.3) (2026-05-21)
**Note:** Version bump only for package @certd/lib-iframe
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
**Note:** Version bump only for package @certd/lib-iframe
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
**Note:** Version bump only for package @certd/lib-iframe
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
**Note:** Version bump only for package @certd/lib-iframe
## [1.39.15](https://github.com/certd/certd/compare/v1.39.14...v1.39.15) (2026-05-13)
**Note:** Version bump only for package @certd/lib-iframe
## [1.39.14](https://github.com/certd/certd/compare/v1.39.13...v1.39.14) (2026-05-11)
**Note:** Version bump only for package @certd/lib-iframe
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
### Performance Improvements
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
**Note:** Version bump only for package @certd/lib-iframe
+6 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-iframe",
"private": false,
"version": "1.39.12",
"version": "1.40.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -9,11 +9,12 @@
"scripts": {
"dev": "vite",
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
"build": "npm run before-build && tsc --skipLibCheck",
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
"dev-build": "npm run build",
"build3": "rollup -c",
"build2": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
"pub": "npm publish"
},
"dependencies": {
@@ -23,13 +24,15 @@
"@types/chai": "^4.3.3",
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"cross-env": "^7.0.3",
"eslint": "^8.24.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"esmock": "^2.7.5",
"prettier": "^2.8.8",
"rimraf": "^5.0.5",
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
"gitHead": "01c91ba294f88bd07fddf9358c4301bbb4027916"
}
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"inlineSourceMap": false
}
}
+1 -1
View File
@@ -8,6 +8,7 @@
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"inlineSourceMap":false,
"sourceMap": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"stripInternal": true,
@@ -22,7 +23,6 @@
"composite": false,
"useDefineForClassFields": true,
"strict": false,
// "sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"lib": ["ESNext", "DOM"],
+30
View File
@@ -3,6 +3,36 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.40.3](https://github.com/certd/certd/compare/v1.40.2...v1.40.3) (2026-05-21)
**Note:** Version bump only for package @certd/jdcloud
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
**Note:** Version bump only for package @certd/jdcloud
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
**Note:** Version bump only for package @certd/jdcloud
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
**Note:** Version bump only for package @certd/jdcloud
## [1.39.15](https://github.com/certd/certd/compare/v1.39.14...v1.39.15) (2026-05-13)
**Note:** Version bump only for package @certd/jdcloud
## [1.39.14](https://github.com/certd/certd/compare/v1.39.13...v1.39.14) (2026-05-11)
**Note:** Version bump only for package @certd/jdcloud
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
### Performance Improvements
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
**Note:** Version bump only for package @certd/jdcloud
+7 -4
View File
@@ -1,13 +1,15 @@
{
"name": "@certd/jdcloud",
"version": "1.39.12",
"version": "1.40.3",
"description": "jdcloud openApi sdk",
"main": "./dist/bundle.js",
"module": "./dist/bundle.js",
"types": "./dist/d/index.d.ts",
"scripts": {
"build": "rollup -c ",
"before-build": "node -e \"const fs=require('fs');fs.rmSync('dist',{recursive:true,force:true});fs.rmSync('tsconfig.tsbuildinfo',{force:true});fs.rmSync('.rollup.cache',{recursive:true,force:true});\"",
"build": "npm run before-build && rollup -c ",
"dev-build": "npm run build",
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
"pub": "npm publish"
},
"author": "",
@@ -29,7 +31,8 @@
"@typescript-eslint/parser": "^8.26.1",
"chai": "^4.1.2",
"config": "^1.30.0",
"cross-env": "^5.1.4",
"cross-env": "^7.0.3",
"esmock": "^2.7.5",
"js-yaml": "^3.11.0",
"mocha": "^5.0.0",
"prettier": "^2.8.8",
@@ -56,5 +59,5 @@
"fetch"
]
},
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
"gitHead": "01c91ba294f88bd07fddf9358c4301bbb4027916"
}
@@ -9,6 +9,7 @@ module.exports = {
output: {
file: "dist/bundle.js",
format: "cjs",
sourcemap: false,
},
plugins: [
// 解析第三方依赖
@@ -22,10 +23,13 @@ module.exports = {
// ],
}),
Typescript({
tsconfig: "./tsconfig.build.json",
target: "esnext",
rootDir: "src",
declaration: true,
declarationDir: "dist/d",
sourceMap: false,
inlineSourceMap: false,
exclude: ["./node_modules/**", "./src/**/*.vue", "./src/**/*.spec.ts"],
allowSyntheticDefaultImports: true,
}),
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"inlineSourceMap": false
}
}
+1 -1
View File
@@ -21,7 +21,7 @@
"composite": false,
"useDefineForClassFields": true,
"strict": false,
"sourceMap": false,
"sourceMap": true,
"inlineSourceMap": false,
"resolveJsonModule": true,
"isolatedModules": false,
+34
View File
@@ -3,6 +3,40 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.40.3](https://github.com/certd/certd/compare/v1.40.2...v1.40.3) (2026-05-21)
**Note:** Version bump only for package @certd/lib-k8s
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
**Note:** Version bump only for package @certd/lib-k8s
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
**Note:** Version bump only for package @certd/lib-k8s
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
**Note:** Version bump only for package @certd/lib-k8s
## [1.39.16](https://github.com/certd/certd/compare/v1.39.15...v1.39.16) (2026-05-13)
**Note:** Version bump only for package @certd/lib-k8s
## [1.39.15](https://github.com/certd/certd/compare/v1.39.14...v1.39.15) (2026-05-13)
**Note:** Version bump only for package @certd/lib-k8s
## [1.39.14](https://github.com/certd/certd/compare/v1.39.13...v1.39.14) (2026-05-11)
**Note:** Version bump only for package @certd/lib-k8s
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
### Performance Improvements
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
**Note:** Version bump only for package @certd/lib-k8s
+7 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@certd/lib-k8s",
"private": false,
"version": "1.39.12",
"version": "1.40.3",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
@@ -9,29 +9,32 @@
"scripts": {
"dev": "vite",
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
"build": "npm run before-build && tsc --skipLibCheck",
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
"dev-build": "npm run build",
"build3": "rollup -c",
"build2": "vue-tsc --noEmit && vite build",
"preview": "vite preview",
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
"pub": "npm publish",
"compile": "tsc --skipLibCheck --watch"
},
"dependencies": {
"@certd/basic": "^1.39.12",
"@certd/basic": "^1.40.3",
"@kubernetes/client-node": "0.21.0"
},
"devDependencies": {
"@types/chai": "^4.3.3",
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"cross-env": "^7.0.3",
"eslint": "^8.24.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"esmock": "^2.7.5",
"prettier": "^2.8.8",
"rimraf": "^5.0.5",
"tslib": "^2.8.1",
"typescript": "^5.4.2"
},
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
"gitHead": "01c91ba294f88bd07fddf9358c4301bbb4027916"
}
@@ -0,0 +1,7 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"sourceMap": false,
"inlineSourceMap": false
}
}
+1 -1
View File
@@ -8,6 +8,7 @@
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"inlineSourceMap":false,
"sourceMap": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"stripInternal": true,
@@ -22,7 +23,6 @@
"composite": false,
"useDefineForClassFields": true,
"strict": false,
// "sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"lib": ["ESNext", "DOM"],
+41
View File
@@ -3,6 +3,47 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [1.40.3](https://github.com/certd/certd/compare/v1.40.2...v1.40.3) (2026-05-21)
**Note:** Version bump only for package @certd/lib-server
## [1.40.2](https://github.com/certd/certd/compare/v1.40.1...v1.40.2) (2026-05-19)
**Note:** Version bump only for package @certd/lib-server
## [1.40.1](https://github.com/certd/certd/compare/v1.40.0...v1.40.1) (2026-05-18)
**Note:** Version bump only for package @certd/lib-server
# [1.40.0](https://github.com/certd/certd/compare/v1.39.16...v1.40.0) (2026-05-14)
### Features
* 彩虹登录支持选择多种登录方式 ([7aa0c7e](https://github.com/certd/certd/commit/7aa0c7e491fe660abb62e68792ff5474f19bd5b8))
## [1.39.16](https://github.com/certd/certd/compare/v1.39.15...v1.39.16) (2026-05-13)
**Note:** Version bump only for package @certd/lib-server
## [1.39.15](https://github.com/certd/certd/compare/v1.39.14...v1.39.15) (2026-05-13)
### Performance Improvements
* **network:** 新增全局公共http请求 headers设置 ([aad9045](https://github.com/certd/certd/commit/aad9045de55e76cb2ad09cac74a7bd60a4b47124))
## [1.39.14](https://github.com/certd/certd/compare/v1.39.13...v1.39.14) (2026-05-11)
### Bug Fixes
* 修复启动时报密钥备份不存在的问题 ([c966896](https://github.com/certd/certd/commit/c9668965226af6b54e0e576931dcba8b3d188ef3))
## [1.39.13](https://github.com/certd/certd/compare/v1.39.12...v1.39.13) (2026-05-10)
### Performance Improvements
* **设置:** 添加首页启用开关配置 ([25ad1e6](https://github.com/certd/certd/commit/25ad1e6f861e43288cc8bd90d4903628e6faec29))
* 重构自动加载模块并优化EAB授权处理 ([4755216](https://github.com/certd/certd/commit/4755216505ad18555a50da9d8008c2207c48df86))
## [1.39.12](https://github.com/certd/certd/compare/v1.39.11...v1.39.12) (2026-04-29)
### Performance Improvements
+14 -9
View File
@@ -1,6 +1,6 @@
{
"name": "@certd/lib-server",
"version": "1.39.12",
"version": "1.40.3",
"description": "midway with flyway, sql upgrade way ",
"private": false,
"type": "module",
@@ -9,9 +9,10 @@
"types": "./dist/index.d.ts",
"scripts": {
"before-build": "rimraf dist && rimraf tsconfig.tsbuildinfo && rimraf .rollup.cache",
"build": "npm run before-build && tsc --skipLibCheck",
"build": "npm run before-build && tsc -p tsconfig.build.json --skipLibCheck",
"dev-build": "npm run build",
"test": "midway-bin test --ts -V",
"test:unit": "cross-env NODE_ENV=unittest mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
"test1": "midway-bin test --ts -V -f test/blank.test.ts -t 'hash-check'",
"cov": "midway-bin cov --ts",
"lint": "mwts check",
@@ -28,11 +29,11 @@
],
"license": "AGPL",
"dependencies": {
"@certd/acme-client": "^1.39.12",
"@certd/basic": "^1.39.12",
"@certd/pipeline": "^1.39.12",
"@certd/plugin-lib": "^1.39.12",
"@certd/plus-core": "^1.39.12",
"@certd/acme-client": "^1.40.3",
"@certd/basic": "^1.40.3",
"@certd/pipeline": "^1.40.3",
"@certd/plugin-lib": "^1.40.3",
"@certd/plus-core": "^1.40.3",
"@midwayjs/cache": "3.14.0",
"@midwayjs/core": "3.20.11",
"@midwayjs/i18n": "3.20.13",
@@ -43,7 +44,6 @@
"@midwayjs/upload": "3.20.13",
"@midwayjs/validate": "3.20.13",
"better-sqlite3": "^11.1.2",
"cross-env": "^7.0.3",
"dayjs": "^1.11.7",
"lodash-es": "^4.17.21",
"mwts": "^1.3.0",
@@ -52,17 +52,22 @@
},
"devDependencies": {
"@types/chai": "^4.3.3",
"@types/mocha": "^10.0.1",
"@types/node": "^18",
"@typescript-eslint/eslint-plugin": "^8.26.1",
"@typescript-eslint/parser": "^8.26.1",
"cross-env": "^7.0.3",
"eslint": "^8.24.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"esmock": "^2.7.5",
"mocha": "^10.2.0",
"prettier": "^2.8.8",
"rimraf": "^5.0.5",
"ts-node": "^10.9.2",
"tslib": "^2.8.1",
"typeorm": "^0.3.11",
"typescript": "^5.4.2"
},
"gitHead": "898bc9b9f2f75df11ea0803b144862ba98b7511a"
"gitHead": "01c91ba294f88bd07fddf9358c4301bbb4027916"
}
@@ -0,0 +1,32 @@
/// <reference types="mocha" />
/// <reference types="node" />
import assert from "node:assert/strict";
import { Constants } from "./constants.js";
import { ParamException } from "./exception/param-exception.js";
import { Result } from "./result.js";
describe("lib-server basic helpers", () => {
it("builds success and error results", () => {
const success = Result.success("ok", { id: 1 });
assert.ok(success instanceof Result);
assert.equal(success.code, 0);
assert.equal(success.message, "ok");
assert.deepEqual(success.data, { id: 1 });
const error = Result.error(400, "bad request");
assert.ok(error instanceof Result);
assert.equal(error.code, 400);
assert.equal(error.message, "bad request");
assert.equal(error.data, undefined);
});
it("uses default param exception metadata", () => {
const error = new ParamException(undefined);
assert.equal(error.name, "ParamException");
assert.equal(error.code, Constants.res.param.code);
assert.equal(error.message, Constants.res.param.message);
});
});
@@ -32,6 +32,7 @@ export class SysPublicSettings extends BaseSettings {
customFooter?: string;
robots?: boolean = true;
aiChatEnabled = true;
homePageEnabled = true;
//验证码是否开启
@@ -63,6 +64,7 @@ export class SysPublicSettings extends BaseSettings {
type: string;
title: string;
addonId: number;
icon?: string;
}> = {};
notice?: string;
@@ -79,6 +81,7 @@ export class SysPrivateSettings extends BaseSettings {
httpsProxy? = '';
httpProxy? = '';
commonHeaders?: string = '';
reverseProxies?: Record<string, string> = {};
@@ -250,6 +253,14 @@ export class SysSuiteSetting extends BaseSettings {
intro?: string;
}
export class SysAutoFixSetting extends BaseSettings {
static __title__ = '自动修复记录';
static __key__ = 'sys.auto.fix';
static __access__ = 'private';
fixed: Record<string, boolean> = {};
}
export type SiteHidden = {
enabled: boolean;
@@ -271,4 +282,3 @@ export class SysSafeSetting extends BaseSettings {
autoHiddenTimes: 5,
};
}
@@ -5,7 +5,7 @@ import { SysSettingsEntity } from '../entity/sys-settings.js';
import { BaseSettings, SysInstallInfo, SysPrivateSettings, SysPublicSettings, SysSecret, SysSecretBackup } from './models.js';
import { getAllSslProviderDomains, setSslProviderReverseProxies, setWalkFromAuthoritative } from '@certd/acme-client';
import { cache, logger, mergeUtils, setGlobalProxy } from '@certd/basic';
import { cache, logger, mergeUtils, setGlobalHeaders, setGlobalProxy } from '@certd/basic';
import { isPlus } from '@certd/plus-core';
import * as dns from 'node:dns';
import { BaseService, setAdminMode } from '../../../basic/index.js';
@@ -167,6 +167,7 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
httpsProxy: privateSetting.httpsProxy,
};
setGlobalProxy(opts);
setGlobalHeaders(this.parseKeyValueText(privateSetting.commonHeaders));
if (privateSetting.dnsResultOrder) {
dns.setDefaultResultOrder(privateSetting.dnsResultOrder as any);
@@ -185,12 +186,12 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
}
setEnvironmentVars(vars: string) {
const envVars = {}
if (typeof vars !== 'string') {
vars = ""
parseKeyValueText(text: string) {
const values = {};
if (typeof text !== 'string') {
text = "";
}
vars.split('\n').forEach(line => {
text.split('\n').forEach(line => {
line = line.trim();
if (!line || line.startsWith('#')) {
return
@@ -204,11 +205,18 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
return
}
const [key, value] = line.split('=');
const eqIndex = line.indexOf('=');
const key = line.substring(0, eqIndex).trim();
const value = line.substring(eqIndex + 1).trim();
if (key && value) {
envVars[key.trim()] = value.trim();
values[key] = value;
}
});
return values;
}
setEnvironmentVars(vars: string) {
const envVars = this.parseKeyValueText(vars);
//先删除旧环境变量
if (lastSaveEnvVars) {
for (const key in lastSaveEnvVars) {
@@ -19,6 +19,9 @@ export class AccessEntity {
@Column({ comment: '类型', length: 100 })
type: string;
@Column({ name: 'subtype', comment: '子类型', length: 100, nullable: true })
subtype: string;
@Column({ name: 'setting', comment: '设置', length: 10240, nullable: true })
setting: string;

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