mirror of
https://github.com/certd/certd.git
synced 2026-06-22 01:37:32 +08:00
Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9ffea32176 | |||
| f32c99d4a1 | |||
| 1f1b1858c7 | |||
| 42fcb91f2e | |||
| 01568ca148 | |||
| 0d97ad67c5 | |||
| c94a5537a3 | |||
| d88dfc197e | |||
| f709c05c0d | |||
| 2c609da7a1 | |||
| d770c7bd08 | |||
| b9ccd4a8a0 | |||
| 8875e8059f | |||
| 6490366c68 | |||
| c278946771 | |||
| 1562d9de36 | |||
| 5bb0990abb | |||
| bfd3cacc68 | |||
| c7e1163d59 | |||
| fba7aeb71b | |||
| c66a2bd77a | |||
| ed58ae3c53 | |||
| 194463bea9 | |||
| 260f5ae777 | |||
| e85d824337 | |||
| e17fc39709 | |||
| da9b297b12 | |||
| 807dfcd57a | |||
| 0a410db52a | |||
| 4501095106 | |||
| bc731e4fb1 | |||
| 53561d2755 | |||
| e913fe509c | |||
| a3a215b7ae | |||
| 56f2949ac5 | |||
| c1b5a35f90 | |||
| 48ab1fbffe | |||
| 5f078273b3 | |||
| 636338f9ed | |||
| 6cbd629777 | |||
| 5a07dce759 | |||
| 15484bc119 | |||
| d6cd9d136d | |||
| c76815756b | |||
| eef93250ac | |||
| a1c6cf0477 | |||
| 14a0ccac93 | |||
| 9439743b7e | |||
| acbac6a9c3 | |||
| cc38ccd0e9 | |||
| 7d0cf846ac | |||
| f9541fab70 | |||
| 8d9870e9c6 | |||
| 0f3f8519e0 | |||
| 016ae865b1 | |||
| 3e9953a74a | |||
| 1fc80d2b93 | |||
| b55fe2ef19 | |||
| 71030b7e27 | |||
| 5e8bdac008 | |||
| 56b8c689ec | |||
| 454912d314 | |||
| 61e3f5761c | |||
| 2908569841 | |||
| 775226b49f | |||
| e3dacb5b3f | |||
| cdea411136 | |||
| fdb000ee7c | |||
| 4a0be1c29d | |||
| 892d22e225 | |||
| 4958a48b92 | |||
| 28bbea85f0 | |||
| 73b3a29cfc | |||
| 77b8024453 | |||
| 1175e1164b | |||
| 5546af518e | |||
| 99fd3083f2 | |||
| c0df8be832 | |||
| 73cab6a6ee | |||
| 7a71e45799 | |||
| fdb1d1e6dd | |||
| 6dd4d6adeb |
@@ -1,36 +0,0 @@
|
|||||||
# 后端规则
|
|
||||||
|
|
||||||
主包:`packages/ui/certd-server`。后端使用 Node.js、ESM、TypeScript、MidwayJS 3、Koa、TypeORM,默认 better-sqlite3,同时支持 PostgreSQL 和 MySQL,并通过 `@certd/midway-flyway-js` 使用类似 Flyway 的 SQL 迁移机制。
|
|
||||||
|
|
||||||
详细入口、模块和验证命令见 `.codex/repo-map.md`。
|
|
||||||
|
|
||||||
## 默认开发配置
|
|
||||||
|
|
||||||
- HTTP 端口:`7001`
|
|
||||||
- HTTPS 端口:`7002`
|
|
||||||
- 默认 SQLite 数据库:`./data/db.sqlite`
|
|
||||||
- 默认文件根目录:`./data/files`
|
|
||||||
|
|
||||||
## 数据与迁移
|
|
||||||
|
|
||||||
- 后端使用 TypeORM 实体加 SQL 迁移。
|
|
||||||
- 重点查看 `packages/ui/certd-server/src/modules/**/entity/*.ts` 和 `packages/ui/certd-server/db/migration/*.sql`。
|
|
||||||
- 默认配置中 `synchronize: false`,涉及表结构变更时应添加或更新迁移脚本,不要依赖 TypeORM 自动同步。
|
|
||||||
|
|
||||||
## 文件上传
|
|
||||||
|
|
||||||
使用 `/basic/file/upload` 上传文件后,接口返回的是临时缓存 key。业务保存表单或设置时,后端必须调用 `FileService.saveFile(userId, key, "public" | "private")` 转成永久文件 key 后再入库/入设置;不要直接保存 `tmpfile_key_...`,否则后续回显或下载会失效。
|
|
||||||
|
|
||||||
## Service 与事务
|
|
||||||
|
|
||||||
- 后端方法参数超过 3 个时,尽量改为对象参数传入。
|
|
||||||
- 需要传入 `manager` / `EntityManager` 做事务传播的方法,必须使用对象参数,不要把 `manager` 作为位置参数藏在参数列表末尾。
|
|
||||||
- 后端 service 层只有存在事务链路传播需求时才定义 `ctx`,不要为了将来可能需要而提前给普通方法加 `ctx`。
|
|
||||||
- 事务链路方法统一采用 `method(ctx, req)` 形式,`ctx` 放第一位并承载 `manager?: EntityManager` 等横切上下文,业务参数放在 `req` 对象里,例如 `settleCommission({ manager }, { tradeId, userId, amount })`。
|
|
||||||
- 无事务链路需求的普通查询、纯函数和简单私有方法继续使用明确参数。
|
|
||||||
- service 内部需要根据事务上下文选择 Repository 时,优先使用 `BaseService.getRepo(ctx, EntityClass)`;不要在业务方法里反复写 `ctx.manager?.getRepository(Entity) || this.xxxRepository`。
|
|
||||||
- 拿到 repo 后 save/update/delete/find 都能做,不需要再包一层 `saveEntity` 之类的单一用途方法。
|
|
||||||
- service 拼接用户与项目范围查询条件时,优先使用 `BaseService.buildUserProjectQuery(userId, projectId)`;不要直接写 `{ userId, projectId }`,否则 `projectId` 为空时可能把 `null`/`undefined` 带入 TypeORM 条件,导致查询或 update 不符合预期。
|
|
||||||
- `ctx` 类型统一从 `BaseService` 导出的 `ServiceContext` 复用,不要在每个 service 里重复定义。
|
|
||||||
- 需要“有事务则复用、无事务则开启”时,使用 `BaseService.transactionWithCtx(ctx, callback)`:`ctx.manager` 存在则直接执行 callback,否则自动 `this.transaction()`。不要在业务代码里手写 `if (ctx.manager) { ... } else { await this.transaction(...) }`。
|
|
||||||
- 新增方法注意不要与 `BaseService` 基类方法签名冲突,例如 `delete(id)` vs `BaseService.delete(ids, where?)`,ts-node 下会直接 TS2416 编译报错。冲突时改用具体名称如 `deleteById`。
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# 代码风格规则
|
|
||||||
|
|
||||||
## 基本原则
|
|
||||||
|
|
||||||
- 中文 README 在部分 PowerShell 环境中可能显示乱码;`README_en.md` 可读性更好,且包含同样的高层项目说明。
|
|
||||||
- 根包管理器是 pnpm,不要引入 npm/yarn lockfile。
|
|
||||||
- 优先沿用现有模块、插件、服务模式,再考虑新增抽象。
|
|
||||||
- 注意本地数据和配置里可能包含凭据、证书材料等敏感信息。
|
|
||||||
|
|
||||||
## 注释
|
|
||||||
|
|
||||||
本仓库代码注释优先使用中文,尤其是解释业务规则、兼容逻辑、协议细节和隐藏风险时;除非文件已有明确英文注释风格或引用外部英文术语,否则不要新增英文说明性注释。
|
|
||||||
|
|
||||||
## 可读性
|
|
||||||
|
|
||||||
代码可读性优先于短写法。遇到包含业务分支的复杂三元表达式、内联对象、链式调用或条件组合时,优先拆成命名清晰的中间变量、独立分支或小函数,让读代码的人能一眼看出业务意图;不要为了少写几行把逻辑压成难读的一坨。
|
|
||||||
|
|
||||||
在对象字面量、查询条件或函数参数里不要内联调用多层 helper,例如 `{ domain, ...this.buildUserProjectQuery(userId, projectId) }`。应先用命名变量承接结果,例如 `const userProjectQuery = this.buildUserProjectQuery(userId, projectId)`,再在对象里展开 `...userProjectQuery`,让条件构造和业务字段都更容易阅读。
|
|
||||||
|
|
||||||
## DRY
|
|
||||||
|
|
||||||
遵守 DRY 原则:同一业务规则、字段转换、权限判断、Repository 选择、事务传播、金额计算等逻辑不要在多个地方复制粘贴。第二次出现时可以先保持清晰,第三次出现前应优先抽成局部 helper、service 方法或已有公共工具;抽象要服务于减少真实重复和降低修改风险,不要为了形式上的“复用”制造过度设计。
|
|
||||||
|
|
||||||
## 单一职责
|
|
||||||
|
|
||||||
遵守单一职责原则:一个方法只负责一个清晰的业务步骤或技术步骤。流程编排方法可以串联多个步骤,但具体的校验、计算、持久化、状态变更、展示数据组装应尽量拆到命名明确的小方法中;不要让一个方法同时承担查询、校验、计算、写库、格式化返回等过多职责。
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# 前端规则
|
|
||||||
|
|
||||||
主包:`packages/ui/certd-client`。前端使用 Vue 3、Vite、TypeScript、Ant Design Vue、Fast Crud、Pinia、vue-router、vue-i18n、Tailwind/Windi 相关样式工具。
|
|
||||||
|
|
||||||
详细入口、路由、状态、API、视图、locale 和验证命令见 `.codex/repo-map.md`。
|
|
||||||
|
|
||||||
## 禁跑命令
|
|
||||||
|
|
||||||
- 不要运行前端 `pnpm tsc` / `vue-tsc`:当前依赖组合中 `vue-tsc@1.8.27` 会直接抛内部错误 `Search string not found: "/supportedTSExtensions = .*(?=;)/"`,不是有效的项目类型检查结果。
|
|
||||||
- 前端暂不跑单元测试;当前 `test:unit` 只是占位脚本。
|
|
||||||
|
|
||||||
## 格式化与校验
|
|
||||||
|
|
||||||
前端 TS/Vue/locale 等文件改动后,优先只对本次改动文件运行项目现有自动格式化/修复:
|
|
||||||
|
|
||||||
- Prettier:`packages\ui\certd-client\node_modules\.bin\prettier.cmd --write <files>`
|
|
||||||
- ESLint:`packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix <files>`
|
|
||||||
|
|
||||||
不要为了格式化无关文件而扩大 diff。项目保留了 `tslint` 依赖,但当前主要使用 ESLint + Prettier。
|
|
||||||
|
|
||||||
## Fast Crud 页面
|
|
||||||
|
|
||||||
- 列表管理、后台管理、记录查询、CRUD 表格类页面,默认优先使用 Fast Crud(`@fast-crud/fast-crud`、`fs-crud`、`useFs`、`createCrudOptions`)实现。
|
|
||||||
- 只有轻量只读展示、强交互自定义界面或已有页面模式明确不适合 Fast Crud 时,才手写 `a-table` / 自定义列表,并在回复中说明原因。
|
|
||||||
- 开发或重构这类页面前,先读取 `.trae/skills/fast-crud-page-dev/SKILL.md`,按仓库内 Fast Crud 页面拆分与验证方式实现。
|
|
||||||
- 页面内嵌 Fast Crud 表格时,要显式给外层容器稳定高度或 `flex: 1; min-height: 0` 的撑满链路;Fast Crud 依赖外部元素高度,不能只依赖表格默认高度。
|
|
||||||
- 后台管理列表里展示或筛选用户字段时,优先参考 `packages/ui/certd-client/src/views/sys/suite/user-suite/crud.tsx` 的 `userId` 字段模式:前端使用 `table-select` + `/sys/authority/user/getSimpleUserByIds` 字典回显和搜索;不要为了展示用户名让后端列表接口额外 `fillSimpleUser` / `userDisplay`,除非该接口本身就是用户端业务列表且已有明确模式。
|
|
||||||
|
|
||||||
## 对话框
|
|
||||||
|
|
||||||
前端对话框里只做纯确认时可以使用 `Modal.confirm`;只要对话框里有字段输入、表单校验或提交字段,统一使用 `useFormDialog` / `openFormDialog`,不要在 `Modal.confirm` 的 `content` 里手写输入框。
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# 流水线与插件规则
|
|
||||||
|
|
||||||
项目最关键的架构概念是证书流水线。核心导出、关键抽象、插件目录和共享 helper 位置见 `.codex/repo-map.md`。
|
|
||||||
|
|
||||||
插件是核心能力,不是边缘功能。新增服务商、DNS 验证、证书部署、通知方式等能力,通常应该放在插件包里,或放在 `packages/ui/certd-server/src/plugins/<plugin-name>/` 下。
|
|
||||||
|
|
||||||
## 改动归属
|
|
||||||
|
|
||||||
修改证书申请、验证、部署或通知行为时,先判断改动属于哪里:
|
|
||||||
|
|
||||||
- ACME client 代码
|
|
||||||
- pipeline 核心抽象
|
|
||||||
- 后端 module/service/entity/controller
|
|
||||||
- 某个具体插件实现
|
|
||||||
- 前端 view/form/schema
|
|
||||||
|
|
||||||
如果只是某个服务商或部署目标的问题,不要轻易修改共享 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;否则应阻止继续申请并提示用户刷新账号私钥。
|
|
||||||
|
|
||||||
## 插件开发技能
|
|
||||||
|
|
||||||
仓库内置了 Certd 插件开发技能,供 Trae 和 Codex 共用:
|
|
||||||
|
|
||||||
- Trae 入口:`.trae/skills`
|
|
||||||
- Codex 入口:`.codex/skills`
|
|
||||||
|
|
||||||
其中 `.codex/skills` 是指向 `.trae/skills` 的目录链接,不要复制出第二份技能内容。更新技能时只维护 `.trae/skills` 下的原始文件,Codex 会通过 `.codex/skills` 读取同一份内容。
|
|
||||||
|
|
||||||
当前技能包括:
|
|
||||||
|
|
||||||
- `access-plugin-dev`:开发 Access 授权插件
|
|
||||||
- `dns-provider-dev`:开发 DNS Provider 插件
|
|
||||||
- `fast-crud-page-dev`:开发或重构前端 Fast Crud 列表管理页面
|
|
||||||
- `task-plugin-dev`:开发 Task 部署任务插件
|
|
||||||
- `plugin-converter`:将插件转换为 YAML 配置
|
|
||||||
|
|
||||||
做插件相关任务时,先读取对应技能目录下的 `SKILL.md`,再进入具体实现。若用户在插件开发中指出更好的做法,应总结并更新对应技能。
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# 测试与验证规则
|
|
||||||
|
|
||||||
实现新功能或修复行为缺陷前,先补对应单元测试,并先运行测试确认它处于失败状态;再实现功能或修复代码,反复运行聚焦单元测试直到通过。若某项改动确实不适合先写单元测试,应在回复中说明原因和替代验证方式。
|
|
||||||
|
|
||||||
后补单元测试时,应先基于对正确行为的实际预期编写测试,而不是为了迎合现有实现改写预期;如果运行后出现红灯,且通过测试需要修改已有实现,应先向用户确认这是确实的 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` 暂时不跑单元测试。前端改动优先使用 Prettier/ESLint 做改动文件验证。
|
|
||||||
|
|
||||||
优先对改动包运行聚焦测试;只有跨包影响明显时再考虑全 monorepo 构建。
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
# Certd 仓库地图
|
|
||||||
|
|
||||||
本文档由 Codex 子智能体只读探索后整理,用于后续开发时快速定位代码。进入仓库仍应先读取根目录 `AGENTS.md`,本文件只作为导航补充。
|
|
||||||
|
|
||||||
## 顶层结构
|
|
||||||
|
|
||||||
Certd 是 pnpm + lerna-lite monorepo。
|
|
||||||
|
|
||||||
- `package.json`:根脚本与 workspace 元信息
|
|
||||||
- `pnpm-workspace.yaml`:workspace 匹配规则,包含 `packages/**`、`packages/ui/**`
|
|
||||||
- `lerna.json`:lerna-lite 配置
|
|
||||||
- `docs`:VitePress 文档站
|
|
||||||
- `docker`:Docker 安装和运行相关文件
|
|
||||||
- `packages/core/acme-client`:ACME 协议客户端
|
|
||||||
- `packages/core/basic`:共享基础工具
|
|
||||||
- `packages/core/pipeline`:流水线核心抽象、插件模型、执行上下文
|
|
||||||
- `packages/libs`:共享集成库
|
|
||||||
- `packages/plugins/plugin-lib`:证书、DNS Provider、格式转换等插件共享能力
|
|
||||||
- `packages/plugins/plugin-cert`:证书插件包入口
|
|
||||||
- `packages/ui/certd-server`:后端 Midway 服务
|
|
||||||
- `packages/ui/certd-client`:前端 Vue/Vite 管理台
|
|
||||||
- `packages/pro`:商业版独立 Git 工作区,需在该目录内单独检查状态
|
|
||||||
|
|
||||||
运行时或生成产物通常包括根目录 `node_modules`、`logs`、`output`、`lerna-debug.log`、`tmp-certd-client-vite*.log`,以及后端 `packages/ui/certd-server/data`、`packages/ui/certd-server/logs`、各包 `dist`、插件 metadata/yaml 导出结果。
|
|
||||||
|
|
||||||
## 常用验证
|
|
||||||
|
|
||||||
- 根目录启动后端生产模式:`pnpm run start:server`
|
|
||||||
- 后端开发服务:`corepack pnpm --dir packages\ui\certd-server dev`
|
|
||||||
- 后端聚焦单测:`corepack pnpm --dir packages\ui\certd-server test:unit`
|
|
||||||
- 后端完整测试:`corepack pnpm --dir packages\ui\certd-server test`
|
|
||||||
- 后端构建:`corepack pnpm --dir packages\ui\certd-server build`
|
|
||||||
- 前端开发服务:`corepack pnpm --dir packages\ui\certd-client dev`
|
|
||||||
- 前端构建:`corepack pnpm --dir packages\ui\certd-client build`
|
|
||||||
- 前端改动文件格式化:`packages\ui\certd-client\node_modules\.bin\prettier.cmd --write <files>`
|
|
||||||
- 前端改动文件 ESLint 修复:`packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix <files>`
|
|
||||||
|
|
||||||
不要主动运行 `pnpm install`。前端不要运行 `pnpm tsc` / `vue-tsc`,当前依赖组合中 `vue-tsc@1.8.27` 会抛无效内部错误;前端 `test:unit` 也只是占位。
|
|
||||||
|
|
||||||
## 后端地图
|
|
||||||
|
|
||||||
主包:`packages/ui/certd-server`。
|
|
||||||
|
|
||||||
- `bootstrap.js`:Midway 启动入口,使用 `@midwayjs/bootstrap`
|
|
||||||
- `src/configuration.ts`:Midway 主配置,注册组件和全局中间件
|
|
||||||
- `src/config/config.default.ts`:端口、HTTPS、静态文件、cron、TypeORM、Flyway、上传、JWT、Swagger 默认配置
|
|
||||||
- `src/config/loader.ts`:读取 `.env`、`.env.<env>.yaml`,支持 `certd_` 前缀环境变量覆盖嵌套配置
|
|
||||||
- `src/modules`:业务模块根目录,例如 `basic`、`cert`、`cname`、`cron`、`login`、`monitor`、`open`、`pipeline`、`plugin`、`suite`、`sys`
|
|
||||||
- `src/controller`:API 入口,按 `basic`、`user`、`sys`、`openapi` 分组
|
|
||||||
- `db/migration`:SQL 迁移目录,TypeORM `synchronize: false`,表结构变更应配套迁移 SQL
|
|
||||||
|
|
||||||
测试使用 Mocha + Node `assert/strict`,纯单测放在 `src/**/*.test.ts`,尽量与被测文件相邻。可参考 `src/utils/random.test.ts`、`src/controller/basic/app-controller.test.ts`、`src/modules/pipeline/service/pipeline-service.test.ts`。
|
|
||||||
|
|
||||||
## 前端地图
|
|
||||||
|
|
||||||
主包:`packages/ui/certd-client`。
|
|
||||||
|
|
||||||
- `vite.config.ts`:Vite 配置,dev 端口 `3008`,`/api`、`/certd/api` 代理到后端 `127.0.0.1:7001`
|
|
||||||
- `src/main.ts`:Vue 启动入口,注册 AntDV、Vben、router、全局组件、插件和偏好设置
|
|
||||||
- `src/App.vue`:根组件,包含 `AConfigProvider`、`FsFormProvider`、`router-view`
|
|
||||||
- `src/router/index.ts`、`src/router/resolve.ts`:路由入口,使用 `createWebHashHistory`
|
|
||||||
- `src/router/source/modules/certd.ts`:Certd 主业务路由
|
|
||||||
- `src/store`:Pinia store,主要有 `user`、`project`、`settings`、`plugin`
|
|
||||||
- `src/api/service.ts`:Axios 封装
|
|
||||||
- `src/api/tools.ts`:错误与响应工具
|
|
||||||
- `src/views/certd`:核心业务视图,例如 `pipeline`、`cert`、`monitor`、`access`、`notification`、`open`、`project`、`suite`、`wallet`
|
|
||||||
- `src/locales`:国际化入口与语言包
|
|
||||||
|
|
||||||
列表管理、后台管理、记录查询、CRUD 表格页面优先使用 Fast Crud。开发前读取 `.trae/skills/fast-crud-page-dev/SKILL.md`。常见拆分是 `api.ts`、`crud.tsx`、`index.vue`。可参考 `src/views/certd/access`、`src/views/sys/suite/user-suite/crud.tsx`、`src/views/certd/wallet/index.vue`。内嵌 `fs-crud` 时要给外层稳定高度或完整 `flex: 1; min-height: 0` 链路。
|
|
||||||
|
|
||||||
## 流水线与插件地图
|
|
||||||
|
|
||||||
核心入口:`packages/core/pipeline/src/index.ts`,导出 `core`、`dt`、`access`、`registry`、`plugin`、`context`、`decorator`、`service`、`notification`。
|
|
||||||
|
|
||||||
- `packages/core/pipeline/src/plugin`:任务插件抽象,例如 `AbstractTaskPlugin`、`IsTaskPlugin`、`TaskInput`、`pluginRegistry`
|
|
||||||
- `packages/core/pipeline/src/access`:授权插件抽象,例如 `BaseAccess`、`IsAccess`、`AccessInput`、`accessRegistry`
|
|
||||||
- `packages/core/pipeline/src/dt/pipeline.ts`:`Pipeline`、`Stage`、`Task`、`RunStrategy` 等流水线数据结构
|
|
||||||
- `packages/core/pipeline/src/core`:执行器、上下文、运行历史、文件存储等
|
|
||||||
- `packages/core/pipeline/src/service`:CNAME、事件、配置、邮件、URL 等 pipeline service 接口
|
|
||||||
- `packages/ui/certd-server/src/plugins`:后端内置服务商、DNS、部署、通知等插件
|
|
||||||
- `packages/ui/certd-server/src/plugins/plugin-cert`:证书申请核心插件
|
|
||||||
- `packages/ui/certd-server/src/plugins/plugin-lib`:后端插件 helper/access
|
|
||||||
- `packages/plugins/plugin-lib/src/cert`:`CertReader`、`CertConverter`、DNS Provider 公共能力
|
|
||||||
- `packages/plugins/plugin-lib/src/cert/dns-provider`:`AbstractDnsProvider`、`dnsProviderRegistry`、`DomainParser`
|
|
||||||
|
|
||||||
插件开发技能入口:
|
|
||||||
|
|
||||||
- `.trae/skills/dns-provider-dev/SKILL.md`:DNS Provider 插件
|
|
||||||
- `.trae/skills/task-plugin-dev/SKILL.md`:Task 部署任务插件
|
|
||||||
- `.trae/skills/access-plugin-dev/SKILL.md`:Access 授权插件
|
|
||||||
- `.trae/skills/plugin-converter/SKILL.md`:插件转 YAML 配置
|
|
||||||
|
|
||||||
改动归属判断:
|
|
||||||
|
|
||||||
- ACME 协议、EAB、账号、订单、挑战流程:优先看 `packages/core/acme-client` 或 `packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/acme.ts`
|
|
||||||
- 流水线执行、任务生命周期、输入输出、注册机制:看 `packages/core/pipeline`
|
|
||||||
- 单个云厂商 DNS 验证、证书部署、API 调用失败:改对应 `packages/ui/certd-server/src/plugins/plugin-xxx`
|
|
||||||
- 通用证书读取、DNS Provider 公共能力、格式转换:改 `packages/plugins/plugin-lib`
|
|
||||||
- 后端业务数据、接口、实体、权限、迁移:改 `packages/ui/certd-server/src/modules` 与 `src/controller`
|
|
||||||
- 表单、列表、插件配置 UI:改 `packages/ui/certd-client/src/views/certd` 及对应 `src/api`
|
|
||||||
|
|
||||||
原则:如果只是单个服务商或部署目标的问题,不动共享 pipeline/core;只有可复用的公共语义或跨插件一致行为,才考虑上移到 `packages/core/pipeline` 或 `packages/plugins/plugin-lib`。
|
|
||||||
|
|
||||||
## Git 注意事项
|
|
||||||
|
|
||||||
子智能体探索时根仓库 `git status --short` 为空。`packages/pro` 也是独立仓库且当时未显示未提交改动,但曾出现无法删除 `packages/pro/.git/index.lock` 的警告;后续操作 pro 仓库前应先检查该锁文件或占用状态。
|
|
||||||
+6
-1
@@ -1,4 +1,4 @@
|
|||||||
./packages/core/lego
|
./packages/core/lego
|
||||||
# IntelliJ project files
|
# IntelliJ project files
|
||||||
node_modules/
|
node_modules/
|
||||||
npm-debug.log
|
npm-debug.log
|
||||||
@@ -35,3 +35,8 @@ test.js
|
|||||||
.pnpm-lock.yaml
|
.pnpm-lock.yaml
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
.studio/
|
.studio/
|
||||||
|
|
||||||
|
# Certd 推广报告,仅本地使用
|
||||||
|
/popularize/reports/
|
||||||
|
output/
|
||||||
|
.uploads/
|
||||||
@@ -0,0 +1,412 @@
|
|||||||
|
# 插件依赖按需加载方案
|
||||||
|
|
||||||
|
## 背景与目标
|
||||||
|
|
||||||
|
### 当前问题
|
||||||
|
- `packages/ui/certd-server/node_modules` 包含 50+ 个插件的所有依赖,体积庞大
|
||||||
|
- 大量云厂商 SDK(AWS、阿里云、腾讯云、华为云等)只在特定插件中使用
|
||||||
|
- 用户通常只使用少数几个插件,但必须安装所有依赖
|
||||||
|
|
||||||
|
### 目标
|
||||||
|
实现依赖的按需下载和加载:
|
||||||
|
1. 插件依赖独立管理,不占用主 `node_modules` 空间
|
||||||
|
2. 只有当用户首次使用某插件时,才动态下载该插件需要的依赖
|
||||||
|
3. 依赖安装完成后,通过 `await import()` 从独立路径加载
|
||||||
|
4. 保持现有插件代码的最小改动
|
||||||
|
|
||||||
|
## 当前架构分析
|
||||||
|
|
||||||
|
### 插件加载机制
|
||||||
|
- 插件位于 `packages/ui/certd-server/src/plugins/` 下(50+ 个插件目录)
|
||||||
|
- `AutoLoadPlugins` 类在启动时扫描 `dist/plugins` 目录并动态导入
|
||||||
|
- 插件注册到不同的 registry:`accessRegistry`, `pluginRegistry`, `dnsProviderRegistry` 等
|
||||||
|
- 插件代码已经使用 `await import()` 进行懒加载(如 `await import("@aws-sdk/client-acm")`)
|
||||||
|
|
||||||
|
### 重型依赖分布
|
||||||
|
从 `packages/ui/certd-server/package.json` 分析,以下依赖体积大且仅特定插件使用:
|
||||||
|
|
||||||
|
**云厂商 SDK(按插件分组):**
|
||||||
|
- **AWS 插件**:`@aws-sdk/client-acm`, `@aws-sdk/client-cloudfront`, `@aws-sdk/client-iam`, `@aws-sdk/client-route-53`, `@aws-sdk/client-s3`, `@aws-sdk/client-sts`
|
||||||
|
- **阿里云插件**:`@alicloud/openapi-client`, `@alicloud/pop-core`, `@alicloud/tea-typescript`, `@alicloud/fc20230330` 等
|
||||||
|
- **腾讯云插件**:`tencentcloud-sdk-nodejs`, `cos-nodejs-sdk-v5`
|
||||||
|
- **华为云插件**:`@huaweicloud/huaweicloud-sdk-cdn`, `@huaweicloud/huaweicloud-sdk-core` 等
|
||||||
|
- **Azure 插件**:`@azure/arm-dns`, `@azure/identity`
|
||||||
|
- **Google Cloud 插件**:`@google-cloud/dns`, `@google-cloud/publicca`
|
||||||
|
- **火山引擎插件**:`@volcengine/openapi`, `@volcengine/tos-sdk`
|
||||||
|
|
||||||
|
**网络/工具库:**
|
||||||
|
- `ssh2`, `socks`, `socks-proxy-agent`(SSH 相关插件)
|
||||||
|
- `ali-oss`, `qiniu`, `basic-ftp`(存储/传输插件)
|
||||||
|
- `nodemailer`(邮件通知插件)
|
||||||
|
|
||||||
|
**通用依赖(保留在主 package.json):**
|
||||||
|
- `@midwayjs/*` 系列(框架核心)
|
||||||
|
- `@certd/*` 系列(项目内部包)
|
||||||
|
- `axios`, `lodash-es`, `dayjs`, `js-yaml` 等基础工具
|
||||||
|
|
||||||
|
## 设计方案
|
||||||
|
|
||||||
|
### 架构概览
|
||||||
|
|
||||||
|
```
|
||||||
|
packages/ui/certd-server/
|
||||||
|
├── package.json # 主依赖(框架、通用工具)
|
||||||
|
├── node_modules/ # 主依赖安装目录
|
||||||
|
├── optional-deps/ # 新增:可选依赖管理目录
|
||||||
|
│ ├── package.json # 可选依赖总配置(用于 pnpm install)
|
||||||
|
│ ├── pnpm-lock.yaml # 可选依赖锁文件
|
||||||
|
│ └── node_modules/ # 可选依赖安装目录
|
||||||
|
├── src/
|
||||||
|
│ └── modules/
|
||||||
|
│ └── dependency/ # 新增:依赖管理模块
|
||||||
|
│ ├── dependency-manager.ts # 核心:依赖管理器
|
||||||
|
│ ├── dependency-registry.ts # 依赖注册表(插件 -> 依赖映射)
|
||||||
|
│ └── types.ts # 类型定义
|
||||||
|
```
|
||||||
|
|
||||||
|
### 核心组件
|
||||||
|
|
||||||
|
#### 1. 依赖管理器(DependencyManager)
|
||||||
|
|
||||||
|
**职责:**
|
||||||
|
- 检查依赖是否已安装
|
||||||
|
- 动态执行 `pnpm install` 安装缺失依赖
|
||||||
|
- 提供从 `optional-deps/node_modules` 加载依赖的方法
|
||||||
|
- 并发控制:避免多个插件同时触发安装
|
||||||
|
|
||||||
|
**关键方法:**
|
||||||
|
```typescript
|
||||||
|
class DependencyManager {
|
||||||
|
// 确保依赖已安装,返回依赖模块
|
||||||
|
async ensureAndImport<T>(packageName: string): Promise<T>
|
||||||
|
|
||||||
|
// 检查依赖是否已安装
|
||||||
|
async isInstalled(packageName: string): Promise<boolean>
|
||||||
|
|
||||||
|
// 安装依赖(带锁,避免并发)
|
||||||
|
async installDependencies(packages: string[]): Promise<void>
|
||||||
|
|
||||||
|
// 从 optional-deps/node_modules 加载依赖
|
||||||
|
async loadModule<T>(packageName: string): Promise<T>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**实现要点:**
|
||||||
|
- 使用文件锁(如 `proper-lockfile`)防止并发安装
|
||||||
|
- 安装前检查 `optional-deps/node_modules/{packageName}` 是否存在
|
||||||
|
- 安装命令:`pnpm install --dir optional-deps --ignore-workspace`
|
||||||
|
- 加载时使用绝对路径:`import('file:///absolute/path/to/optional-deps/node_modules/package')`
|
||||||
|
|
||||||
|
#### 2. 依赖注册表(DependencyRegistry)
|
||||||
|
|
||||||
|
**职责:**
|
||||||
|
- 维护插件名称到依赖列表的映射
|
||||||
|
- 提供依赖查询接口
|
||||||
|
|
||||||
|
**数据结构:**
|
||||||
|
```typescript
|
||||||
|
interface PluginDependencyConfig {
|
||||||
|
pluginName: string;
|
||||||
|
dependencies: {
|
||||||
|
packageName: string;
|
||||||
|
version: string;
|
||||||
|
optional?: boolean; // 是否可选(安装失败不阻塞)
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 示例注册
|
||||||
|
dependencyRegistry.register('plugin-aws', [
|
||||||
|
{ packageName: '@aws-sdk/client-acm', version: '^3.964.0' },
|
||||||
|
{ packageName: '@aws-sdk/client-cloudfront', version: '^3.964.0' },
|
||||||
|
{ packageName: '@aws-sdk/client-route-53', version: '^3.964.0' },
|
||||||
|
]);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. 插件集成
|
||||||
|
|
||||||
|
**改造现有插件代码:**
|
||||||
|
|
||||||
|
改造前(`plugin-aws/libs/aws-client.ts`):
|
||||||
|
```typescript
|
||||||
|
const { ACMClient, ImportCertificateCommand } = await import("@aws-sdk/client-acm");
|
||||||
|
```
|
||||||
|
|
||||||
|
改造后:
|
||||||
|
```typescript
|
||||||
|
import { DependencyManager } from "../../../modules/dependency/dependency-manager.js";
|
||||||
|
|
||||||
|
const depManager = new DependencyManager();
|
||||||
|
const { ACMClient, ImportCertificateCommand } = await depManager.ensureAndImport("@aws-sdk/client-acm");
|
||||||
|
```
|
||||||
|
|
||||||
|
**简化方案(推荐):**
|
||||||
|
|
||||||
|
创建辅助函数,减少改动量:
|
||||||
|
```typescript
|
||||||
|
// src/modules/dependency/import-helper.ts
|
||||||
|
export async function importOptionalDep<T>(packageName: string): Promise<T> {
|
||||||
|
const depManager = new DependencyManager();
|
||||||
|
return await depManager.ensureAndImport<T>(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插件中使用
|
||||||
|
import { importOptionalDep } from "../../../modules/dependency/import-helper.js";
|
||||||
|
const { ACMClient } = await importOptionalDep("@aws-sdk/client-acm");
|
||||||
|
```
|
||||||
|
|
||||||
|
### 实施步骤
|
||||||
|
|
||||||
|
#### 阶段一:基础设施搭建
|
||||||
|
1. 创建 `optional-deps/` 目录结构
|
||||||
|
2. 生成 `optional-deps/package.json`(包含所有可选依赖)
|
||||||
|
3. 实现 `DependencyManager` 核心逻辑
|
||||||
|
4. 实现依赖安装锁机制
|
||||||
|
5. 编写单元测试
|
||||||
|
|
||||||
|
#### 阶段二:依赖迁移
|
||||||
|
6. 从主 `package.json` 移除可选依赖
|
||||||
|
7. 将依赖添加到 `optional-deps/package.json`
|
||||||
|
8. 创建依赖注册表,映射插件到依赖
|
||||||
|
|
||||||
|
#### 阶段三:插件改造
|
||||||
|
9. 创建 `import-helper.ts` 辅助函数
|
||||||
|
10. 逐步改造插件代码,使用 `importOptionalDep` 加载依赖
|
||||||
|
11. 优先改造重型依赖(AWS、阿里云、腾讯云等)
|
||||||
|
|
||||||
|
#### 阶段四:测试与优化
|
||||||
|
12. 端到端测试:验证依赖按需安装和加载
|
||||||
|
13. 性能优化:缓存已加载的模块
|
||||||
|
14. 错误处理:安装失败时的降级策略
|
||||||
|
15. 文档:编写使用说明和迁移指南
|
||||||
|
|
||||||
|
## 关键技术决策
|
||||||
|
|
||||||
|
### 1. 依赖分组策略
|
||||||
|
**选择:按插件分组**
|
||||||
|
- 每个插件声明自己需要的依赖
|
||||||
|
- 优点:职责清晰,易于维护
|
||||||
|
- 缺点:可能有重复依赖(但 pnpm 会去重)
|
||||||
|
|
||||||
|
**备选:按功能分组**
|
||||||
|
- 将依赖按功能分组(如 "aws-deps", "aliyun-deps")
|
||||||
|
- 优点:更细粒度控制
|
||||||
|
- 缺点:增加复杂度
|
||||||
|
|
||||||
|
### 2. 安装触发时机
|
||||||
|
**选择:首次使用时触发**
|
||||||
|
- 在插件的 `execute()` 或 `getClient()` 方法中触发安装
|
||||||
|
- 优点:真正的按需加载
|
||||||
|
- 缺点:首次使用有延迟
|
||||||
|
|
||||||
|
**备选:启动时预检查**
|
||||||
|
- 启动时扫描启用的插件,预安装依赖
|
||||||
|
- 优点:避免运行时延迟
|
||||||
|
- 缺点:可能安装不需要的依赖
|
||||||
|
|
||||||
|
### 3. 依赖路径解析
|
||||||
|
**选择:使用绝对路径 + `file://` 协议**
|
||||||
|
```typescript
|
||||||
|
const modulePath = path.resolve(__dirname, '../../optional-deps/node_modules', packageName);
|
||||||
|
return await import(`file://${modulePath}/index.js`);
|
||||||
|
```
|
||||||
|
|
||||||
|
**原因:**
|
||||||
|
- Node.js ESM 要求明确的 URL 格式
|
||||||
|
- 避免模块解析冲突
|
||||||
|
|
||||||
|
### 4. 并发控制
|
||||||
|
**选择:文件锁 + 内存锁双重保护**
|
||||||
|
- 使用 `proper-lockfile` 锁定 `optional-deps/` 目录
|
||||||
|
- 内存中使用 `Map` 记录正在安装的依赖
|
||||||
|
- 避免多个插件同时触发安装
|
||||||
|
|
||||||
|
### 5. 错误处理
|
||||||
|
**策略:**
|
||||||
|
- 安装失败时记录日志,抛出明确的错误信息
|
||||||
|
- 提供手动安装命令提示:`请运行: cd optional-deps && pnpm install`
|
||||||
|
- 支持降级:某些非核心依赖安装失败时,插件可以部分功能可用
|
||||||
|
|
||||||
|
## 验证方案
|
||||||
|
|
||||||
|
### 单元测试
|
||||||
|
1. 测试 `DependencyManager.isInstalled()` 正确检测依赖状态
|
||||||
|
2. 测试 `DependencyManager.installDependencies()` 成功安装依赖
|
||||||
|
3. 测试并发安装时的锁机制
|
||||||
|
4. 测试从 `optional-deps/node_modules` 加载模块
|
||||||
|
|
||||||
|
### 集成测试
|
||||||
|
1. 清空 `optional-deps/node_modules`
|
||||||
|
2. 启动服务,验证不触发安装
|
||||||
|
3. 调用 AWS 插件,验证触发安装并成功加载
|
||||||
|
4. 再次调用,验证不重复安装
|
||||||
|
5. 验证主 `node_modules` 体积减少
|
||||||
|
|
||||||
|
### 性能测试
|
||||||
|
1. 测量首次安装依赖的耗时
|
||||||
|
2. 测量后续加载的耗时(应该与正常 import 相近)
|
||||||
|
3. 对比改造前后的 `node_modules` 大小
|
||||||
|
|
||||||
|
## 风险与挑战
|
||||||
|
|
||||||
|
### 1. 首次使用延迟
|
||||||
|
**风险:** 用户首次使用插件时需要等待依赖安装(可能几十秒)
|
||||||
|
**缓解:**
|
||||||
|
- 在 UI 上显示安装进度
|
||||||
|
- 提供预安装命令:`pnpm run install-optional-deps`
|
||||||
|
- 文档说明首次使用会有延迟
|
||||||
|
|
||||||
|
### 2. 离线环境
|
||||||
|
**风险:** 离线环境无法下载依赖
|
||||||
|
**缓解:**
|
||||||
|
- 提供完整安装包(包含所有可选依赖)
|
||||||
|
- 支持手动复制 `node_modules`
|
||||||
|
|
||||||
|
### 3. 版本冲突
|
||||||
|
**风险:** 可选依赖与主依赖版本冲突
|
||||||
|
**缓解:**
|
||||||
|
- 使用 `--ignore-workspace` 隔离安装
|
||||||
|
- 定期同步主依赖版本
|
||||||
|
|
||||||
|
### 4. TypeScript 类型
|
||||||
|
**风险:** 动态导入的类型推断
|
||||||
|
**缓解:**
|
||||||
|
- 保留 `@types/*` 在主 `devDependencies`
|
||||||
|
- 使用泛型和类型断言
|
||||||
|
|
||||||
|
## 预期收益
|
||||||
|
|
||||||
|
1. **空间节省:** 主 `node_modules` 体积减少 60-70%(估算)
|
||||||
|
2. **安装速度:** 初始 `pnpm install` 速度提升 3-5 倍
|
||||||
|
3. **用户体验:** 不使用的插件不占用空间,按需加载
|
||||||
|
4. **维护性:** 依赖分组清晰,易于管理
|
||||||
|
|
||||||
|
## 后续优化
|
||||||
|
|
||||||
|
1. **依赖预热:** 在后台预安装常用插件依赖
|
||||||
|
2. **依赖缓存:** 支持从 CDN 或本地缓存安装
|
||||||
|
3. **依赖更新:** 提供命令批量更新可选依赖
|
||||||
|
4. **插件市场:** 支持从远程下载插件及其依赖配置
|
||||||
|
|
||||||
|
## 附录:依赖分类清单
|
||||||
|
|
||||||
|
### 可选依赖(迁移到 optional-deps/package.json)
|
||||||
|
|
||||||
|
**AWS 相关(plugin-aws, plugin-aws-cn):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@aws-sdk/client-acm": "^3.964.0",
|
||||||
|
"@aws-sdk/client-cloudfront": "^3.964.0",
|
||||||
|
"@aws-sdk/client-iam": "^3.964.0",
|
||||||
|
"@aws-sdk/client-route-53": "^3.964.0",
|
||||||
|
"@aws-sdk/client-s3": "^3.964.0",
|
||||||
|
"@aws-sdk/client-sts": "^3.990.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**阿里云相关(plugin-aliyun, plugin-lib/aliyun):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@alicloud/fc20230330": "^4.1.7",
|
||||||
|
"@alicloud/openapi-client": "^0.4.12",
|
||||||
|
"@alicloud/openapi-util": "^0.3.2",
|
||||||
|
"@alicloud/pop-core": "^1.7.10",
|
||||||
|
"@alicloud/sts-sdk": "^1.0.2",
|
||||||
|
"@alicloud/tea-typescript": "^1.8.0",
|
||||||
|
"@alicloud/tea-util": "^1.4.10",
|
||||||
|
"ali-oss": "^6.21.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**腾讯云相关(plugin-tencent, plugin-lib/tencent):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tencentcloud-sdk-nodejs": "^4.1.112",
|
||||||
|
"cos-nodejs-sdk-v5": "^2.14.6"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**华为云相关(plugin-huawei):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@huaweicloud/huaweicloud-sdk-cdn": "3.1.185",
|
||||||
|
"@huaweicloud/huaweicloud-sdk-core": "3.1.185",
|
||||||
|
"@huaweicloud/huaweicloud-sdk-elb": "3.1.185",
|
||||||
|
"@huaweicloud/huaweicloud-sdk-iam": "3.1.185",
|
||||||
|
"esdk-obs-nodejs": "^3.25.6"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Azure 相关(plugin-azure):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@azure/arm-dns": "^5.1.0",
|
||||||
|
"@azure/identity": "^4.13.1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Google Cloud 相关(plugin-google, plugin-cert/google):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@google-cloud/dns": "^5.3.1",
|
||||||
|
"@google-cloud/publicca": "^1.3.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**火山引擎相关(plugin-volcengine):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@volcengine/openapi": "^1.28.1",
|
||||||
|
"@volcengine/tos-sdk": "^2.9.1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**SSH/网络相关(plugin-host, plugin-lib/ssh):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ssh2": "^1.17.0",
|
||||||
|
"socks": "^2.8.3",
|
||||||
|
"socks-proxy-agent": "^8.0.4",
|
||||||
|
"basic-ftp": "^5.0.5"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**其他存储/传输(plugin-qiniu, plugin-lib/qiniu):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"qiniu": "^7.12.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**邮件通知(plugin-notification/email):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"nodemailer": "^6.9.16"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 主依赖(保留在主 package.json)
|
||||||
|
|
||||||
|
**框架核心:**
|
||||||
|
- `@midwayjs/*` 系列
|
||||||
|
- `@koa/cors`
|
||||||
|
- `typeorm`, `better-sqlite3`, `mysql2`, `pg`
|
||||||
|
|
||||||
|
**项目内部包:**
|
||||||
|
- `@certd/*` 系列
|
||||||
|
|
||||||
|
**通用工具:**
|
||||||
|
- `axios`, `lodash-es`, `dayjs`, `js-yaml`
|
||||||
|
- `crypto-js`, `jsonwebtoken`, `bcryptjs`
|
||||||
|
- `reflect-metadata`, `uuid`, `nanoid`
|
||||||
|
- 等等
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
本方案通过引入独立的可选依赖管理机制,实现了插件依赖的按需下载和加载。核心思路是:
|
||||||
|
|
||||||
|
1. **隔离管理:** 在 `optional-deps/` 目录下维护独立的 `package.json` 和 `node_modules`
|
||||||
|
2. **动态安装:** 通过 `DependencyManager` 在首次使用时触发 `pnpm install`
|
||||||
|
3. **路径加载:** 使用绝对路径从独立目录加载依赖模块
|
||||||
|
4. **最小改动:** 通过辅助函数 `importOptionalDep` 简化插件代码改造
|
||||||
|
|
||||||
|
该方案可以显著减少主 `node_modules` 体积,提升初始安装速度,同时保持现有架构的兼容性和可维护性。
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
你是一名资深nodejs工程师,擅长开发Certd开源系统的任务插件。
|
|
||||||
certd是一款全自动证书申请部署管理工具,基于流水线的方式,通过里面申请证书插件申请证书,然后将证书传递给下一个部署任务插件,不同的部署任务插件将证书部署到用户的各个应用系统当中。
|
|
||||||
|
|
||||||
certd插件分成以下几种类型:
|
|
||||||
Access:存储用户的第三放应用的授权数据,比如用户名密码,accessSecret 或 accessToken等。同时它里面的方法还负责对接第三方的api接口
|
|
||||||
Task: 部署任务插件,它继承AbstractTaskPlugin类,被流水线调用execute方法,将证书部署到对应的应用上
|
|
||||||
DnsProvider: DNS提供商插件,它用于在ACME申请证书时给域名添加txt解析记录。
|
|
||||||
|
|
||||||
注意事项:
|
|
||||||
1、使用技能:在开始工作前,请阅读并加载.trae/skills下面的技能,根据skills进行相应的插件开发
|
|
||||||
2、迭代技能:当开发过程用户提醒你更好的做法时,你需要总结经验,更新相应的skills,让skills越来越完善,能够在以后得新插件开发中具备指导意义。
|
|
||||||
3、一般调用的api接口文档会比较复杂,你不知道接口是什么时,请务必询问用户,让用户提供API接口文档
|
|
||||||
4、完成开发后无需测试,通知用户自己去测试
|
|
||||||
@@ -25,7 +25,7 @@ version: 1.0.0
|
|||||||
## 实现流程
|
## 实现流程
|
||||||
|
|
||||||
1. 先在 `packages/ui/certd-client/src/views` 下找 1-2 个相近 Fast Crud 页面,沿用它们的导入、布局、命名和权限写法。
|
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()`。
|
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` 等,接口分页参数和返回值按现有页面适配。
|
3. 在 `crud.tsx` 中配置 `request.pageRequest`、`columns`、`search`、`form`、`rowHandle`、`actionbar`、`toolbar` 等,接口分页参数和返回值按现有页面适配。
|
||||||
4. 操作按钮优先放在 Fast Crud 的 `rowHandle.buttons` 或 `actionbar.buttons` 中;审核、保存设置、批量操作等复杂交互可通过 `context` 调用 `index.vue` 中的方法。
|
4. 操作按钮优先放在 Fast Crud 的 `rowHandle.buttons` 或 `actionbar.buttons` 中;审核、保存设置、批量操作等复杂交互可通过 `context` 调用 `index.vue` 中的方法。
|
||||||
5. 金额、状态、时间、枚举等字段优先复用项目已有组件、字典和格式化工具;避免在模板里重复堆格式化逻辑。
|
5. 金额、状态、时间、枚举等字段优先复用项目已有组件、字典和格式化工具;避免在模板里重复堆格式化逻辑。
|
||||||
@@ -77,6 +77,84 @@ container:{}, //容器配置 ,对应fs-container
|
|||||||
- 有固定操作栏、统计区、说明区时,这些区域应 `flex: none`,把剩余空间交给表格区域。
|
- 有固定操作栏、统计区、说明区时,这些区域应 `flex: none`,把剩余空间交给表格区域。
|
||||||
- 修改嵌入式 Fast Crud 页面后,要检查空数据、少量数据和多页数据时表格高度、分页器和空状态是否仍在预期区域内。
|
- 修改嵌入式 Fast Crud 页面后,要检查空数据、少量数据和多页数据时表格高度、分页器和空状态是否仍在预期区域内。
|
||||||
|
|
||||||
|
## 列表导出
|
||||||
|
|
||||||
|
- 列表需要导出时,优先使用 Fast Crud 工具栏导出能力,不要另写一套导出按钮或后端接口,除非数据必须跨权限、跨分页或异步生成文件。
|
||||||
|
- 导出当前搜索条件下的数据时,在 `toolbar.export` 中设置 `dataFrom: "search"`,并显式打开导出按钮。
|
||||||
|
- 导出列必须输出 Excel 可读的纯文本或数字;不要直接导出对象、数组、VNode、进度条组件、开关组件、时间戳毫秒值等。
|
||||||
|
- 有隐藏但业务上需要导出的字段时,把字段定义为普通列并设置 `column.show: false`,再在 `columnFilter` 中对该字段返回 `true`。例如证书域名这类只用于导出的辅助列。
|
||||||
|
- 嵌套字段可以使用 `lastVars.certDomains` 这类 key;导出格式化时用安全取值函数读取嵌套值。
|
||||||
|
- `dataFormatter` 中统一格式化特殊字段:时间字段转 `YYYY-MM-DD HH:mm:ss`,日期类有效期转业务文案或 `YYYY-MM-DD`,枚举/开关转字典 label,数组转逗号分隔字符串,对象转明确的业务摘要。
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { ColumnProps, DataFormatterContext } from "@fast-crud/fast-crud";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
function getRecordValue(row: any, key: string) {
|
||||||
|
return key.split(".").reduce((target, item) => target?.[item], row);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatListValue(value: any) {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
return value.join(",");
|
||||||
|
}
|
||||||
|
return value ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportColumnFilter(col: ColumnProps) {
|
||||||
|
if (!col.key || ["_index", "_selection", "rowHandle"].includes(col.key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (col.key === "lastVars.certDomains") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return col.show !== false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportDataFormatter(opts: DataFormatterContext) {
|
||||||
|
const { row, originalRow, col, exportCol } = opts;
|
||||||
|
const key = col.key;
|
||||||
|
const value = getRecordValue(originalRow, key);
|
||||||
|
|
||||||
|
if (key === "lastVars.certDomains") {
|
||||||
|
row[key] = formatListValue(value);
|
||||||
|
} else if (key.includes("Time") && value) {
|
||||||
|
row[key] = dayjs(value).format("YYYY-MM-DD HH:mm:ss");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col.width) {
|
||||||
|
exportCol.width = col.width / 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
crudOptions: {
|
||||||
|
toolbar: {
|
||||||
|
buttons: {
|
||||||
|
export: { show: true },
|
||||||
|
},
|
||||||
|
export: {
|
||||||
|
dataFrom: "search",
|
||||||
|
columnFilter: exportColumnFilter,
|
||||||
|
dataFormatter: exportDataFormatter,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
"lastVars.certDomains": {
|
||||||
|
title: "证书域名",
|
||||||
|
type: "text",
|
||||||
|
column: {
|
||||||
|
show: false,
|
||||||
|
width: 260,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
form: { show: false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
## 内置 CRUD 按钮
|
## 内置 CRUD 按钮
|
||||||
|
|
||||||
只要在 `request` 中配置了 `addRequest`、`editRequest`、`delRequest`,Fast Crud 会自动在 `rowHandle` 渲染新增、编辑、删除按钮并完成对应操作,**不需要手写 `openDeleteConfirm`、`openEditDialog` 等方法**。
|
只要在 `request` 中配置了 `addRequest`、`editRequest`、`delRequest`,Fast Crud 会自动在 `rowHandle` 渲染新增、编辑、删除按钮并完成对应操作,**不需要手写 `openDeleteConfirm`、`openEditDialog` 等方法**。
|
||||||
|
|||||||
@@ -1,71 +1,210 @@
|
|||||||
# Certd 开发 Agent 上下文
|
# Certd 开发 Agent 上下文
|
||||||
|
|
||||||
这个文件是给在本仓库工作的开发 agent 看的常驻项目说明。进入仓库后先读本文,再按任务读取对应导航或规则文件,避免每次重新全量扫描项目。
|
进入仓库后先读本文。本文同时包含常驻规则、仓库地图、常用入口和验证命令;不要依赖分散规则文件。
|
||||||
|
|
||||||
仓库代码导航、目录地图、常用入口和参考文件见 `.codex/repo-map.md`。更细的开发规则拆在 `.codex/agent-rules/` 下;本文只保留最高优先级的规则、架构边界和工作方式。
|
|
||||||
|
|
||||||
## 项目定位
|
## 项目定位
|
||||||
|
|
||||||
Certd 是支持私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web 管理台和后端服务,用于证书申请、续期、部署、监控、通知和开放 API 集成。
|
Certd 是可私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web 管理台和后端服务,用于证书申请、续期、部署、监控、通知和开放 API 集成。
|
||||||
|
|
||||||
核心产品模型是“证书流水线”:
|
核心模型是“证书流水线”:
|
||||||
|
|
||||||
- 通过 ACME 申请证书
|
- 通过 ACME 申请证书。
|
||||||
- 使用 DNS-01、HTTP-01、CNAME 代理或服务商集成完成域名验证
|
- 完成 DNS-01、HTTP-01、CNAME 代理或服务商验证。
|
||||||
- 将证书转换或导出为 pem、pfx、der、jks、p7b 等格式
|
- 导出 pem、pfx、der、jks、p7b 等证书格式。
|
||||||
- 部署证书到主机、Nginx、Kubernetes、CDN、云厂商、面板等目标
|
- 部署到主机、Nginx、Kubernetes、CDN、云厂商、面板等目标。
|
||||||
- 通知用户,并监控站点证书过期时间
|
- 通知用户,并监控站点证书过期时间。
|
||||||
|
|
||||||
系统会保存证书、云厂商凭据、SSH 信息、API Key 等敏感数据,始终按私有化/本地部署产品处理,避免泄露本地数据和配置。
|
系统会保存证书、云厂商凭据、SSH 信息、API Key 等敏感数据。始终按私有化/本地部署产品处理,避免泄露本地数据和配置。
|
||||||
|
|
||||||
## 必读索引
|
|
||||||
|
|
||||||
- `.codex/repo-map.md`:仓库结构、后端/前端入口、流水线与插件地图、验证命令
|
|
||||||
- `.codex/agent-rules/backend.md`:后端、数据库迁移、文件上传、service/事务约定
|
|
||||||
- `.codex/agent-rules/frontend.md`:前端、Fast Crud、弹窗表单、格式化和禁跑命令
|
|
||||||
- `.codex/agent-rules/plugins.md`:流水线、插件归属、ACME/EAB、插件开发技能
|
|
||||||
- `.codex/agent-rules/testing.md`:测试优先策略、单测位置、ESM mock、聚焦验证
|
|
||||||
- `.codex/agent-rules/coding-style.md`:注释、可读性、DRY、单一职责等通用代码风格
|
|
||||||
|
|
||||||
## 仓库边界
|
## 仓库边界
|
||||||
|
|
||||||
这是一个 pnpm + lerna 的 monorepo。核心定位:
|
- 根仓库是 pnpm + lerna monorepo;不要引入 npm/yarn lockfile。
|
||||||
|
- `packages/pro/` 是独立 Git 工作区;修改商业版代码后,必须在 `packages/pro` 内单独执行 `git status` / `git diff`。
|
||||||
- `packages/ui/certd-server`:后端服务
|
|
||||||
- `packages/ui/certd-client`:前端 Web 管理台
|
|
||||||
- `packages/core/pipeline`:流水线核心
|
|
||||||
- `packages/core/acme-client`:ACME 协议客户端
|
|
||||||
- `packages/plugins/plugin-lib`:通用插件辅助能力和证书相关共享代码
|
|
||||||
|
|
||||||
`packages/pro/` 是独立 Git 工作区,使用 `packages/pro/.git` 管理。根仓库的 `git status` / `git diff` 默认看不到这里的实际改动;修改商业版代码后,要在 `packages/pro` 目录内单独执行 `git status` / `git diff` 检查。
|
|
||||||
|
|
||||||
## 硬性规则
|
|
||||||
|
|
||||||
- 根包管理器是 pnpm,不要引入 npm/yarn lockfile。
|
|
||||||
- 不要主动运行 `pnpm install`;用户会事先准备好 `node_modules`。如果 `pnpm install` 或测试因缺少依赖、TTY、网络问题失败,停止尝试并告知用户环境问题。
|
|
||||||
- 前端不要运行 `pnpm tsc` / `vue-tsc`;当前依赖组合中 `vue-tsc@1.8.27` 会抛无效内部错误。前端 `test:unit` 只是占位脚本。
|
|
||||||
- 不要把 `packages/ui/certd-server/data/`、`logs/`、生成的 metadata/dist 等运行时或构建产物纳入改动,除非任务明确要求。
|
- 不要把 `packages/ui/certd-server/data/`、`logs/`、生成的 metadata/dist 等运行时或构建产物纳入改动,除非任务明确要求。
|
||||||
- 做数据库结构变更时,添加或更新迁移脚本,不要依赖 TypeORM 自动同步。
|
- 例外:分析插件动态依赖时,可以只读查看后端数据目录下的 `./data/.runtime-deps`;该目录用于 runtime-deps 动态安装第三方 SDK,不应纳入提交。
|
||||||
- 做插件相关任务时,先读取对应 `.trae/skills/<skill>/SKILL.md`,再进入具体实现。
|
|
||||||
- 后端 service 拼接可选 `projectId` 查询条件时,不要直接写 `{ userId, projectId }`;应使用 `BaseService.buildUserProjectQuery(userId, projectId)`,只有 `projectId != null` 时才加入查询条件。
|
|
||||||
|
|
||||||
## 工作方式
|
核心包:
|
||||||
|
|
||||||
- 先读本文;需要代码导航、目录入口、参考文件或验证命令时读 `.codex/repo-map.md`。
|
- `packages/ui/certd-server`:后端服务。
|
||||||
- 任务涉及后端、前端、插件、测试或代码风格时,先读取 `.codex/agent-rules/` 下对应规则文件,再查看具体代码。
|
- `packages/ui/certd-client`:前端 Web 管理台。
|
||||||
- 在 PowerShell 中读取中文、Markdown、locale、文档类文件时,显式使用 `Get-Content -Encoding utf8`;如果仍乱码,再执行 `[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()` 后重试。
|
- `packages/core/pipeline`:流水线核心。
|
||||||
|
- `packages/core/acme-client`:ACME 客户端。
|
||||||
|
- `packages/plugins/plugin-lib`:插件共享能力。
|
||||||
|
|
||||||
|
## 仓库地图
|
||||||
|
|
||||||
|
- `package.json`:根脚本与 workspace 元信息。
|
||||||
|
- `pnpm-workspace.yaml`:workspace 匹配规则,包含 `packages/**`、`packages/ui/**`。
|
||||||
|
- `lerna.json`:lerna-lite 配置。
|
||||||
|
- `docs`:VitePress 文档站。
|
||||||
|
- `docker`:Docker 安装和运行相关文件。
|
||||||
|
- `packages/core/acme-client`:ACME 协议客户端。
|
||||||
|
- `packages/core/basic`:共享基础工具。
|
||||||
|
- `packages/core/pipeline`:流水线核心抽象、插件模型、执行上下文。
|
||||||
|
- `packages/libs`:共享集成库。
|
||||||
|
- `packages/plugins/plugin-lib`:证书、DNS Provider、格式转换等插件共享能力。
|
||||||
|
- `packages/plugins/plugin-cert`:证书插件包入口。
|
||||||
|
- `packages/ui/certd-server`:后端 Midway 服务。
|
||||||
|
- `packages/ui/certd-client`:前端 Vue/Vite 管理台。
|
||||||
|
- `packages/pro`:商业版独立 Git 工作区,需在该目录内单独检查状态。
|
||||||
|
|
||||||
|
常见运行时或生成产物:
|
||||||
|
|
||||||
|
- 根目录:`node_modules`、`logs`、`output`、`lerna-debug.log`、`tmp-certd-client-vite*.log`。
|
||||||
|
- 后端:`packages/ui/certd-server/data`、`packages/ui/certd-server/logs`。
|
||||||
|
- 后端动态依赖:`./data/.runtime-deps`,常见于阿里云 SDK、腾讯云 SDK 等插件第三方依赖。
|
||||||
|
- 各包:`dist`。
|
||||||
|
- 插件:metadata/yaml 导出结果。
|
||||||
|
|
||||||
|
## 常用验证
|
||||||
|
|
||||||
|
- 后端聚焦单测:`corepack pnpm --dir packages\ui\certd-server test:unit`。
|
||||||
|
- 后端完整测试:`corepack pnpm --dir packages\ui\certd-server test`。
|
||||||
|
- 前端构建:`corepack pnpm --dir packages\ui\certd-client build`。
|
||||||
|
- 前端改动文件格式化:`packages\ui\certd-client\node_modules\.bin\prettier.cmd --write <files>`。
|
||||||
|
- 前端改动文件 ESLint 修复:`packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix <files>`。
|
||||||
|
- 后端改动文件 lint fix:`corepack pnpm --dir packages\ui\certd-server run lint`。
|
||||||
|
- 其他package lint fix:`corepack pnpm --dir packages\xxx\xxxx run lint`。
|
||||||
|
|
||||||
|
## 通用工作规则
|
||||||
|
|
||||||
|
- 先读本文,再按任务读取具体代码或技能文件。
|
||||||
|
- PowerShell 读取中文、Markdown、locale、文档类文件时使用 `Get-Content -Raw -Encoding UTF8`;仍乱码时先执行 `[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()`。
|
||||||
|
- PowerShell 中用 `rg` 搜索含引号、括号、反斜杠的 pattern 时,优先用单引号包裹整个 pattern,例如 `rg 'await import\("tencentcloud-sdk-nodejs' packages/ui/certd-server/src -g '*.ts'`。
|
||||||
|
- 不要主动运行 `pnpm install`;缺依赖、TTY、网络导致安装或测试失败时,停止尝试并说明环境问题。
|
||||||
|
- 优先沿用现有模块、插件、service、页面模式;不要为形式上的复用制造过度抽象。
|
||||||
|
- 代码可读性优先于短写法。复杂条件、三元表达式、链式调用、内联对象和多层 helper 调用要拆成命名清晰的中间变量或小方法。
|
||||||
|
- 方法调用链不要直接塞进另一个方法参数;先用有意义的局部变量承接返回值,再传入下一步。
|
||||||
|
- 注释优先使用中文,尤其是业务规则、兼容逻辑、协议细节和隐藏风险;文件已有英文风格或引用外部术语时可保持一致。
|
||||||
|
- 遵守 DRY 和单一职责;第三次出现的业务规则、字段转换、权限判断、Repository 选择、事务传播、金额计算等逻辑,应优先抽成合适 helper 或 service 方法。
|
||||||
|
|
||||||
|
## 后端规则
|
||||||
|
|
||||||
|
- 后端主包是 `packages/ui/certd-server`,使用 Node.js、ESM、TypeScript、MidwayJS 3、Koa、TypeORM 和 SQL 迁移。
|
||||||
- 做后端任务时,先定位 `packages/ui/certd-server/src/modules` 下的模块,以及相关 entity/service/controller。
|
- 做后端任务时,先定位 `packages/ui/certd-server/src/modules` 下的模块,以及相关 entity/service/controller。
|
||||||
|
- 表结构变更必须添加或更新 `packages/ui/certd-server/db/migration/*.sql`;不要依赖 TypeORM 自动同步。
|
||||||
|
- 文件上传接口 `/basic/file/upload` 返回临时 key;业务保存前必须调用 `FileService.saveFile(userId, key, "public" | "private")` 转成永久 key,不能直接保存 `tmpfile_key_...`。
|
||||||
|
- 方法参数超过 3 个时,优先改为对象参数。
|
||||||
|
- 事务链路方法统一用 `method(ctx, req)`,`ctx` 放第一位并承载 `manager?: EntityManager`,业务参数放 `req` 对象。
|
||||||
|
- 只有需要事务传播时才定义 `ctx`;普通查询、纯函数和简单私有方法继续使用明确参数。
|
||||||
|
- 需要按事务上下文取 Repository 时,用 `BaseService.getRepo(ctx, EntityClass)`。
|
||||||
|
- 需要“有事务则复用、无事务则开启”时,用 `BaseService.transactionWithCtx(ctx, callback)`。
|
||||||
|
- 拼接可选 `projectId` 查询条件时,用 `BaseService.buildUserProjectQuery(userId, projectId)`;不要直接写 `{ userId, projectId }`。
|
||||||
|
- `ctx` 类型复用 `BaseService` 导出的 `ServiceContext`。
|
||||||
|
- 新增 service 方法避免与 `BaseService` 方法签名冲突,例如不要用 `delete(id)` 覆盖 `delete(ids, where?)`;改用 `deleteById` 等具体名称。
|
||||||
|
|
||||||
|
### 后端地图
|
||||||
|
|
||||||
|
- `packages/ui/certd-server/bootstrap.js`:Midway 启动入口,使用 `@midwayjs/bootstrap`。
|
||||||
|
- `packages/ui/certd-server/src/configuration.ts`:Midway 主配置,注册组件和全局中间件。
|
||||||
|
- `packages/ui/certd-server/src/config/config.default.ts`:端口、HTTPS、静态文件、cron、TypeORM、Flyway、上传、JWT、Swagger 默认配置。
|
||||||
|
- `packages/ui/certd-server/src/config/loader.ts`:读取 `.env`、`.env.<env>.yaml`,支持 `certd_` 前缀环境变量覆盖嵌套配置。
|
||||||
|
- `packages/ui/certd-server/src/modules`:业务模块根目录,常见模块包括:
|
||||||
|
- `basic`
|
||||||
|
- `cert`
|
||||||
|
- `cname`
|
||||||
|
- `cron`
|
||||||
|
- `login`
|
||||||
|
- `monitor`
|
||||||
|
- `open`
|
||||||
|
- `pipeline`
|
||||||
|
- `plugin`
|
||||||
|
- `suite`
|
||||||
|
- `sys`
|
||||||
|
- `packages/ui/certd-server/src/controller`:API 入口,按 `basic`、`user`、`sys`、`openapi` 分组。
|
||||||
|
- `packages/ui/certd-server/db/migration`:SQL 迁移目录;TypeORM `synchronize: false`,表结构变更必须配套迁移 SQL。
|
||||||
|
- 后端测试使用 Mocha + Node `assert/strict`;纯单测放在 `src/**/*.test.ts`,可参考 `src/utils/random.test.ts`、`src/controller/basic/app-controller.test.ts`、`src/modules/pipeline/service/pipeline-service.test.ts`。
|
||||||
|
|
||||||
|
## 前端规则
|
||||||
|
|
||||||
|
- 前端主包是 `packages/ui/certd-client`,使用 Vue 3、Vite、TypeScript、Ant Design Vue、Fast Crud、Pinia、vue-router、vue-i18n。
|
||||||
- 做前端任务时,先定位 `packages/ui/certd-client/src/views/certd` 下的页面,再找对应 `src/api`。
|
- 做前端任务时,先定位 `packages/ui/certd-client/src/views/certd` 下的页面,再找对应 `src/api`。
|
||||||
- 做服务商、DNS、部署、通知相关任务时,先看 `packages/ui/certd-server/src/plugins`,再看 `packages/plugins/plugin-lib` 里的共享辅助能力。
|
- 不要运行前端 `pnpm tsc` / `vue-tsc`;当前 `vue-tsc@1.8.27` 会抛无效内部错误。前端 `test:unit` 只是占位脚本,也不要跑。
|
||||||
- 优先沿用现有模块、插件、服务模式,再考虑新增抽象;避免为了形式上的“复用”制造过度设计。
|
- 前端 TS/Vue/locale 改动后,只对本次改动文件运行现有 Prettier / ESLint:`packages\ui\certd-client\node_modules\.bin\prettier.cmd --write <files>` 和 `packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix <files>`。
|
||||||
- 实现新功能或修复行为缺陷前,优先补对应单元测试并确认红灯,再实现代码并跑聚焦验证。确实不适合先写测试时,在回复中说明原因和替代验证方式。
|
- 列表管理、后台管理、记录查询、CRUD 表格页面优先使用 Fast Crud;开发或重构前读 `.trae/skills/fast-crud-page-dev/SKILL.md`。
|
||||||
- 后补单元测试时,先按正确行为写预期;如果红灯需要修改既有实现,先向用户确认这是 bug 还是既有需求,避免未经确认改变行为。
|
- 只有轻量只读展示、强交互自定义界面或既有页面模式明显不适合 Fast Crud 时,才手写 `a-table` / 自定义列表,并在回复中说明。
|
||||||
|
- 内嵌 Fast Crud 时,外层必须有稳定高度或完整 `flex: 1; min-height: 0` 链路。
|
||||||
|
- 后台管理列表展示或筛选用户字段时,优先参考 `packages/ui/certd-client/src/views/sys/suite/user-suite/crud.tsx` 的 `userId` 字段模式,用 `table-select` + `/sys/authority/user/getSimpleUserByIds` 字典回显和搜索。
|
||||||
|
- 对话框里只做确认可用 `Modal.confirm`;有字段输入、表单校验或提交字段时,必须用 `useFormDialog` / `openFormDialog`。
|
||||||
|
|
||||||
|
### 前端地图
|
||||||
|
|
||||||
|
- `packages/ui/certd-client/vite.config.ts`:Vite 配置。
|
||||||
|
- dev 端口:`3008`
|
||||||
|
- 代理路径:`/api`、`/certd/api`
|
||||||
|
- 代理目标:`127.0.0.1:7001`
|
||||||
|
- `packages/ui/certd-client/src/main.ts`:Vue 启动入口,注册 AntDV、Vben、router、全局组件、插件和偏好设置。
|
||||||
|
- `packages/ui/certd-client/src/App.vue`:根组件,包含 `AConfigProvider`、`FsFormProvider`、`router-view`。
|
||||||
|
- `packages/ui/certd-client/src/router/index.ts`、`src/router/resolve.ts`:路由入口,使用 `createWebHashHistory`。
|
||||||
|
- `packages/ui/certd-client/src/router/source/modules/certd.ts`:Certd 主业务路由。
|
||||||
|
- `packages/ui/certd-client/src/store`:Pinia store,主要包括:
|
||||||
|
- `user`
|
||||||
|
- `project`
|
||||||
|
- `settings`
|
||||||
|
- `plugin`
|
||||||
|
- `packages/ui/certd-client/src/api/service.ts`:Axios 封装。
|
||||||
|
- `packages/ui/certd-client/src/api/tools.ts`:错误与响应工具。
|
||||||
|
- `packages/ui/certd-client/src/views/certd`:核心业务视图,常见目录包括:
|
||||||
|
- `pipeline`
|
||||||
|
- `cert`
|
||||||
|
- `monitor`
|
||||||
|
- `access`
|
||||||
|
- `notification`
|
||||||
|
- `open`
|
||||||
|
- `project`
|
||||||
|
- `suite`
|
||||||
|
- `wallet`
|
||||||
|
- `packages/ui/certd-client/src/locales`:国际化入口与语言包。
|
||||||
|
- Fast Crud 页面常见拆分是 `api.ts`、`crud.tsx`、`index.vue`;可参考 `src/views/certd/access`、`src/views/sys/suite/user-suite/crud.tsx`、`src/views/certd/wallet/index.vue`。
|
||||||
|
|
||||||
|
## 流水线与插件规则
|
||||||
|
|
||||||
|
- 插件是核心能力。新增服务商、DNS 验证、证书部署、通知方式,通常放到插件包或 `packages/ui/certd-server/src/plugins/<plugin-name>/`。
|
||||||
|
- 做服务商、DNS、部署、通知相关任务时,先看 `packages/ui/certd-server/src/plugins`,再看 `packages/plugins/plugin-lib`。
|
||||||
|
- 插件依赖的第三方 SDK 可能通过 runtime-deps 动态安装到后端运行目录 `./data/.runtime-deps`。分析阿里云、腾讯云等 SDK 行为时,需要进入该目录阅读实际安装版本代码。
|
||||||
|
- 修改证书申请、验证、部署或通知行为时,先判断归属:ACME client、pipeline 核心、后端 module/service/entity/controller、具体插件、前端 view/form/schema。
|
||||||
|
- 单个服务商或部署目标的问题,不要轻易修改共享 pipeline/core;只有可复用公共语义或跨插件一致行为才上移到 `packages/core/pipeline` 或 `packages/plugins/plugin-lib`。
|
||||||
|
- ACME / EAB:公共 EAB 可能只能创建一次账号;跨用户复用公共 EAB 时,应保存并复用同一个 ACME account private key。
|
||||||
|
- `newAccount({ onlyReturnExisting: true })` 可用同一个 account private key 取回已创建账号 URL,且不会再次消费 EAB。
|
||||||
|
- 修改 EAB `kid` 后,应重新生成绑定该 `kid` 的 account private key;否则应阻止继续申请并提示刷新账号私钥。
|
||||||
|
- 插件开发前先读对应技能:`.trae/skills/dns-provider-dev/SKILL.md`、`.trae/skills/task-plugin-dev/SKILL.md`、`.trae/skills/access-plugin-dev/SKILL.md`、`.trae/skills/plugin-converter/SKILL.md`。
|
||||||
|
- `.codex/skills` 是指向 `.trae/skills` 的目录链接;更新技能只维护 `.trae/skills`,不要复制第二份。
|
||||||
|
|
||||||
|
### 流水线与插件地图
|
||||||
|
|
||||||
|
- `packages/core/pipeline/src/index.ts`:核心导出入口,导出 `core`、`dt`、`access`、`registry`、`plugin`、`context`、`decorator`、`service`、`notification`。
|
||||||
|
- `packages/core/pipeline/src/plugin`:任务插件抽象,主要包括:
|
||||||
|
- `AbstractTaskPlugin`
|
||||||
|
- `IsTaskPlugin`
|
||||||
|
- `TaskInput`
|
||||||
|
- `pluginRegistry`
|
||||||
|
- `packages/core/pipeline/src/access`:授权插件抽象,主要包括:
|
||||||
|
- `BaseAccess`
|
||||||
|
- `IsAccess`
|
||||||
|
- `AccessInput`
|
||||||
|
- `accessRegistry`
|
||||||
|
- `packages/core/pipeline/src/dt/pipeline.ts`:`Pipeline`、`Stage`、`Task`、`RunStrategy` 等流水线数据结构。
|
||||||
|
- `packages/core/pipeline/src/core`:执行器、上下文、运行历史、文件存储等。
|
||||||
|
- `packages/core/pipeline/src/service`:CNAME、事件、配置、邮件、URL 等 pipeline service 接口。
|
||||||
|
- `packages/ui/certd-server/src/plugins`:后端内置服务商、DNS、部署、通知等插件。
|
||||||
|
- `packages/ui/certd-server/src/plugins/plugin-cert`:证书申请核心插件。
|
||||||
|
- `packages/ui/certd-server/src/plugins/plugin-lib`:后端插件 helper/access。
|
||||||
|
- `packages/plugins/plugin-lib/src/cert`:`CertReader`、`CertConverter`、DNS Provider 公共能力。
|
||||||
|
- `packages/plugins/plugin-lib/src/cert/dns-provider`:`AbstractDnsProvider`、`dnsProviderRegistry`、`DomainParser`。
|
||||||
|
- ACME 协议、EAB、账号、订单、挑战流程:优先看 `packages/core/acme-client` 或 `packages/ui/certd-server/src/plugins/plugin-cert/plugin/cert-plugin/acme.ts`。
|
||||||
|
- 流水线执行、任务生命周期、输入输出、注册机制:看 `packages/core/pipeline`。
|
||||||
|
- 单个云厂商 DNS 验证、证书部署、API 调用失败:改对应 `packages/ui/certd-server/src/plugins/plugin-xxx`。
|
||||||
|
- 通用证书读取、DNS Provider 公共能力、格式转换:改 `packages/plugins/plugin-lib`。
|
||||||
|
- 后端业务数据、接口、实体、权限、迁移:改 `packages/ui/certd-server/src/modules` 与 `src/controller`。
|
||||||
|
- 表单、列表、插件配置 UI:改 `packages/ui/certd-client/src/views/certd` 及对应 `src/api`。
|
||||||
|
|
||||||
|
## 测试与验证
|
||||||
|
|
||||||
|
- 实现新功能或修复行为缺陷前,优先补单元测试并先确认红灯,再实现并跑聚焦验证。
|
||||||
|
- 确实不适合先写测试时,在回复中说明原因和替代验证方式。
|
||||||
|
- 后补单元测试时,按正确行为写预期;若红灯需要修改既有实现,先向用户确认这是 bug 还是既有需求,避免未经确认改变行为。
|
||||||
|
- 后端纯单测放在 `src/**/*.test.ts`,尽量与被测文件相邻;`test:unit` 只跑这些文件,构建/打包应排除 `*.test.ts`。
|
||||||
|
- 单测需要 mock ESM 静态 import 时,优先使用 `esmock`,不要为了测试改业务代码结构。
|
||||||
|
- 各包 `test:unit` 脚本应显式设置 `NODE_ENV=unittest`。
|
||||||
|
- 单包单测优先用 `corepack pnpm --dir <包目录> test:unit`,例如 `corepack pnpm --dir packages\ui\certd-server test:unit`。
|
||||||
- 优先对改动包运行聚焦测试或格式化/ESLint;只有跨包影响明显时再考虑更大范围构建。
|
- 优先对改动包运行聚焦测试或格式化/ESLint;只有跨包影响明显时再考虑更大范围构建。
|
||||||
|
|
||||||
## 架构边界
|
|
||||||
|
|
||||||
插件是核心能力,不是边缘功能。新增服务商、DNS 验证、证书部署、通知方式等能力,通常应该放在插件包里,或放在 `packages/ui/certd-server/src/plugins/<plugin-name>/` 下。
|
|
||||||
|
|
||||||
修改证书申请、验证、部署或通知行为时,先判断改动属于 ACME client、pipeline 核心抽象、后端 module/service/entity/controller、具体插件实现,还是前端 view/form/schema。
|
|
||||||
|
|
||||||
如果只是某个服务商或部署目标的问题,不要轻易修改共享 pipeline/core 行为,除非确实是可复用的公共能力。
|
|
||||||
|
|||||||
@@ -3,6 +3,53 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **plugin:** 增加 Dynadot DNS and access 插件 ([a3a215b](https://github.com/certd/certd/commit/a3a215b7ae2b90efcde91270ce4165bbfe77dc64))
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复litessl无法申请证书,报authorization must be pending 错误的问题 ([d6cd9d1](https://github.com/certd/certd/commit/d6cd9d136d2812b2335917305f36d6d9414507ad))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 首页夜间模式主图切换为黑色背景 ([15484bc](https://github.com/certd/certd/commit/15484bc119fef7a0ca7f3fdab01d665fde47e688))
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **cert-plugin:** 修复DNS提供商授权无法回显的bug ([016ae86](https://github.com/certd/certd/commit/016ae865b1d914fe5792e77a08e3ab5358df5f89))
|
||||||
|
* Parse PEM chain and import certificate chain ([#747](https://github.com/certd/certd/issues/747)) ([454912d](https://github.com/certd/certd/commit/454912d31407d350cbd170953ccbd0564e74fd6c))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 添加AWS Rate Limit应对措施 ([#748](https://github.com/certd/certd/issues/748)) ([56b8c68](https://github.com/certd/certd/commit/56b8c689ec2b5cff49010a8c765483dd36803e9d))
|
||||||
|
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
|
||||||
|
* 优化 HiPM DNSMgr 插件,添加域名查询双层策略 ([#744](https://github.com/certd/certd/issues/744)) @WUHINS ([0f3f851](https://github.com/certd/certd/commit/0f3f8519e04d95cb848e28b98a3d4fcbed481fce))
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "perf: 添加AWS Rate Limit应对措施 (#748)" (#749) ([5e8bdac](https://github.com/certd/certd/commit/5e8bdac00850bed4f5f2a272bee42c490730ec21)), closes [#748](https://github.com/certd/certd/issues/748) [#749](https://github.com/certd/certd/issues/749)
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 流水线、监控站点支持导出 ([99fd308](https://github.com/certd/certd/commit/99fd3083f259cdb96fd656f04858dd708d1251c7))
|
||||||
|
* 优化列表页面请求两次的问题 ([5546af5](https://github.com/certd/certd/commit/5546af518e92c765513787ccaf8e856be789bcf9))
|
||||||
|
* 优化邀请注册流程 ([7a71e45](https://github.com/certd/certd/commit/7a71e45799d782d0691606fb42b4236f1d3009b0))
|
||||||
|
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
|
||||||
|
* **volcengine-vke:** 火山VKE集群证书支持两种类型的证书保密字典 ([77b8024](https://github.com/certd/certd/commit/77b802445322d576d54d194f7c505da49e0e824c))
|
||||||
|
|
||||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -179,7 +179,10 @@ https://certd.handfree.work/
|
|||||||
2. 您的需求我们将优先实现,并且可能将作为专业版功能提供
|
2. 您的需求我们将优先实现,并且可能将作为专业版功能提供
|
||||||
3. 获得专业版功能
|
3. 获得专业版功能
|
||||||
|
|
||||||
[50元专业版优惠券限时领取](https://app.handfree.work/subject/#/app/certd/product)
|
|
||||||
|
> [50元专业版优惠券限时领取](https://app.handfree.work/subject/#/app/certd/product) https://app.handfree.work/subject/#/app/certd/product
|
||||||
|
> app.handfree.work是Certd官方激活码购买平台
|
||||||
|
|
||||||
|
|
||||||
专业版、商业版特权对比
|
专业版、商业版特权对比
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,78 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **plugin:** 增加 Dynadot DNS and access 插件 ([a3a215b](https://github.com/certd/certd/commit/a3a215b7ae2b90efcde91270ce4165bbfe77dc64))
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复litessl无法申请证书,报authorization must be pending 错误的问题 ([d6cd9d1](https://github.com/certd/certd/commit/d6cd9d136d2812b2335917305f36d6d9414507ad))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 首页夜间模式主图切换为黑色背景 ([15484bc](https://github.com/certd/certd/commit/15484bc119fef7a0ca7f3fdab01d665fde47e688))
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **cert-plugin:** 修复DNS提供商授权无法回显的bug ([016ae86](https://github.com/certd/certd/commit/016ae865b1d914fe5792e77a08e3ab5358df5f89))
|
||||||
|
* Parse PEM chain and import certificate chain ([#747](https://github.com/certd/certd/issues/747)) ([454912d](https://github.com/certd/certd/commit/454912d31407d350cbd170953ccbd0564e74fd6c))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 添加AWS Rate Limit应对措施 ([#748](https://github.com/certd/certd/issues/748)) ([56b8c68](https://github.com/certd/certd/commit/56b8c689ec2b5cff49010a8c765483dd36803e9d))
|
||||||
|
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
|
||||||
|
* 优化 HiPM DNSMgr 插件,添加域名查询双层策略 ([#744](https://github.com/certd/certd/issues/744)) @WUHINS ([0f3f851](https://github.com/certd/certd/commit/0f3f8519e04d95cb848e28b98a3d4fcbed481fce))
|
||||||
|
|
||||||
|
### Reverts
|
||||||
|
|
||||||
|
* Revert "perf: 添加AWS Rate Limit应对措施 (#748)" (#749) ([5e8bdac](https://github.com/certd/certd/commit/5e8bdac00850bed4f5f2a272bee42c490730ec21)), closes [#748](https://github.com/certd/certd/issues/748) [#749](https://github.com/certd/certd/issues/749)
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 流水线、监控站点支持导出 ([99fd308](https://github.com/certd/certd/commit/99fd3083f259cdb96fd656f04858dd708d1251c7))
|
||||||
|
* 优化列表页面请求两次的问题 ([5546af5](https://github.com/certd/certd/commit/5546af518e92c765513787ccaf8e856be789bcf9))
|
||||||
|
* 优化邀请注册流程 ([7a71e45](https://github.com/certd/certd/commit/7a71e45799d782d0691606fb42b4236f1d3009b0))
|
||||||
|
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
|
||||||
|
* **volcengine-vke:** 火山VKE集群证书支持两种类型的证书保密字典 ([77b8024](https://github.com/certd/certd/commit/77b802445322d576d54d194f7c505da49e0e824c))
|
||||||
|
|
||||||
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复阿里云证书订单orderid 选择出错的问题 ([41254d1](https://github.com/certd/certd/commit/41254d10b748a2d3e6ba43c7e11411650c748d1b))
|
||||||
|
* **monitor:** 修复开放接口自动创建证书流水线重复触发和等待时间不足的问题 ([91d5c90](https://github.com/certd/certd/commit/91d5c90eb0eaf65c81dddbd2d4d4b404cb8b4d07))
|
||||||
|
* **pipeline:** 修复批量随机修改定时没有生效的bug ([2e19dda](https://github.com/certd/certd/commit/2e19dda72e70b525a7c269e18e963a5ee602f59f))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* 商业版支持邀请返佣功能 ([f9a310b](https://github.com/certd/certd/commit/f9a310b6c3bbf30f221482a0c59e9c30080bdfc8))
|
||||||
|
* 商业版支持邀请推广功能 ([f1d2a10](https://github.com/certd/certd/commit/f1d2a1033a0f8d3dbd91fc9793e07bd0b858b539))
|
||||||
|
* 新增管理员针对用户流水线和证书监控管理功能 ([0211552](https://github.com/certd/certd/commit/021155278e7375f8487b0531ed1b5ad52512f007))
|
||||||
|
* 新增套餐激活码功能,通过CDK兑换套餐 ([81d6289](https://github.com/certd/certd/commit/81d6289a8631b073b49f24dee4b14bb1c8f31071))
|
||||||
|
* 新增推广等级激励功能 ([5096df5](https://github.com/certd/certd/commit/5096df5cc0d8f0ad8aa327b8e2a900ba23714bd8))
|
||||||
|
* 新增证书申请参数模版管理,开放接口支持使用证书参数模版和指定证书申请参数 ([f8b71a0](https://github.com/certd/certd/commit/f8b71a0e612fad527cf49136335e0b46f0f379cd))
|
||||||
|
* 支持dns-persist-01持久化验证方式申请证书,优化Acme账号的存储方式 ([67b05e2](https://github.com/certd/certd/commit/67b05e2d75e96b9f855e1ca0b3d0d8d03b92d8e6))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 插件全局配置支持下拉选项自定义映射功能 ([c637985](https://github.com/certd/certd/commit/c637985575b09196b04cce37ac14fbe68c029bde))
|
||||||
|
* 商业版提现增加收款二维码上传 ([83a5a21](https://github.com/certd/certd/commit/83a5a21f956e50942541f1532f3a8dcaa5821d34))
|
||||||
|
* **aliyun-apig:** 优化阿里云API网关部署插件的查询及翻页 ([3e4b7f3](https://github.com/certd/certd/commit/3e4b7f30ac6f3c976c8274bdf256c69b8a2c46db))
|
||||||
|
* **trade:** 优化商品购买页面的规格展示和折扣计算,支持订单取消 ([6624769](https://github.com/certd/certd/commit/66247690326ce2789900fc9110c08b3502cea655))
|
||||||
|
|
||||||
## [1.40.5](https://github.com/certd/certd/compare/v1.40.4...v1.40.5) (2026-05-26)
|
## [1.40.5](https://github.com/certd/certd/compare/v1.40.4...v1.40.5) (2026-05-26)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ Certd 是一款开源、免费、全自动申请和部署更新SSL证书的工
|
|||||||
关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具
|
关键字:证书自动申请、证书自动更新、证书自动续期、证书自动续签、证书管理工具
|
||||||
|
|
||||||
|
|
||||||
| 官方开源地址: | |
|
| |官方开源地址: |
|
||||||
| ---- | ---- |
|
| ---- | ---- |
|
||||||
| [Github](https://github.com/certd/certd)|  |
|
| [Github](https://github.com/certd/certd)|  |
|
||||||
| [Gitee](https://gitee.com/certd/certd) |  |
|
| [Gitee](https://gitee.com/certd/certd) |  |
|
||||||
|
|||||||
@@ -33,53 +33,54 @@
|
|||||||
| 29.| **彩虹DNS** | 彩虹DNS管理系统授权 |
|
| 29.| **彩虹DNS** | 彩虹DNS管理系统授权 |
|
||||||
| 30.| **多吉云** | |
|
| 30.| **多吉云** | |
|
||||||
| 31.| **Dokploy授权** | |
|
| 31.| **Dokploy授权** | |
|
||||||
| 32.| **farcdn授权** | |
|
| 32.| **Dynadot授权** | |
|
||||||
| 33.| **FlexCDN授权** | |
|
| 33.| **farcdn授权** | |
|
||||||
| 34.| **Gcore** | Gcore |
|
| 34.| **FlexCDN授权** | |
|
||||||
| 35.| **Github授权** | |
|
| 35.| **Gcore** | Gcore |
|
||||||
| 36.| **godaddy授权** | |
|
| 36.| **Github授权** | |
|
||||||
| 37.| **HiPM DNSMgr** | HiPM DNSMgr API Token 授权 |
|
| 37.| **godaddy授权** | |
|
||||||
| 38.| **金山云授权** | |
|
| 38.| **HiPM DNSMgr** | HiPM DNSMgr API Token 授权 |
|
||||||
| 39.| **FTP授权** | |
|
| 39.| **金山云授权** | |
|
||||||
| 40.| **七牛OSS授权** | |
|
| 40.| **FTP授权** | |
|
||||||
| 41.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 |
|
| 41.| **七牛OSS授权** | |
|
||||||
| 42.| **s3/minio授权** | S3/minio oss授权 |
|
| 42.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 |
|
||||||
| 43.| **namesilo授权** | |
|
| 43.| **s3/minio授权** | S3/minio oss授权 |
|
||||||
| 44.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 |
|
| 44.| **namesilo授权** | |
|
||||||
| 45.| **Nginx Proxy Manager 授权** | 用于登录 Nginx Proxy Manager,并为代理主机证书部署提供授权。 |
|
| 45.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 |
|
||||||
| 46.| **1panel授权** | 账号和密码 |
|
| 46.| **Nginx Proxy Manager 授权** | 用于登录 Nginx Proxy Manager,并为代理主机证书部署提供授权。 |
|
||||||
| 47.| **支付宝** | |
|
| 47.| **1panel授权** | 账号和密码 |
|
||||||
| 48.| **白山云授权** | |
|
| 48.| **支付宝** | |
|
||||||
| 49.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 |
|
| 49.| **白山云授权** | |
|
||||||
| 50.| **cdnfly授权** | |
|
| 50.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 |
|
||||||
| 51.| **k8s授权** | |
|
| 51.| **cdnfly授权** | |
|
||||||
| 52.| **括彩云cdn授权** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
|
| 52.| **k8s授权** | |
|
||||||
| 53.| **LeCDN授权** | |
|
| 53.| **括彩云cdn授权** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) |
|
||||||
| 54.| **lucky** | |
|
| 54.| **LeCDN授权** | |
|
||||||
| 55.| **猫云授权** | |
|
| 55.| **lucky** | |
|
||||||
| 56.| **plesk授权** | |
|
| 56.| **猫云授权** | |
|
||||||
| 57.| **长亭雷池授权** | |
|
| 57.| **plesk授权** | |
|
||||||
| 58.| **群晖登录授权** | |
|
| 58.| **长亭雷池授权** | |
|
||||||
| 59.| **uniCloud** | unicloud授权 |
|
| 59.| **群晖登录授权** | |
|
||||||
| 60.| **微信支付** | |
|
| 60.| **uniCloud** | unicloud授权 |
|
||||||
| 61.| **易盾rcdn授权** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
|
| 61.| **微信支付** | |
|
||||||
| 62.| **易发云短信** | sms.yfyidc.cn/ |
|
| 62.| **易盾rcdn授权** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) |
|
||||||
| 63.| **易盾DCDN授权** | https://user.yiduncdn.com |
|
| 63.| **易发云短信** | sms.yfyidc.cn/ |
|
||||||
| 64.| **易支付** | |
|
| 64.| **易盾DCDN授权** | https://user.yiduncdn.com |
|
||||||
| 65.| **proxmox** | |
|
| 65.| **易支付** | |
|
||||||
| 66.| **Spaceship.com 授权** | Spaceship.com API 授权插件 |
|
| 66.| **proxmox** | |
|
||||||
| 67.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器授权 |
|
| 67.| **Spaceship.com 授权** | Spaceship.com API 授权插件 |
|
||||||
| 68.| **UCloud授权** | 优刻得授权 |
|
| 68.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器授权 |
|
||||||
| 69.| **又拍云** | |
|
| 69.| **UCloud授权** | 优刻得授权 |
|
||||||
| 70.| **网宿授权** | |
|
| 70.| **又拍云** | |
|
||||||
| 71.| **西部数码授权** | |
|
| 71.| **网宿授权** | |
|
||||||
| 72.| **我爱云授权** | 我爱云CDN |
|
| 72.| **西部数码授权** | |
|
||||||
| 73.| **新网授权(代理方式)** | |
|
| 73.| **我爱云授权** | 我爱云CDN |
|
||||||
| 74.| **新网授权** | |
|
| 74.| **新网授权(代理方式)** | |
|
||||||
| 75.| **新网互联授权** | 仅支持代理账号,ip需要加入白名单 |
|
| 75.| **新网授权** | |
|
||||||
| 76.| **Zenlayer授权** | Zenlayer授权 |
|
| 76.| **新网互联授权** | 仅支持代理账号,ip需要加入白名单 |
|
||||||
| 77.| **GoEdge授权** | |
|
| 77.| **Zenlayer授权** | Zenlayer授权 |
|
||||||
| 78.| **雨云授权** | https://app.rainyun.com/ |
|
| 78.| **GoEdge授权** | |
|
||||||
|
| 79.| **雨云授权** | https://app.rainyun.com/ |
|
||||||
|
|
||||||
<style module>
|
<style module>
|
||||||
table th:first-of-type {
|
table th:first-of-type {
|
||||||
|
|||||||
@@ -13,21 +13,22 @@
|
|||||||
| 9.| **BIND9 DNS** | 通过 SSH 连接到 BIND9 服务器,使用 nsupdate 命令管理 DNS 记录 |
|
| 9.| **BIND9 DNS** | 通过 SSH 连接到 BIND9 服务器,使用 nsupdate 命令管理 DNS 记录 |
|
||||||
| 10.| **cloudflare** | cloudflare dns provider |
|
| 10.| **cloudflare** | cloudflare dns provider |
|
||||||
| 11.| **dns.la** | dns.la |
|
| 11.| **dns.la** | dns.la |
|
||||||
| 12.| **godaddy** | GoDaddy |
|
| 12.| **Dynadot** | Dynadot DNS提供商 |
|
||||||
| 13.| **HiPM DNSMgr** | HiPM DNSMgr DNS 解析提供商 |
|
| 13.| **godaddy** | GoDaddy |
|
||||||
| 14.| **华为云** | 华为云DNS解析提供商 |
|
| 14.| **HiPM DNSMgr** | HiPM DNSMgr DNS 解析提供商 |
|
||||||
| 15.| **namesilo** | namesilo dns provider |
|
| 15.| **华为云** | 华为云DNS解析提供商 |
|
||||||
| 16.| **雨云** | 雨云DNS解析提供商 |
|
| 16.| **namesilo** | namesilo dns provider |
|
||||||
| 17.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器 |
|
| 17.| **雨云** | 雨云DNS解析提供商 |
|
||||||
| 18.| **腾讯云** | 腾讯云域名DNS解析提供者 |
|
| 18.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器 |
|
||||||
| 19.| **腾讯云EO DNS** | 腾讯云EO DNS解析提供者 |
|
| 19.| **腾讯云** | 腾讯云域名DNS解析提供者 |
|
||||||
| 20.| **西部数码** | west dns provider |
|
| 20.| **腾讯云EO DNS** | 腾讯云EO DNS解析提供者 |
|
||||||
| 21.| **Google Cloud DNS** | Google Cloud DNS提供商 |
|
| 21.| **西部数码** | west dns provider |
|
||||||
| 22.| **Dns提供商Demo** | dns provider示例 |
|
| 22.| **Google Cloud DNS** | Google Cloud DNS提供商 |
|
||||||
| 23.| **彩虹DNS** | 彩虹DNS管理系统 |
|
| 23.| **Dns提供商Demo** | dns provider示例 |
|
||||||
| 24.| **Spaceship** | Spaceship 域名解析 |
|
| 24.| **彩虹DNS** | 彩虹DNS管理系统 |
|
||||||
| 25.| **51dns** | 51DNS |
|
| 25.| **Spaceship** | Spaceship 域名解析 |
|
||||||
| 26.| **新网互联** | 新网互联 |
|
| 26.| **51dns** | 51DNS |
|
||||||
|
| 27.| **新网互联** | 新网互联 |
|
||||||
|
|
||||||
<style module>
|
<style module>
|
||||||
table th:first-of-type {
|
table th:first-of-type {
|
||||||
|
|||||||
+1
-1
@@ -9,5 +9,5 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"npmClient": "pnpm",
|
"npmClient": "pnpm",
|
||||||
"version": "1.41.0"
|
"version": "1.41.4"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -15,8 +15,7 @@
|
|||||||
"vitepress-plugin-lightbox": "^1.0.2"
|
"vitepress-plugin-lightbox": "^1.0.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "lerna bootstrap --hoist",
|
"start": "cd ./packages/ui/certd-server && pnpm start",
|
||||||
"start:server": "cd ./packages/ui/certd-server && pnpm start",
|
|
||||||
"devb": "lerna run dev-build",
|
"devb": "lerna run dev-build",
|
||||||
"i-all": "lerna link && lerna exec npm install ",
|
"i-all": "lerna link && lerna exec npm install ",
|
||||||
"publish": "pnpm run prepublishOnly2 && lerna publish --force-publish=pro/plus-core --conventional-commits && pnpm run afterpublishOnly ",
|
"publish": "pnpm run prepublishOnly2 && lerna publish --force-publish=pro/plus-core --conventional-commits && pnpm run afterpublishOnly ",
|
||||||
|
|||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/publishlab/node-acme-client/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/publishlab/node-acme-client/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复litessl无法申请证书,报authorization must be pending 错误的问题 ([d6cd9d1](https://github.com/publishlab/node-acme-client/commit/d6cd9d136d2812b2335917305f36d6d9414507ad))
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/publishlab/node-acme-client/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/publishlab/node-acme-client/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/acme-client
|
||||||
|
|
||||||
# [1.41.0](https://github.com/publishlab/node-acme-client/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/publishlab/node-acme-client/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "Simple and unopinionated ACME client",
|
"description": "Simple and unopinionated ACME client",
|
||||||
"private": false,
|
"private": false,
|
||||||
"author": "nmorsman",
|
"author": "nmorsman",
|
||||||
"version": "1.41.0",
|
"version": "1.41.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"types"
|
"types"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.41.0",
|
"@certd/basic": "^1.41.4",
|
||||||
"@peculiar/x509": "^1.11.0",
|
"@peculiar/x509": "^1.11.0",
|
||||||
"asn1js": "^3.0.5",
|
"asn1js": "^3.0.5",
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
@@ -33,18 +33,18 @@
|
|||||||
"@types/node": "^20.14.10",
|
"@types/node": "^20.14.10",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"chai": "^4.4.1",
|
"chai": "^5.1.0",
|
||||||
"chai-as-promised": "^7.1.2",
|
"chai-as-promised": "^7.1.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"esmock": "^2.7.5",
|
"esmock": "^2.7.5",
|
||||||
"jsdoc-to-markdown": "^8.0.1",
|
"jsdoc-to-markdown": "^8.0.1",
|
||||||
"mocha": "^10.6.0",
|
"mocha": "^10.6.0",
|
||||||
"nock": "^13.5.4",
|
"nock": "^13.5.4",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "3.3.3",
|
||||||
"tsd": "^0.31.1",
|
"tsd": "^0.31.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
@@ -59,7 +59,8 @@
|
|||||||
"before-test:unit": "node -e \"const fs=require('fs');fs.rmSync('dist-test',{recursive:true,force:true});fs.rmSync('tsconfig.test.tsbuildinfo',{force:true});\"",
|
"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\"",
|
"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",
|
"pub": "npm publish",
|
||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch",
|
||||||
|
"format": "prettier --write src"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -76,5 +77,5 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/publishlab/node-acme-client/issues"
|
"url": "https://github.com/publishlab/node-acme-client/issues"
|
||||||
},
|
},
|
||||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
"gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
import assert from "node:assert/strict";
|
||||||
|
import auto from "./auto.js";
|
||||||
|
import { createCsr, createPrivateRsaKey } from "./crypto/index.js";
|
||||||
|
|
||||||
|
declare const describe: any;
|
||||||
|
declare const it: any;
|
||||||
|
|
||||||
|
describe("auto challenge status polling", () => {
|
||||||
|
it("polls the authorization URL after completing a challenge", async () => {
|
||||||
|
const [, csr] = await createCsr({ commonName: "example.com" }, await createPrivateRsaKey());
|
||||||
|
const challenge = {
|
||||||
|
type: "dns-01",
|
||||||
|
url: "https://ca.example/chall/1",
|
||||||
|
token: "token",
|
||||||
|
};
|
||||||
|
const authz = {
|
||||||
|
status: "pending",
|
||||||
|
identifier: { type: "dns", value: "example.com" },
|
||||||
|
url: "https://ca.example/authz/1",
|
||||||
|
challenges: [challenge],
|
||||||
|
};
|
||||||
|
const order = {
|
||||||
|
status: "pending",
|
||||||
|
url: "https://ca.example/order/1",
|
||||||
|
finalize: "https://ca.example/order/1/finalize",
|
||||||
|
authorizations: [authz.url],
|
||||||
|
};
|
||||||
|
const polledUrls: string[] = [];
|
||||||
|
const originalSetTimeout = globalThis.setTimeout;
|
||||||
|
|
||||||
|
(globalThis as any).setTimeout = (fn: (...args: any[]) => void) => originalSetTimeout(fn, 0);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const certificate = await auto(
|
||||||
|
{
|
||||||
|
logger: { info: () => {} },
|
||||||
|
sslProvider: "litessl",
|
||||||
|
getAccountUrl: () => "https://ca.example/acct/1",
|
||||||
|
createOrder: async () => order,
|
||||||
|
getAuthorizations: async () => [authz],
|
||||||
|
getChallengeKeyAuthorization: async () => "key-authorization",
|
||||||
|
verifyChallenge: async () => {},
|
||||||
|
completeChallenge: async () => ({ ...challenge, status: "processing" }),
|
||||||
|
waitForValidStatus: async (item: { url: string }) => {
|
||||||
|
polledUrls.push(item.url);
|
||||||
|
return { ...item, status: "valid" };
|
||||||
|
},
|
||||||
|
finalizeOrder: async () => ({ ...order, status: "valid", certificate: "https://ca.example/cert/1" }),
|
||||||
|
getCertificate: async () => "CERTIFICATE",
|
||||||
|
} as any,
|
||||||
|
{
|
||||||
|
csr,
|
||||||
|
termsOfServiceAgreed: true,
|
||||||
|
waitDnsDiffuseTime: 0,
|
||||||
|
challengeCreateFn: async (_authz: any, keyAuthorizationGetter: (challenge: any) => Promise<string>) => ({
|
||||||
|
challenge,
|
||||||
|
keyAuthorization: await keyAuthorizationGetter(challenge),
|
||||||
|
}),
|
||||||
|
challengeRemoveFn: async () => {},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(certificate, "CERTIFICATE");
|
||||||
|
assert.deepEqual(polledUrls, [authz.url]);
|
||||||
|
} finally {
|
||||||
|
(globalThis as any).setTimeout = originalSetTimeout;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -172,7 +172,7 @@ export default async (client, userOpts) => {
|
|||||||
}
|
}
|
||||||
challengeCompleted = true;
|
challengeCompleted = true;
|
||||||
log(`[auto] [${d}] 等待返回valid状态`);
|
log(`[auto] [${d}] 等待返回valid状态`);
|
||||||
await client.waitForValidStatus(challenge,d);
|
await client.waitForValidStatus(authz,d);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -263,4 +263,4 @@ export function createChallengeFn(opts = {}) {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// createChallengeFn({logger:{info:console.log}}).walkDnsChallengeRecord("handsfree.work")
|
// createChallengeFn({logger:{info:console.log}}).walkDnsChallengeRecord("handfree.work")
|
||||||
|
|||||||
@@ -3,6 +3,28 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/basic
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
|
||||||
|
|
||||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/basic
|
**Note:** Version bump only for package @certd/basic
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
12:22
|
21:25
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/basic",
|
"name": "@certd/basic",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.41.0",
|
"version": "1.41.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -15,7 +15,9 @@
|
|||||||
"test": "mocha --loader=ts-node/esm",
|
"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\"",
|
"test:unit": "cross-env NODE_ENV=unittest mocha --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
||||||
"pub": "npm publish",
|
"pub": "npm publish",
|
||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"lint": "eslint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async-lock": "^1.4.1",
|
"async-lock": "^1.4.1",
|
||||||
@@ -26,31 +28,31 @@
|
|||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"log4js": "^6.9.1",
|
"log4js": "^6.9.1",
|
||||||
"lru-cache": "^10.0.0",
|
"lru-cache": "^11.0.1",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
"node-forge": "^1.3.1",
|
"node-forge": "^1.3.1",
|
||||||
"nodemailer": "^6.9.3"
|
"nodemailer": "^6.9.16"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.3.10",
|
"@types/chai": "^4.3.12",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/mocha": "^10.0.1",
|
"@types/mocha": "^10.0.6",
|
||||||
"@types/node-forge": "^1.3.2",
|
"@types/node-forge": "^1.3.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"chai": "4.3.10",
|
"chai": "^5.1.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.41.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"esmock": "^2.7.5",
|
"esmock": "^2.7.5",
|
||||||
"mocha": "^10.2.0",
|
"mocha": "^10.6.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "3.3.3",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
"gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import { expect } from "chai";
|
import { expect } from "chai";
|
||||||
import { createAxiosService, HttpClient, setGlobalHeaders } from "./util.request.js";
|
import { createAgent, createAxiosService, getGlobalAgents, HttpClient, isNoProxyMatched, setGlobalHeaders, setGlobalProxy } from "./util.request.js";
|
||||||
import { ILogger } from "./util.log.js";
|
import { ILogger } from "./util.log.js";
|
||||||
|
|
||||||
const testLogger = {
|
const testLogger = {
|
||||||
|
debug() {},
|
||||||
info() {},
|
info() {},
|
||||||
error() {},
|
error() {},
|
||||||
} as unknown as ILogger;
|
} as unknown as ILogger;
|
||||||
@@ -10,6 +11,9 @@ const testLogger = {
|
|||||||
describe("util.request", () => {
|
describe("util.request", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
setGlobalHeaders({});
|
setGlobalHeaders({});
|
||||||
|
setGlobalProxy({});
|
||||||
|
delete process.env.NO_PROXY;
|
||||||
|
delete process.env.no_proxy;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should merge global headers without overriding request headers", async () => {
|
it("should merge global headers without overriding request headers", async () => {
|
||||||
@@ -50,4 +54,122 @@ describe("util.request", () => {
|
|||||||
request: "request",
|
request: "request",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should set no_proxy environment variables", () => {
|
||||||
|
setGlobalProxy({
|
||||||
|
httpProxy: "http://127.0.0.1:1080",
|
||||||
|
httpsProxy: "http://127.0.0.1:1080",
|
||||||
|
noProxy: "localhost,*.internal.example.com",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(process.env.NO_PROXY).to.equal("localhost,*.internal.example.com");
|
||||||
|
expect(process.env.no_proxy).to.equal("localhost,*.internal.example.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should normalize multiline no_proxy environment variables", () => {
|
||||||
|
setGlobalProxy({
|
||||||
|
noProxy: "localhost\n127.0.0.1, 192.168.*\n*.internal.example.com",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(process.env.NO_PROXY).to.equal("localhost,127.0.0.1,192.168.*,*.internal.example.com");
|
||||||
|
expect(process.env.no_proxy).to.equal("localhost,127.0.0.1,192.168.*,*.internal.example.com");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not change environment variables when creating agents", () => {
|
||||||
|
process.env.HTTP_PROXY = "http://old-http-proxy";
|
||||||
|
process.env.HTTPS_PROXY = "http://old-https-proxy";
|
||||||
|
process.env.NO_PROXY = "old.local";
|
||||||
|
|
||||||
|
createAgent({
|
||||||
|
httpProxy: "http://127.0.0.1:1080",
|
||||||
|
httpsProxy: "http://127.0.0.1:1081",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(process.env.HTTP_PROXY).to.equal("http://old-http-proxy");
|
||||||
|
expect(process.env.HTTPS_PROXY).to.equal("http://old-https-proxy");
|
||||||
|
expect(process.env.NO_PROXY).to.equal("old.local");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should bypass global proxy when request host matches no_proxy", async () => {
|
||||||
|
setGlobalProxy({
|
||||||
|
httpProxy: "http://127.0.0.1:1080",
|
||||||
|
httpsProxy: "http://127.0.0.1:1080",
|
||||||
|
noProxy: "localhost,.internal.example.com",
|
||||||
|
});
|
||||||
|
|
||||||
|
const globalAgents = getGlobalAgents();
|
||||||
|
const http = createAxiosService({ logger: testLogger }) as HttpClient;
|
||||||
|
const res = await http.request({
|
||||||
|
url: "https://api.internal.example.com",
|
||||||
|
method: "get",
|
||||||
|
logReq: false,
|
||||||
|
logRes: false,
|
||||||
|
adapter: async config => {
|
||||||
|
return {
|
||||||
|
config,
|
||||||
|
data: {
|
||||||
|
usesGlobalHttpAgent: config.httpAgent === globalAgents.httpAgent,
|
||||||
|
usesGlobalHttpsAgent: config.httpsAgent === globalAgents.httpsAgent,
|
||||||
|
},
|
||||||
|
headers: {},
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).to.deep.equal({
|
||||||
|
usesGlobalHttpAgent: false,
|
||||||
|
usesGlobalHttpsAgent: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should bypass custom request proxy when request host matches no_proxy", async () => {
|
||||||
|
setGlobalProxy({
|
||||||
|
noProxy: ".internal.example.com",
|
||||||
|
});
|
||||||
|
|
||||||
|
const http = createAxiosService({ logger: testLogger }) as HttpClient;
|
||||||
|
const res = await http.request({
|
||||||
|
url: "https://api.internal.example.com",
|
||||||
|
method: "get",
|
||||||
|
httpProxy: "http://127.0.0.1:1080",
|
||||||
|
logReq: false,
|
||||||
|
logRes: false,
|
||||||
|
adapter: async config => {
|
||||||
|
return {
|
||||||
|
config,
|
||||||
|
data: {
|
||||||
|
httpAgent: config.httpAgent?.constructor?.name,
|
||||||
|
httpsAgent: config.httpsAgent?.constructor?.name,
|
||||||
|
},
|
||||||
|
headers: {},
|
||||||
|
status: 200,
|
||||||
|
statusText: "OK",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(res).to.deep.equal({
|
||||||
|
httpAgent: "Agent",
|
||||||
|
httpsAgent: "Agent",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should match no_proxy rules", () => {
|
||||||
|
expect(isNoProxyMatched("*", { hostname: "api.example.com", port: "" })).to.equal(true);
|
||||||
|
expect(isNoProxyMatched("api.example.com", { hostname: "api.example.com", port: "" })).to.equal(true);
|
||||||
|
expect(isNoProxyMatched("example.com", { hostname: "api.example.com", port: "" })).to.equal(true);
|
||||||
|
expect(isNoProxyMatched(".example.com", { hostname: "api.example.com", port: "" })).to.equal(true);
|
||||||
|
expect(isNoProxyMatched("*.example.com", { hostname: "api.example.com", port: "" })).to.equal(true);
|
||||||
|
expect(isNoProxyMatched("127.0.0.1", { hostname: "127.0.0.1", port: "" })).to.equal(true);
|
||||||
|
expect(isNoProxyMatched("192.168.*", { hostname: "192.168.1.10", port: "" })).to.equal(true);
|
||||||
|
expect(isNoProxyMatched("192.168.*", { hostname: "192.169.1.10", port: "" })).to.equal(false);
|
||||||
|
expect(isNoProxyMatched("[::1]", { hostname: "::1", port: "" })).to.equal(true);
|
||||||
|
expect(isNoProxyMatched("[::1]:8443", { hostname: "::1", port: "8443" })).to.equal(true);
|
||||||
|
expect(isNoProxyMatched("api.example.com:8443", { hostname: "api.example.com", port: "8443" })).to.equal(true);
|
||||||
|
expect(isNoProxyMatched("api.example.com:8443", { hostname: "api.example.com", port: "443" })).to.equal(false);
|
||||||
|
expect(isNoProxyMatched("127.0.0.1", { hostname: "127.0.0.2", port: "" })).to.equal(false);
|
||||||
|
expect(isNoProxyMatched(".example.com", { hostname: "example.org", port: "" })).to.equal(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -82,11 +82,24 @@ export class HttpError extends Error {
|
|||||||
export const HttpCommonError = HttpError;
|
export const HttpCommonError = HttpError;
|
||||||
|
|
||||||
let defaultAgents = createAgent();
|
let defaultAgents = createAgent();
|
||||||
|
const directAgents = createAgent();
|
||||||
|
let defaultProxyOptions: GlobalProxyOptions = {};
|
||||||
let defaultHeaders: Record<string, string> = {};
|
let defaultHeaders: Record<string, string> = {};
|
||||||
|
|
||||||
export function setGlobalProxy(opts: { httpProxy?: string; httpsProxy?: string }) {
|
export type GlobalProxyOptions = {
|
||||||
|
httpProxy?: string;
|
||||||
|
httpsProxy?: string;
|
||||||
|
noProxy?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function setGlobalProxy(opts: GlobalProxyOptions) {
|
||||||
logger.info("setGlobalProxy:", opts);
|
logger.info("setGlobalProxy:", opts);
|
||||||
defaultAgents = createAgent(opts);
|
defaultProxyOptions = { ...opts };
|
||||||
|
defaultAgents = createAgent({
|
||||||
|
httpProxy: opts.httpProxy,
|
||||||
|
httpsProxy: opts.httpsProxy,
|
||||||
|
});
|
||||||
|
setProxyEnvironment(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getGlobalAgents() {
|
export function getGlobalAgents() {
|
||||||
@@ -137,21 +150,25 @@ export function createAxiosService({ logger }: { logger: ILogger }) {
|
|||||||
if (config.timeout == null) {
|
if (config.timeout == null) {
|
||||||
config.timeout = 15000;
|
config.timeout = 15000;
|
||||||
}
|
}
|
||||||
let agents = defaultAgents;
|
const bypassProxy = shouldBypassProxy(config, defaultProxyOptions.noProxy);
|
||||||
if (config.skipSslVerify || config.httpProxy) {
|
const useCustomProxy = !!config.httpProxy && !bypassProxy;
|
||||||
let rejectUnauthorized = true;
|
let agents = bypassProxy ? directAgents : defaultAgents;
|
||||||
|
if (bypassProxy) {
|
||||||
|
logger.info("命中no_proxy配置,跳过代理:", config.url);
|
||||||
|
}
|
||||||
|
if (config.skipSslVerify || useCustomProxy) {
|
||||||
|
const agentOptions: any = {};
|
||||||
if (config.skipSslVerify) {
|
if (config.skipSslVerify) {
|
||||||
logger.info("忽略接口请求的SSL校验");
|
logger.info("忽略接口请求的SSL校验");
|
||||||
rejectUnauthorized = false;
|
agentOptions.rejectUnauthorized = false;
|
||||||
}
|
}
|
||||||
const proxy: any = {};
|
if (useCustomProxy) {
|
||||||
if (config.httpProxy) {
|
|
||||||
logger.info("使用自定义http代理:", config.httpProxy);
|
logger.info("使用自定义http代理:", config.httpProxy);
|
||||||
proxy.httpProxy = config.httpProxy;
|
agentOptions.httpProxy = config.httpProxy;
|
||||||
proxy.httpsProxy = config.httpProxy;
|
agentOptions.httpsProxy = config.httpProxy;
|
||||||
}
|
}
|
||||||
|
|
||||||
agents = createAgent({ rejectUnauthorized, ...proxy } as any);
|
agents = createAgent(agentOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete config.skipSslVerify;
|
delete config.skipSslVerify;
|
||||||
@@ -354,7 +371,7 @@ export type CreateAgentOptions = {
|
|||||||
httpsProxy?: string;
|
httpsProxy?: string;
|
||||||
} & nodeHttp.AgentOptions;
|
} & nodeHttp.AgentOptions;
|
||||||
export function createAgent(opts: CreateAgentOptions = {}) {
|
export function createAgent(opts: CreateAgentOptions = {}) {
|
||||||
opts = merge(
|
const { httpProxy, httpsProxy, ...agentOptions } = merge(
|
||||||
{
|
{
|
||||||
autoSelectFamily: true,
|
autoSelectFamily: true,
|
||||||
autoSelectFamilyAttemptTimeout: 1000,
|
autoSelectFamilyAttemptTimeout: 1000,
|
||||||
@@ -364,29 +381,19 @@ export function createAgent(opts: CreateAgentOptions = {}) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let httpAgent, httpsAgent;
|
let httpAgent, httpsAgent;
|
||||||
const httpProxy = opts.httpProxy;
|
|
||||||
if (httpProxy) {
|
if (httpProxy) {
|
||||||
process.env.HTTP_PROXY = httpProxy;
|
|
||||||
process.env.http_proxy = httpProxy;
|
|
||||||
logger.info("use httpProxy:", httpProxy);
|
logger.info("use httpProxy:", httpProxy);
|
||||||
httpAgent = new HttpProxyAgent(httpProxy, opts as any);
|
httpAgent = new HttpProxyAgent(httpProxy, agentOptions as any);
|
||||||
merge(httpAgent.options, opts);
|
merge(httpAgent.options, agentOptions);
|
||||||
} else {
|
} else {
|
||||||
process.env.HTTP_PROXY = "";
|
httpAgent = new nodeHttp.Agent(agentOptions);
|
||||||
process.env.http_proxy = "";
|
|
||||||
httpAgent = new nodeHttp.Agent(opts);
|
|
||||||
}
|
}
|
||||||
const httpsProxy = opts.httpsProxy;
|
|
||||||
if (httpsProxy) {
|
if (httpsProxy) {
|
||||||
process.env.HTTPS_PROXY = httpsProxy;
|
|
||||||
process.env.https_proxy = httpsProxy;
|
|
||||||
logger.info("use httpsProxy:", httpsProxy);
|
logger.info("use httpsProxy:", httpsProxy);
|
||||||
httpsAgent = new HttpsProxyAgent(httpsProxy, opts as any);
|
httpsAgent = new HttpsProxyAgent(httpsProxy, agentOptions as any);
|
||||||
merge(httpsAgent.options, opts);
|
merge(httpsAgent.options, agentOptions);
|
||||||
} else {
|
} else {
|
||||||
process.env.HTTPS_PROXY = "";
|
httpsAgent = new https.Agent(agentOptions);
|
||||||
process.env.https_proxy = "";
|
|
||||||
httpsAgent = new https.Agent(opts);
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
httpAgent,
|
httpAgent,
|
||||||
@@ -394,6 +401,145 @@ export function createAgent(opts: CreateAgentOptions = {}) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setProxyEnvironment(opts: GlobalProxyOptions = {}) {
|
||||||
|
setEnvValue("HTTP_PROXY", opts.httpProxy);
|
||||||
|
setEnvValue("http_proxy", opts.httpProxy);
|
||||||
|
setEnvValue("HTTPS_PROXY", opts.httpsProxy);
|
||||||
|
setEnvValue("https_proxy", opts.httpsProxy);
|
||||||
|
const noProxy = normalizeNoProxyText(opts.noProxy);
|
||||||
|
setEnvValue("NO_PROXY", noProxy);
|
||||||
|
setEnvValue("no_proxy", noProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setEnvValue(key: string, value?: string) {
|
||||||
|
process.env[key] = value || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldBypassProxy(config: AxiosRequestConfig, noProxy?: string) {
|
||||||
|
if (!noProxy) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const target = getRequestTarget(config);
|
||||||
|
if (!target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return splitNoProxyRules(noProxy).some(item => isNoProxyMatched(item, target));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRequestTarget(config: AxiosRequestConfig) {
|
||||||
|
try {
|
||||||
|
const baseURL = config.baseURL || undefined;
|
||||||
|
const url = new URL(config.url || "", baseURL);
|
||||||
|
return {
|
||||||
|
hostname: normalizeHost(url.hostname),
|
||||||
|
port: url.port,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isNoProxyMatched(rule: string, target: { hostname: string; port: string }) {
|
||||||
|
if (rule === "*") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedRule = normalizeNoProxyRule(rule);
|
||||||
|
if (!normalizedRule.host) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (normalizedRule.port && normalizedRule.port !== target.port) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const host = normalizeHost(target.hostname);
|
||||||
|
if (normalizedRule.host.includes("*")) {
|
||||||
|
return wildcardHostMatched(normalizedRule.host, host);
|
||||||
|
}
|
||||||
|
if (normalizedRule.host.startsWith("*.")) {
|
||||||
|
const suffix = normalizedRule.host.substring(1);
|
||||||
|
return host.endsWith(suffix);
|
||||||
|
}
|
||||||
|
if (normalizedRule.host.startsWith(".")) {
|
||||||
|
return host === normalizedRule.host.substring(1) || host.endsWith(normalizedRule.host);
|
||||||
|
}
|
||||||
|
return host === normalizedRule.host || host.endsWith(`.${normalizedRule.host}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeNoProxyRule(rule: string) {
|
||||||
|
let value = rule.trim().toLowerCase();
|
||||||
|
if (value.includes("://")) {
|
||||||
|
try {
|
||||||
|
const url = new URL(value);
|
||||||
|
return {
|
||||||
|
host: normalizeHost(url.hostname),
|
||||||
|
port: url.port,
|
||||||
|
};
|
||||||
|
} catch (e) {
|
||||||
|
return {
|
||||||
|
host: "",
|
||||||
|
port: "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let port = "";
|
||||||
|
if (value.startsWith("[")) {
|
||||||
|
const closeIndex = value.indexOf("]");
|
||||||
|
const host = value.substring(1, closeIndex);
|
||||||
|
const rest = value.substring(closeIndex + 1);
|
||||||
|
if (rest.startsWith(":")) {
|
||||||
|
port = rest.substring(1);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
host: normalizeHost(host),
|
||||||
|
port,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const colonCount = (value.match(/:/g) || []).length;
|
||||||
|
const portIndex = value.lastIndexOf(":");
|
||||||
|
if (colonCount === 1 && portIndex > -1) {
|
||||||
|
port = value.substring(portIndex + 1);
|
||||||
|
value = value.substring(0, portIndex);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
host: normalizeHost(value),
|
||||||
|
port,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeHost(host: string) {
|
||||||
|
let value = host.trim().toLowerCase();
|
||||||
|
if (value.startsWith("[") && value.endsWith("]")) {
|
||||||
|
value = value.substring(1, value.length - 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wildcardHostMatched(rule: string, host: string) {
|
||||||
|
const pattern = rule.split("*").map(escapeRegExp).join(".*");
|
||||||
|
return new RegExp(`^${pattern}$`).test(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
function escapeRegExp(value: string) {
|
||||||
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeNoProxyText(noProxy?: string) {
|
||||||
|
return splitNoProxyRules(noProxy).join(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitNoProxyRules(noProxy?: string) {
|
||||||
|
if (!noProxy) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return noProxy
|
||||||
|
.split(/[,\s]+/)
|
||||||
|
.map(item => item.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
export async function download(req: { http: HttpClient; config: HttpRequestConfig; savePath: string; logger: ILogger }) {
|
export async function download(req: { http: HttpClient; config: HttpRequestConfig; savePath: string; logger: ILogger }) {
|
||||||
const { http, config, savePath, logger } = req;
|
const { http, config, savePath, logger } = req;
|
||||||
return safePromise((resolve, reject) => {
|
return safePromise((resolve, reject) => {
|
||||||
|
|||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/pipeline
|
||||||
|
|
||||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/pipeline",
|
"name": "@certd/pipeline",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.41.0",
|
"version": "1.41.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -16,14 +16,16 @@
|
|||||||
"test": "mocha --loader=ts-node/esm",
|
"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\"",
|
"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",
|
"pub": "npm publish",
|
||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"lint": "eslint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.41.0",
|
"@certd/basic": "^1.41.4",
|
||||||
"@certd/plus-core": "^1.41.0",
|
"@certd/plus-core": "^1.41.4",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"reflect-metadata": "^0.1.13"
|
"reflect-metadata": "^0.2.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^23.0.4",
|
"@rollup/plugin-commonjs": "^23.0.4",
|
||||||
@@ -31,23 +33,23 @@
|
|||||||
"@rollup/plugin-node-resolve": "^15.0.1",
|
"@rollup/plugin-node-resolve": "^15.0.1",
|
||||||
"@rollup/plugin-terser": "^0.4.3",
|
"@rollup/plugin-terser": "^0.4.3",
|
||||||
"@rollup/plugin-typescript": "^11.0.0",
|
"@rollup/plugin-typescript": "^11.0.0",
|
||||||
"@types/chai": "^4.3.10",
|
"@types/chai": "^4.3.12",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/mocha": "^10.0.1",
|
"@types/mocha": "^10.0.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"chai": "4.3.10",
|
"chai": "^5.1.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.41.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^8.8.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"esmock": "^2.7.5",
|
"esmock": "^2.7.5",
|
||||||
"mocha": "^10.2.0",
|
"mocha": "^10.6.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "3.3.3",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
"gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { FormItemProps } from "../dt/index.js";
|
|||||||
import { HttpClient, ILogger, utils } from "@certd/basic";
|
import { HttpClient, ILogger, utils } from "@certd/basic";
|
||||||
import * as _ from "lodash-es";
|
import * as _ from "lodash-es";
|
||||||
import { PluginRequestHandleReq } from "../plugin/index.js";
|
import { PluginRequestHandleReq } from "../plugin/index.js";
|
||||||
|
import { IRuntimeDepsService, IServiceGetter } from "../service/index.js";
|
||||||
|
|
||||||
// export type AccessRequestHandleReqInput<T = any> = {
|
// export type AccessRequestHandleReqInput<T = any> = {
|
||||||
// id?: number;
|
// id?: number;
|
||||||
@@ -20,6 +21,8 @@ export type AccessInputDefine = FormItemProps & {
|
|||||||
export type AccessDefine = Registrable & {
|
export type AccessDefine = Registrable & {
|
||||||
icon?: string;
|
icon?: string;
|
||||||
subtype?: string;
|
subtype?: string;
|
||||||
|
dependPlugins?: Record<string, string>;
|
||||||
|
dependPackages?: Record<string, string>;
|
||||||
input?: {
|
input?: {
|
||||||
[key: string]: AccessInputDefine;
|
[key: string]: AccessInputDefine;
|
||||||
};
|
};
|
||||||
@@ -39,13 +42,29 @@ export type AccessContext = {
|
|||||||
logger: ILogger;
|
logger: ILogger;
|
||||||
utils: typeof utils;
|
utils: typeof utils;
|
||||||
accessService: IAccessService;
|
accessService: IAccessService;
|
||||||
|
serviceGetter?: IServiceGetter;
|
||||||
|
define?: AccessDefine;
|
||||||
};
|
};
|
||||||
|
|
||||||
export abstract class BaseAccess implements IAccess {
|
export abstract class BaseAccess implements IAccess {
|
||||||
ctx!: AccessContext;
|
ctx!: AccessContext;
|
||||||
|
runtimeDepsService?: IRuntimeDepsService;
|
||||||
|
|
||||||
setCtx(ctx: AccessContext) {
|
async importRuntime(specifier: string) {
|
||||||
|
if (!this.runtimeDepsService) {
|
||||||
|
throw new Error("runtimeDepsService 未初始化");
|
||||||
|
}
|
||||||
|
return await this.runtimeDepsService.importRuntime(specifier, this.ctx.logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
async setCtx(ctx: AccessContext) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
|
if (!this.runtimeDepsService && this.ctx.serviceGetter) {
|
||||||
|
this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService");
|
||||||
|
}
|
||||||
|
if (this.runtimeDepsService && this.ctx.define?.name) {
|
||||||
|
await this.runtimeDepsService.ensureRuntimeDependencies({ pluginKeys: `access:${this.ctx.define.name}`, logger: this.ctx.logger });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async onRequest(req: AccessRequestHandleReq) {
|
async onRequest(req: AccessRequestHandleReq) {
|
||||||
|
|||||||
@@ -67,7 +67,9 @@ export async function newAccess(type: string, input: any, accessService: IAccess
|
|||||||
accessService,
|
accessService,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
access.setCtx(ctx);
|
ctx.define = ctx.define || register.define;
|
||||||
|
access.runtimeDepsService = (accessService as any).runtimeDepsService;
|
||||||
|
await access.setCtx(ctx);
|
||||||
access._type = type;
|
access._type = type;
|
||||||
return access;
|
return access;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ export class Executor {
|
|||||||
}),
|
}),
|
||||||
serviceGetter: this.options.serviceGetter,
|
serviceGetter: this.options.serviceGetter,
|
||||||
};
|
};
|
||||||
instance.setCtx(taskCtx);
|
await instance.setCtx(taskCtx);
|
||||||
|
|
||||||
await instance.onInstance();
|
await instance.onInstance();
|
||||||
const result = await instance.execute();
|
const result = await instance.execute();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Registrable } from "../registry/index.js";
|
|||||||
import { FormItemProps, HistoryResult, Pipeline } from "../dt/index.js";
|
import { FormItemProps, HistoryResult, Pipeline } from "../dt/index.js";
|
||||||
import { HttpClient, ILogger, utils } from "@certd/basic";
|
import { HttpClient, ILogger, utils } from "@certd/basic";
|
||||||
import * as _ from "lodash-es";
|
import * as _ from "lodash-es";
|
||||||
import { IEmailService } from "../service/index.js";
|
import { IEmailService, IRuntimeDepsService, IServiceGetter } from "../service/index.js";
|
||||||
|
|
||||||
export type NotificationBody = {
|
export type NotificationBody = {
|
||||||
userId?: number;
|
userId?: number;
|
||||||
@@ -39,6 +39,8 @@ export type NotificationInputDefine = FormItemProps & {
|
|||||||
};
|
};
|
||||||
export type NotificationDefine = Registrable & {
|
export type NotificationDefine = Registrable & {
|
||||||
needPlus?: boolean;
|
needPlus?: boolean;
|
||||||
|
dependPlugins?: Record<string, string>;
|
||||||
|
dependPackages?: Record<string, string>;
|
||||||
input?: {
|
input?: {
|
||||||
[key: string]: NotificationInputDefine;
|
[key: string]: NotificationInputDefine;
|
||||||
};
|
};
|
||||||
@@ -78,6 +80,8 @@ export type NotificationContext = {
|
|||||||
logger: ILogger;
|
logger: ILogger;
|
||||||
utils: typeof utils;
|
utils: typeof utils;
|
||||||
emailService: IEmailService;
|
emailService: IEmailService;
|
||||||
|
serviceGetter?: IServiceGetter;
|
||||||
|
define?: NotificationDefine;
|
||||||
};
|
};
|
||||||
|
|
||||||
export abstract class BaseNotification implements INotification {
|
export abstract class BaseNotification implements INotification {
|
||||||
@@ -85,6 +89,14 @@ export abstract class BaseNotification implements INotification {
|
|||||||
ctx!: NotificationContext;
|
ctx!: NotificationContext;
|
||||||
http!: HttpClient;
|
http!: HttpClient;
|
||||||
logger!: ILogger;
|
logger!: ILogger;
|
||||||
|
runtimeDepsService?: IRuntimeDepsService;
|
||||||
|
|
||||||
|
async importRuntime(specifier: string) {
|
||||||
|
if (!this.runtimeDepsService) {
|
||||||
|
return await import(specifier);
|
||||||
|
}
|
||||||
|
return await this.runtimeDepsService.importRuntime(specifier, this.logger);
|
||||||
|
}
|
||||||
|
|
||||||
async doSend(body: NotificationBody) {
|
async doSend(body: NotificationBody) {
|
||||||
return await this.send(body);
|
return await this.send(body);
|
||||||
@@ -93,10 +105,16 @@ export abstract class BaseNotification implements INotification {
|
|||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
async onInstance() {}
|
async onInstance() {}
|
||||||
setCtx(ctx: NotificationContext) {
|
async setCtx(ctx: NotificationContext) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.http = ctx.http;
|
this.http = ctx.http;
|
||||||
this.logger = ctx.logger;
|
this.logger = ctx.logger;
|
||||||
|
if (!this.runtimeDepsService && this.ctx.serviceGetter) {
|
||||||
|
this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService");
|
||||||
|
}
|
||||||
|
if (this.runtimeDepsService && this.ctx.define?.name) {
|
||||||
|
await this.runtimeDepsService.ensureRuntimeDependencies({ pluginKeys: `notification:${this.ctx.define.name}`, logger: this.logger });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setDefine = (define: NotificationDefine) => {
|
setDefine = (define: NotificationDefine) => {
|
||||||
this.define = define;
|
this.define = define;
|
||||||
|
|||||||
@@ -61,7 +61,8 @@ export async function newNotification(type: string, input: any, ctx: Notificatio
|
|||||||
throw new Error("ctx is required");
|
throw new Error("ctx is required");
|
||||||
}
|
}
|
||||||
plugin.setDefine(register.define);
|
plugin.setDefine(register.define);
|
||||||
plugin.setCtx(ctx);
|
ctx.define = ctx.define || register.define;
|
||||||
|
await plugin.setCtx(ctx);
|
||||||
await plugin.onInstance();
|
await plugin.onInstance();
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { INotificationService } from "../notification/index.js";
|
|||||||
import { Registrable } from "../registry/index.js";
|
import { Registrable } from "../registry/index.js";
|
||||||
import { IPluginConfigService } from "../service/config.js";
|
import { IPluginConfigService } from "../service/config.js";
|
||||||
import { TaskEmitter } from "../service/emit.js";
|
import { TaskEmitter } from "../service/emit.js";
|
||||||
import { ICnameProxyService, IEmailService, IServiceGetter, IUrlService } from "../service/index.js";
|
import { ICnameProxyService, IEmailService, IRuntimeDepsService, IServiceGetter, IUrlService } from "../service/index.js";
|
||||||
|
|
||||||
export type PluginRequestHandleReq<T = any> = {
|
export type PluginRequestHandleReq<T = any> = {
|
||||||
typeName: string;
|
typeName: string;
|
||||||
@@ -46,6 +46,8 @@ export type PluginDefine = Registrable & {
|
|||||||
default?: any;
|
default?: any;
|
||||||
group?: string;
|
group?: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
dependPlugins?: Record<string, string>;
|
||||||
|
dependPackages?: Record<string, string>;
|
||||||
input?: {
|
input?: {
|
||||||
[key: string]: TaskInputDefine;
|
[key: string]: TaskInputDefine;
|
||||||
};
|
};
|
||||||
@@ -73,6 +75,8 @@ export type ITaskPlugin = {
|
|||||||
onInstance(): Promise<void>;
|
onInstance(): Promise<void>;
|
||||||
execute(): Promise<void | string>;
|
execute(): Promise<void | string>;
|
||||||
onRequest(req: PluginRequestHandleReq<any>): Promise<any>;
|
onRequest(req: PluginRequestHandleReq<any>): Promise<any>;
|
||||||
|
setCtx(ctx: TaskInstanceContext): Promise<void>;
|
||||||
|
importRuntime?(specifier: string): Promise<any>;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -146,6 +150,14 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
|||||||
logger!: ILogger;
|
logger!: ILogger;
|
||||||
http!: HttpClient;
|
http!: HttpClient;
|
||||||
accessService!: IAccessService;
|
accessService!: IAccessService;
|
||||||
|
runtimeDepsService!: IRuntimeDepsService;
|
||||||
|
|
||||||
|
async importRuntime(specifier: string) {
|
||||||
|
if (!this.runtimeDepsService) {
|
||||||
|
throw new Error("runtimeDepsService 未初始化");
|
||||||
|
}
|
||||||
|
return await this.runtimeDepsService.importRuntime(specifier, this.logger);
|
||||||
|
}
|
||||||
|
|
||||||
clearLastStatus() {
|
clearLastStatus() {
|
||||||
this._result.clearLastStatus = true;
|
this._result.clearLastStatus = true;
|
||||||
@@ -161,11 +173,17 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setCtx(ctx: TaskInstanceContext) {
|
async setCtx(ctx: TaskInstanceContext) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.logger = ctx.logger;
|
this.logger = ctx.logger;
|
||||||
this.accessService = ctx.accessService;
|
this.accessService = ctx.accessService;
|
||||||
this.http = ctx.http;
|
this.http = ctx.http;
|
||||||
|
if (!this.runtimeDepsService && this.ctx.serviceGetter) {
|
||||||
|
this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService");
|
||||||
|
}
|
||||||
|
if (this.runtimeDepsService && this.ctx.define?.name) {
|
||||||
|
await this.runtimeDepsService.ensureRuntimeDependencies({ pluginKeys: `plugin:${this.ctx.define.name}`, logger: this.logger });
|
||||||
|
}
|
||||||
// 将证书加入secret
|
// 将证书加入secret
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (this.cert && this.cert.crt && this.cert.key) {
|
if (this.cert && this.cert.crt && this.cert.key) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export type Registrable = {
|
|||||||
group?: string;
|
group?: string;
|
||||||
deprecated?: string;
|
deprecated?: string;
|
||||||
order?: number;
|
order?: number;
|
||||||
|
icon?: string;
|
||||||
};
|
};
|
||||||
export type TargetGetter<T> = () => Promise<T>;
|
export type TargetGetter<T> = () => Promise<T>;
|
||||||
export type RegistryItem<T> = {
|
export type RegistryItem<T> = {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export class PipelineEmitter {
|
|||||||
}
|
}
|
||||||
off(event: string, listener: PipelineEventListener) {
|
off(event: string, listener: PipelineEventListener) {
|
||||||
if (this.events[event]) {
|
if (this.events[event]) {
|
||||||
this.events[event] = this.events[event].filter((l) => l !== listener);
|
this.events[event] = this.events[event].filter(l => l !== listener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
once(event: string, listener: PipelineEventListener) {
|
once(event: string, listener: PipelineEventListener) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export * from "./cname.js";
|
|||||||
export * from "./config.js";
|
export * from "./config.js";
|
||||||
export * from "./url.js";
|
export * from "./url.js";
|
||||||
export * from "./emit.js";
|
export * from "./emit.js";
|
||||||
|
export * from "./runtime.js";
|
||||||
export type IServiceGetter = {
|
export type IServiceGetter = {
|
||||||
get: <T>(name: string) => Promise<T>;
|
get: <T>(name: string) => Promise<T>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 运行时动态导入函数类型
|
||||||
|
*/
|
||||||
|
export type ImportRuntime = (specifier: string, logger?: ILogger) => Promise<any>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日志接口
|
||||||
|
*/
|
||||||
|
export type ILogger = {
|
||||||
|
info: (message: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运行时依赖服务参数
|
||||||
|
*/
|
||||||
|
export type EnsureRuntimeDepsOptions = {
|
||||||
|
pluginKeys: string | string[];
|
||||||
|
logger?: ILogger;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 运行时依赖服务接口
|
||||||
|
*/
|
||||||
|
export interface IRuntimeDepsService {
|
||||||
|
ensureRuntimeDependencies(options: EnsureRuntimeDepsOptions): Promise<any>;
|
||||||
|
importRuntime: ImportRuntime;
|
||||||
|
}
|
||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|
||||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-huawei
|
**Note:** Version bump only for package @certd/lib-huawei
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-huawei",
|
"name": "@certd/lib-huawei",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.41.0",
|
"version": "1.41.4",
|
||||||
"main": "./dist/bundle.js",
|
"main": "./dist/bundle.js",
|
||||||
"module": "./dist/bundle.js",
|
"module": "./dist/bundle.js",
|
||||||
"types": "./dist/d/index.d.ts",
|
"types": "./dist/d/index.d.ts",
|
||||||
@@ -12,20 +12,23 @@
|
|||||||
"dev-build": "npm run build",
|
"dev-build": "npm run build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||||
"pub": "npm publish"
|
"pub": "npm publish",
|
||||||
|
"compile": "npm run build",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"lint": "eslint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.9.0",
|
"axios": "^1.9.0",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"rollup": "^3.7.4"
|
"rollup": "4.50.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"esmock": "^2.7.5",
|
"esmock": "^2.7.5",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "3.3.3",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
},
|
},
|
||||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
"gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|
||||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-iframe
|
**Note:** Version bump only for package @certd/lib-iframe
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-iframe",
|
"name": "@certd/lib-iframe",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.41.0",
|
"version": "1.41.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -15,24 +15,27 @@
|
|||||||
"build2": "vue-tsc --noEmit && vite build",
|
"build2": "vue-tsc --noEmit && vite build",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||||
"pub": "npm publish"
|
"pub": "npm publish",
|
||||||
|
"compile": "npm run build",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"lint": "eslint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^4.0.0"
|
"nanoid": "^5.0.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.3.3",
|
"@types/chai": "^4.3.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.24.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"esmock": "^2.7.5",
|
"esmock": "^2.7.5",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "3.3.3",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
"gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|
||||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/jdcloud
|
**Note:** Version bump only for package @certd/jdcloud
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/jdcloud",
|
"name": "@certd/jdcloud",
|
||||||
"version": "1.41.0",
|
"version": "1.41.4",
|
||||||
"description": "jdcloud openApi sdk",
|
"description": "jdcloud openApi sdk",
|
||||||
"main": "./dist/bundle.js",
|
"main": "./dist/bundle.js",
|
||||||
"module": "./dist/bundle.js",
|
"module": "./dist/bundle.js",
|
||||||
@@ -10,7 +10,10 @@
|
|||||||
"build": "npm run before-build && rollup -c ",
|
"build": "npm run before-build && rollup -c ",
|
||||||
"dev-build": "npm run build",
|
"dev-build": "npm run build",
|
||||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||||
"pub": "npm publish"
|
"pub": "npm publish",
|
||||||
|
"compile": "npm run build",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"lint": "eslint --fix"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "Apache",
|
"license": "Apache",
|
||||||
@@ -21,7 +24,7 @@
|
|||||||
"debug": "^3.1.0",
|
"debug": "^3.1.0",
|
||||||
"node-fetch": "^2.1.2",
|
"node-fetch": "^2.1.2",
|
||||||
"querystring": "^0.2.0",
|
"querystring": "^0.2.0",
|
||||||
"rollup": "^3.7.4",
|
"rollup": "4.50.0",
|
||||||
"url": "^0.11.0",
|
"url": "^0.11.0",
|
||||||
"uuid": "^3.1.0"
|
"uuid": "^3.1.0"
|
||||||
},
|
},
|
||||||
@@ -29,13 +32,13 @@
|
|||||||
"@rollup/plugin-typescript": "^11.0.0",
|
"@rollup/plugin-typescript": "^11.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"chai": "^4.1.2",
|
"chai": "^5.1.0",
|
||||||
"config": "^1.30.0",
|
"config": "^1.30.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"esmock": "^2.7.5",
|
"esmock": "^2.7.5",
|
||||||
"js-yaml": "^3.11.0",
|
"js-yaml": "^3.11.0",
|
||||||
"mocha": "^5.0.0",
|
"mocha": "^10.6.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "3.3.3",
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -59,5 +62,5 @@
|
|||||||
"fetch"
|
"fetch"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
"gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|
||||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/lib-k8s
|
**Note:** Version bump only for package @certd/lib-k8s
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-k8s",
|
"name": "@certd/lib-k8s",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.41.0",
|
"version": "1.41.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -16,25 +16,27 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||||
"pub": "npm publish",
|
"pub": "npm publish",
|
||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"lint": "eslint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/basic": "^1.41.0",
|
"@certd/basic": "^1.41.4",
|
||||||
"@kubernetes/client-node": "0.21.0"
|
"@kubernetes/client-node": "0.21.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.3.3",
|
"@types/chai": "^4.3.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.24.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"esmock": "^2.7.5",
|
"esmock": "^2.7.5",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "3.3.3",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
"gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,22 @@
|
|||||||
{
|
{
|
||||||
"extends": "./node_modules/mwts/",
|
"parser": "@typescript-eslint/parser",
|
||||||
"ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"],
|
"plugins": [
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:prettier/recommended",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"jest": true
|
"mocha": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-var-requires": "off",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
|
"@typescript-eslint/ban-ts-ignore": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/lib-server
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
|
||||||
|
|
||||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/lib-server",
|
"name": "@certd/lib-server",
|
||||||
"version": "1.41.0",
|
"version": "1.41.4",
|
||||||
"description": "midway with flyway, sql upgrade way ",
|
"description": "midway with flyway, sql upgrade way ",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -15,11 +15,11 @@
|
|||||||
"test:unit": "cross-env NODE_ENV=unittest mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
"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'",
|
"test1": "midway-bin test --ts -V -f test/blank.test.ts -t 'hash-check'",
|
||||||
"cov": "midway-bin cov --ts",
|
"cov": "midway-bin cov --ts",
|
||||||
"lint": "mwts check",
|
|
||||||
"lint:fix": "mwts fix",
|
|
||||||
"prepublish": "npm run build",
|
"prepublish": "npm run build",
|
||||||
"pub": "npm publish",
|
"pub": "npm publish",
|
||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"lint": "eslint --fix"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "greper",
|
"author": "greper",
|
||||||
@@ -29,11 +29,11 @@
|
|||||||
],
|
],
|
||||||
"license": "AGPL",
|
"license": "AGPL",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^1.41.0",
|
"@certd/acme-client": "^1.41.4",
|
||||||
"@certd/basic": "^1.41.0",
|
"@certd/basic": "^1.41.4",
|
||||||
"@certd/pipeline": "^1.41.0",
|
"@certd/pipeline": "^1.41.4",
|
||||||
"@certd/plugin-lib": "^1.41.0",
|
"@certd/plugin-lib": "^1.41.4",
|
||||||
"@certd/plus-core": "^1.41.0",
|
"@certd/plus-core": "^1.41.4",
|
||||||
"@midwayjs/cache": "3.14.0",
|
"@midwayjs/cache": "3.14.0",
|
||||||
"@midwayjs/core": "3.20.11",
|
"@midwayjs/core": "3.20.11",
|
||||||
"@midwayjs/i18n": "3.20.13",
|
"@midwayjs/i18n": "3.20.13",
|
||||||
@@ -46,28 +46,28 @@
|
|||||||
"better-sqlite3": "^11.1.2",
|
"better-sqlite3": "^11.1.2",
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mwts": "^1.3.0",
|
|
||||||
"mwtsc": "^1.4.0",
|
|
||||||
"typeorm": "^0.3.20"
|
"typeorm": "^0.3.20"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.3.3",
|
"mwts": "^1.3.0",
|
||||||
"@types/mocha": "^10.0.1",
|
"mwtsc": "^1.4.0",
|
||||||
|
"@types/chai": "^4.3.12",
|
||||||
|
"@types/mocha": "^10.0.6",
|
||||||
"@types/node": "^18",
|
"@types/node": "^18",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.24.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"esmock": "^2.7.5",
|
"esmock": "^2.7.5",
|
||||||
"mocha": "^10.2.0",
|
"mocha": "^10.6.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "3.3.3",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.20",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
"gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export class SysPrivateSettings extends BaseSettings {
|
|||||||
|
|
||||||
httpsProxy? = '';
|
httpsProxy? = '';
|
||||||
httpProxy? = '';
|
httpProxy? = '';
|
||||||
|
noProxy? = '';
|
||||||
commonHeaders?: string = '';
|
commonHeaders?: string = '';
|
||||||
|
|
||||||
reverseProxies?: Record<string, string> = {};
|
reverseProxies?: Record<string, string> = {};
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
|
|||||||
const opts = {
|
const opts = {
|
||||||
httpProxy: privateSetting.httpProxy,
|
httpProxy: privateSetting.httpProxy,
|
||||||
httpsProxy: privateSetting.httpsProxy,
|
httpsProxy: privateSetting.httpsProxy,
|
||||||
|
noProxy: privateSetting.noProxy,
|
||||||
};
|
};
|
||||||
setGlobalProxy(opts);
|
setGlobalProxy(opts);
|
||||||
setGlobalHeaders(this.parseKeyValueText(privateSetting.commonHeaders));
|
setGlobalHeaders(this.parseKeyValueText(privateSetting.commonHeaders));
|
||||||
@@ -172,7 +173,6 @@ export class SysSettingsService extends BaseService<SysSettingsEntity> {
|
|||||||
if (privateSetting.dnsResultOrder) {
|
if (privateSetting.dnsResultOrder) {
|
||||||
dns.setDefaultResultOrder(privateSetting.dnsResultOrder as any);
|
dns.setDefaultResultOrder(privateSetting.dnsResultOrder as any);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (privateSetting.pipelineMaxRunningCount) {
|
if (privateSetting.pipelineMaxRunningCount) {
|
||||||
executorQueue.setMaxRunningCount(privateSetting.pipelineMaxRunningCount);
|
executorQueue.setMaxRunningCount(privateSetting.pipelineMaxRunningCount);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,29 @@
|
|||||||
import { IAccessService } from "@certd/pipeline";
|
import { IAccessService, IRuntimeDepsService } from "@certd/pipeline";
|
||||||
|
|
||||||
|
export type AccessRuntimeDepsService = IRuntimeDepsService;
|
||||||
|
|
||||||
export class AccessGetter implements IAccessService {
|
export class AccessGetter implements IAccessService {
|
||||||
userId: number;
|
userId: number;
|
||||||
projectId?: number;
|
projectId?: number;
|
||||||
getter: <T>(id: any, userId?: number, projectId?: number, ignorePermission?: boolean) => Promise<T>;
|
runtimeDepsService?: AccessRuntimeDepsService;
|
||||||
constructor(userId: number, projectId: number, getter: (id: any, userId: number, projectId?: number, ignorePermission?: boolean) => Promise<any>) {
|
getter: <T>(id: any, userId?: number, projectId?: number, ignorePermission?: boolean, runtimeDepsService?: AccessRuntimeDepsService) => Promise<T>;
|
||||||
|
constructor(
|
||||||
|
userId: number,
|
||||||
|
projectId: number,
|
||||||
|
getter: (id: any, userId: number, projectId?: number, ignorePermission?: boolean, runtimeDepsService?: AccessRuntimeDepsService) => Promise<any>,
|
||||||
|
runtimeDepsService?: AccessRuntimeDepsService
|
||||||
|
) {
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
this.projectId = projectId;
|
this.projectId = projectId;
|
||||||
this.getter = getter;
|
this.getter = getter;
|
||||||
|
this.runtimeDepsService = runtimeDepsService;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getById<T = any>(id: any) {
|
async getById<T = any>(id: any) {
|
||||||
return await this.getter<T>(id, this.userId, this.projectId);
|
return await this.getter<T>(id, this.userId, this.projectId, false, this.runtimeDepsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCommonById<T = any>(id: any) {
|
async getCommonById<T = any>(id: any) {
|
||||||
return await this.getter<T>(id, 0, null);
|
return await this.getter<T>(id, 0, null, false, this.runtimeDepsService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ import { Inject, Provide, Scope, ScopeEnum } from "@midwayjs/core";
|
|||||||
import { InjectEntityModel } from "@midwayjs/typeorm";
|
import { InjectEntityModel } from "@midwayjs/typeorm";
|
||||||
import { In, Repository } from "typeorm";
|
import { In, Repository } from "typeorm";
|
||||||
import { AccessGetter, BaseService, PageReq, PermissionException, ValidateException } from "../../../index.js";
|
import { AccessGetter, BaseService, PageReq, PermissionException, ValidateException } from "../../../index.js";
|
||||||
|
import type { AccessRuntimeDepsService } from "./access-getter.js";
|
||||||
import { AccessEntity } from "../entity/access.js";
|
import { AccessEntity } from "../entity/access.js";
|
||||||
import { AccessDefine, accessRegistry, newAccess } from "@certd/pipeline";
|
import { AccessDefine, accessRegistry, newAccess } from "@certd/pipeline";
|
||||||
import { EncryptService } from "./encrypt-service.js";
|
import { EncryptService } from "./encrypt-service.js";
|
||||||
import { logger, utils } from "@certd/basic";
|
import { http, logger, utils } from "@certd/basic";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 授权
|
* 授权
|
||||||
@@ -160,7 +161,7 @@ export class AccessService extends BaseService<AccessEntity> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAccessById(id: any, checkUserId: boolean, userId?: number, projectId?: number): Promise<any> {
|
async getAccessById(id: any, checkUserId: boolean, userId?: number, projectId?: number, runtimeDepsService?: AccessRuntimeDepsService): Promise<any> {
|
||||||
const entity = await this.info(id);
|
const entity = await this.info(id);
|
||||||
if (entity == null) {
|
if (entity == null) {
|
||||||
throw new Error(`该授权配置不存在,请确认是否已被删除:id=${id}`);
|
throw new Error(`该授权配置不存在,请确认是否已被删除:id=${id}`);
|
||||||
@@ -183,12 +184,20 @@ export class AccessService extends BaseService<AccessEntity> {
|
|||||||
id: entity.id,
|
id: entity.id,
|
||||||
...setting,
|
...setting,
|
||||||
};
|
};
|
||||||
const accessGetter = new AccessGetter(userId, projectId, this.getById.bind(this));
|
const getAccessById = this.getById.bind(this);
|
||||||
return await newAccess(entity.type, input, accessGetter);
|
const accessGetter = new AccessGetter(userId, projectId, getAccessById, runtimeDepsService);
|
||||||
|
const accessContext = {
|
||||||
|
logger,
|
||||||
|
http,
|
||||||
|
utils,
|
||||||
|
accessService: accessGetter,
|
||||||
|
} as any;
|
||||||
|
const access = await newAccess(entity.type, input, accessGetter, accessContext);
|
||||||
|
return access;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getById(id: any, userId: number, projectId?: number): Promise<any> {
|
async getById(id: any, userId: number, projectId?: number, _ignorePermission?: boolean, runtimeDepsService?: AccessRuntimeDepsService): Promise<any> {
|
||||||
return await this.getAccessById(id, true, userId, projectId);
|
return await this.getAccessById(id, true, userId, projectId, runtimeDepsService);
|
||||||
}
|
}
|
||||||
|
|
||||||
decryptAccessEntity(entity: AccessEntity): any {
|
decryptAccessEntity(entity: AccessEntity): any {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
accessRegistry,
|
accessRegistry,
|
||||||
FormItemProps,
|
FormItemProps,
|
||||||
IAccessService,
|
IAccessService,
|
||||||
|
IRuntimeDepsService,
|
||||||
IServiceGetter,
|
IServiceGetter,
|
||||||
PluginRequestHandleReq,
|
PluginRequestHandleReq,
|
||||||
Registrable
|
Registrable
|
||||||
@@ -27,6 +28,8 @@ export type AddonInputDefine = FormItemProps & {
|
|||||||
export type AddonDefine = Registrable & {
|
export type AddonDefine = Registrable & {
|
||||||
addonType: string;
|
addonType: string;
|
||||||
needPlus?: boolean;
|
needPlus?: boolean;
|
||||||
|
dependPlugins?: Record<string, string>;
|
||||||
|
dependPackages?: Record<string, string>;
|
||||||
input?: {
|
input?: {
|
||||||
[key: string]: AddonInputDefine;
|
[key: string]: AddonInputDefine;
|
||||||
};
|
};
|
||||||
@@ -64,15 +67,20 @@ export abstract class BaseAddon implements IAddon {
|
|||||||
ctx!: AddonContext;
|
ctx!: AddonContext;
|
||||||
http!: HttpClient;
|
http!: HttpClient;
|
||||||
logger!: ILogger;
|
logger!: ILogger;
|
||||||
|
runtimeDepsService?: IRuntimeDepsService;
|
||||||
|
|
||||||
|
async importRuntime(specifier: string) {
|
||||||
|
if (!this.runtimeDepsService) {
|
||||||
|
return await import(specifier);
|
||||||
|
}
|
||||||
|
return await this.runtimeDepsService.importRuntime(specifier, this.logger);
|
||||||
|
}
|
||||||
|
|
||||||
title!: string;
|
title!: string;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
async onInstance() {}
|
async onInstance() {}
|
||||||
|
|
||||||
|
|
||||||
async getAccess<T = any>(accessId: string | number, isCommon = false) {
|
async getAccess<T = any>(accessId: string | number, isCommon = false) {
|
||||||
if (accessId == null) {
|
if (accessId == null) {
|
||||||
throw new Error("您还没有配置授权");
|
throw new Error("您还没有配置授权");
|
||||||
@@ -106,11 +114,16 @@ export abstract class BaseAddon implements IAddon {
|
|||||||
return res as T;
|
return res as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setCtx(ctx: AddonContext) {
|
||||||
setCtx(ctx: AddonContext) {
|
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.http = ctx.http;
|
this.http = ctx.http;
|
||||||
this.logger = ctx.logger;
|
this.logger = ctx.logger;
|
||||||
|
if (!this.runtimeDepsService && this.ctx.serviceGetter) {
|
||||||
|
this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService");
|
||||||
|
}
|
||||||
|
if (this.runtimeDepsService && this.define?.addonType && this.define?.name) {
|
||||||
|
await this.runtimeDepsService.ensureRuntimeDependencies({ pluginKeys: `addon:${this.define.addonType}:${this.define.name}`, logger: this.logger });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
setDefine = (define:AddonDefine) => {
|
setDefine = (define:AddonDefine) => {
|
||||||
this.define = define;
|
this.define = define;
|
||||||
|
|||||||
@@ -63,9 +63,7 @@ export async function newAddon(addonType:string,type: string, input: any, ctx: A
|
|||||||
throw new Error("ctx is required");
|
throw new Error("ctx is required");
|
||||||
}
|
}
|
||||||
plugin.setDefine(register.define);
|
plugin.setDefine(register.define);
|
||||||
plugin.setCtx(ctx);
|
await plugin.setCtx(ctx);
|
||||||
await plugin.onInstance();
|
await plugin.onInstance();
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,22 @@
|
|||||||
{
|
{
|
||||||
"extends": "./node_modules/mwts/",
|
"parser": "@typescript-eslint/parser",
|
||||||
"ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"],
|
"plugins": [
|
||||||
|
"@typescript-eslint"
|
||||||
|
],
|
||||||
|
"extends": [
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:prettier/recommended",
|
||||||
|
"prettier"
|
||||||
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"jest": true
|
"mocha": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-var-requires": "off",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "off",
|
||||||
|
"@typescript-eslint/ban-ts-ignore": "off",
|
||||||
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|
||||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/midway-flyway-js
|
**Note:** Version bump only for package @certd/midway-flyway-js
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/midway-flyway-js",
|
"name": "@certd/midway-flyway-js",
|
||||||
"version": "1.41.0",
|
"version": "1.41.4",
|
||||||
"description": "midway with flyway, sql upgrade way ",
|
"description": "midway with flyway, sql upgrade way ",
|
||||||
"private": false,
|
"private": false,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -16,7 +16,10 @@
|
|||||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||||
"cov": "midway-bin cov --ts",
|
"cov": "midway-bin cov --ts",
|
||||||
"prepublish": "npm run build",
|
"prepublish": "npm run build",
|
||||||
"pub": "npm publish"
|
"pub": "npm publish",
|
||||||
|
"compile": "npm run build",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"lint": "eslint --fix"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "greper",
|
"author": "greper",
|
||||||
@@ -32,22 +35,22 @@
|
|||||||
"better-sqlite3": "^11.1.2"
|
"better-sqlite3": "^11.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.3.3",
|
"@types/chai": "^4.3.12",
|
||||||
"@types/node": "^18",
|
"@types/node": "^18",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.24.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"esmock": "^2.7.5",
|
"esmock": "^2.7.5",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "3.3.3",
|
||||||
"rimraf": "^5.0.5",
|
"rimraf": "^5.0.5",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typeorm": "^0.3.11",
|
"typeorm": "^0.3.20",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
"gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|
||||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/plugin-cert
|
**Note:** Version bump only for package @certd/plugin-cert
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugin-cert",
|
"name": "@certd/plugin-cert",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.41.0",
|
"version": "1.41.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -15,31 +15,28 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
"test:unit": "cross-env NODE_ENV=unittest echo no unit tests",
|
||||||
"pub": "npm publish",
|
"pub": "npm publish",
|
||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"lint": "eslint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@certd/acme-client": "^1.41.0",
|
"@certd/plugin-lib": "^1.41.4"
|
||||||
"@certd/basic": "^1.41.0",
|
|
||||||
"@certd/pipeline": "^1.41.0",
|
|
||||||
"@certd/plugin-lib": "^1.41.0",
|
|
||||||
"psl": "^1.9.0",
|
|
||||||
"punycode.js": "^2.3.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.3.3",
|
"@types/chai": "^4.3.12",
|
||||||
"@types/mocha": "^10.0.0",
|
"@types/mocha": "^10.0.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"chai": "^4.3.6",
|
"chai": "^5.1.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.24.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"esmock": "^2.7.5",
|
"esmock": "^2.7.5",
|
||||||
"mocha": "^10.1.0",
|
"mocha": "^10.6.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "3.3.3",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
"gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,24 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-lib
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-lib
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
**Note:** Version bump only for package @certd/plugin-lib
|
||||||
|
|
||||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
**Note:** Version bump only for package @certd/plugin-lib
|
**Note:** Version bump only for package @certd/plugin-lib
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/plugin-lib",
|
"name": "@certd/plugin-lib",
|
||||||
"private": false,
|
"private": false,
|
||||||
"version": "1.41.0",
|
"version": "1.41.4",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -15,51 +15,35 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"test:unit": "cross-env NODE_ENV=unittest mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"",
|
"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",
|
"pub": "npm publish",
|
||||||
"compile": "tsc --skipLibCheck --watch"
|
"compile": "tsc --skipLibCheck --watch",
|
||||||
|
"format": "prettier --write src",
|
||||||
|
"lint": "eslint --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alicloud/openapi-client": "^0.4.15",
|
"@certd/acme-client": "^1.41.4",
|
||||||
"@alicloud/openapi-util": "^0.3.2",
|
"@certd/basic": "^1.41.4",
|
||||||
"@alicloud/pop-core": "^1.7.10",
|
"@certd/pipeline": "^1.41.4",
|
||||||
"@alicloud/tea-util": "^1.4.11",
|
|
||||||
"@aws-sdk/client-s3": "^3.964.0",
|
|
||||||
"@certd/acme-client": "^1.41.0",
|
|
||||||
"@certd/basic": "^1.41.0",
|
|
||||||
"@certd/pipeline": "^1.41.0",
|
|
||||||
"@certd/plus-core": "^1.41.0",
|
|
||||||
"@kubernetes/client-node": "0.21.0",
|
|
||||||
"ali-oss": "^6.22.0",
|
|
||||||
"basic-ftp": "^5.0.5",
|
|
||||||
"cos-nodejs-sdk-v5": "^2.15.4",
|
|
||||||
"dayjs": "^1.11.7",
|
"dayjs": "^1.11.7",
|
||||||
"iconv-lite": "^0.6.3",
|
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"psl": "^1.15.0",
|
"psl": "^1.15.0"
|
||||||
"punycode.js": "^2.3.1",
|
|
||||||
"qiniu": "^7.12.0",
|
|
||||||
"rimraf": "^5.0.5",
|
|
||||||
"socks": "^2.8.3",
|
|
||||||
"socks-proxy-agent": "^8.0.4",
|
|
||||||
"ssh2": "1.17.0",
|
|
||||||
"strip-ansi": "^7.1.0",
|
|
||||||
"tencentcloud-sdk-nodejs": "^4.1.166"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/chai": "^4.3.3",
|
"rimraf": "^5.0.5",
|
||||||
"@types/mocha": "^10.0.0",
|
"@types/chai": "^4.3.12",
|
||||||
|
"@types/mocha": "^10.0.6",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^8.26.1",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"chai": "^4.3.6",
|
"chai": "^5.1.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "^8.24.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-prettier": "^4.2.1",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"esmock": "^2.7.5",
|
"esmock": "^2.7.5",
|
||||||
"mocha": "^10.1.0",
|
"mocha": "^10.6.0",
|
||||||
"prettier": "^2.8.8",
|
"prettier": "3.3.3",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"typescript": "^5.4.2"
|
"typescript": "^5.4.2"
|
||||||
},
|
},
|
||||||
"gitHead": "7ceb0f6306b8b5e9ab875b9f7c41cc7d56209ea4"
|
"gitHead": "bc731e4fb119787930e816a7d57c808b1b5cd66a"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ export class CertConverter {
|
|||||||
if (!fs.existsSync(dir)) {
|
if (!fs.existsSync(dir)) {
|
||||||
fs.mkdirSync(dir, { recursive: true });
|
fs.mkdirSync(dir, { recursive: true });
|
||||||
}
|
}
|
||||||
await this.exec(`keytool -importkeystore -srckeystore ${p12Path} -srcstoretype PKCS12 -srcstorepass "${jksPassword}" -destkeystore ${jksPath} -deststoretype PKCS12 -deststorepass "${jksPassword}" `);
|
await this.exec(`keytool -importkeystore -srckeystore ${p12Path} -srcstoretype PKCS12 -srcstorepass "${jksPassword}" -destkeystore ${jksPath} -deststoretype JKS -deststorepass "${jksPassword}" `);
|
||||||
fs.unlinkSync(p12Path);
|
fs.unlinkSync(p12Path);
|
||||||
|
|
||||||
const fileBuffer = fs.readFileSync(jksPath);
|
const fileBuffer = fs.readFileSync(jksPath);
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { IAccess, IAccessService, IServiceGetter, PageRes, PageSearch, Registrab
|
|||||||
export type DnsProviderDefine = Registrable & {
|
export type DnsProviderDefine = Registrable & {
|
||||||
accessType: string;
|
accessType: string;
|
||||||
icon?: string;
|
icon?: string;
|
||||||
|
dependPlugins?: Record<string, string>;
|
||||||
|
dependPackages?: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type CreateRecordOptions = {
|
export type CreateRecordOptions = {
|
||||||
@@ -27,6 +29,7 @@ export type DnsProviderContext = {
|
|||||||
domainParser: IDomainParser;
|
domainParser: IDomainParser;
|
||||||
serviceGetter: IServiceGetter;
|
serviceGetter: IServiceGetter;
|
||||||
accessGetter?: IAccessService;
|
accessGetter?: IAccessService;
|
||||||
|
define?: DnsProviderDefine;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DomainRecord = {
|
export type DomainRecord = {
|
||||||
@@ -34,6 +37,14 @@ export type DomainRecord = {
|
|||||||
domain: string;
|
domain: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type DnsResolveRecord = {
|
||||||
|
id: string;
|
||||||
|
hostRecord: string;
|
||||||
|
fullRecord: string;
|
||||||
|
type: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
export interface IDnsProvider<T = any> {
|
export interface IDnsProvider<T = any> {
|
||||||
onInstance(): Promise<void>;
|
onInstance(): Promise<void>;
|
||||||
|
|
||||||
@@ -53,12 +64,14 @@ export interface IDnsProvider<T = any> {
|
|||||||
|
|
||||||
removeRecord(options: RemoveRecordOptions<T>): Promise<void>;
|
removeRecord(options: RemoveRecordOptions<T>): Promise<void>;
|
||||||
|
|
||||||
setCtx(ctx: DnsProviderContext): void;
|
setCtx(ctx: DnsProviderContext): Promise<void>;
|
||||||
|
|
||||||
//中文域名是否需要punycode转码,如果返回True,则使用punycode来添加解析记录,否则使用中文域名添加解析记录
|
//中文域名是否需要punycode转码,如果返回True,则使用punycode来添加解析记录,否则使用中文域名添加解析记录
|
||||||
usePunyCode(): boolean;
|
usePunyCode(): boolean;
|
||||||
|
|
||||||
getDomainListPage(pager: PageSearch): Promise<PageRes<DomainRecord>>;
|
getDomainListPage(pager: PageSearch): Promise<PageRes<DomainRecord>>;
|
||||||
|
|
||||||
|
getRecordListPage?(domain: string, pager: PageSearch): Promise<PageRes<DnsResolveRecord>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISubDomainsGetter {
|
export interface ISubDomainsGetter {
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
import { HttpClient, ILogger } from "@certd/basic";
|
import { HttpClient, ILogger } from "@certd/basic";
|
||||||
import { IAccessService, PageRes, PageSearch } from "@certd/pipeline";
|
import { IAccessService, IRuntimeDepsService, PageRes, PageSearch } from "@certd/pipeline";
|
||||||
import punycode from "punycode.js";
|
import punycode from "punycode.js";
|
||||||
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, DomainRecord, IDnsProvider, RemoveRecordOptions } from "./api.js";
|
import { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, DnsResolveRecord, DomainRecord, IDnsProvider, RemoveRecordOptions } from "./api.js";
|
||||||
import { dnsProviderRegistry } from "./registry.js";
|
import { dnsProviderRegistry } from "./registry.js";
|
||||||
export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
||||||
ctx!: DnsProviderContext;
|
ctx!: DnsProviderContext;
|
||||||
http!: HttpClient;
|
http!: HttpClient;
|
||||||
logger!: ILogger;
|
logger!: ILogger;
|
||||||
|
runtimeDepsService?: IRuntimeDepsService;
|
||||||
|
|
||||||
|
async importRuntime(specifier: string) {
|
||||||
|
if (!this.runtimeDepsService) {
|
||||||
|
throw new Error("runtimeDepsService 未初始化");
|
||||||
|
}
|
||||||
|
return await this.runtimeDepsService.importRuntime(specifier, this.logger);
|
||||||
|
}
|
||||||
|
|
||||||
usePunyCode(): boolean {
|
usePunyCode(): boolean {
|
||||||
//是否使用punycode来添加解析记录
|
//是否使用punycode来添加解析记录
|
||||||
@@ -30,10 +38,16 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
|||||||
return punycode.toUnicode(domain);
|
return punycode.toUnicode(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
setCtx(ctx: DnsProviderContext) {
|
async setCtx(ctx: DnsProviderContext) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.logger = ctx.logger;
|
this.logger = ctx.logger;
|
||||||
this.http = ctx.http;
|
this.http = ctx.http;
|
||||||
|
if (!this.runtimeDepsService && this.ctx.serviceGetter) {
|
||||||
|
this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService");
|
||||||
|
}
|
||||||
|
if (this.runtimeDepsService && this.ctx.define?.name) {
|
||||||
|
await this.runtimeDepsService.ensureRuntimeDependencies({ pluginKeys: `dnsProvider:${this.ctx.define.name}`, logger: this.logger });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async parseDomain(fullDomain: string) {
|
async parseDomain(fullDomain: string) {
|
||||||
@@ -49,6 +63,10 @@ export abstract class AbstractDnsProvider<T = any> implements IDnsProvider<T> {
|
|||||||
async getDomainListPage(req: PageSearch): Promise<PageRes<DomainRecord>> {
|
async getDomainListPage(req: PageSearch): Promise<PageRes<DomainRecord>> {
|
||||||
throw new Error("Method not implemented.");
|
throw new Error("Method not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getRecordListPage(domain: string, req: PageSearch): Promise<PageRes<DnsResolveRecord>> {
|
||||||
|
throw new Error("Method not implemented.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createDnsProvider(opts: { dnsProviderType: string; context: DnsProviderContext }): Promise<IDnsProvider> {
|
export async function createDnsProvider(opts: { dnsProviderType: string; context: DnsProviderContext }): Promise<IDnsProvider> {
|
||||||
@@ -64,9 +82,10 @@ export async function createDnsProvider(opts: { dnsProviderType: string; context
|
|||||||
const accessGetter: IAccessService = await context.serviceGetter.get("accessService");
|
const accessGetter: IAccessService = await context.serviceGetter.get("accessService");
|
||||||
context.accessGetter = accessGetter;
|
context.accessGetter = accessGetter;
|
||||||
}
|
}
|
||||||
|
context.define = dnsProviderDefine;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const dnsProvider: IDnsProvider = new DnsProviderClass();
|
const dnsProvider: IDnsProvider = new DnsProviderClass();
|
||||||
dnsProvider.setCtx(context);
|
await dnsProvider.setCtx(context);
|
||||||
await dnsProvider.onInstance();
|
await dnsProvider.onInstance();
|
||||||
return dnsProvider;
|
return dnsProvider;
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-30
@@ -1,12 +1,9 @@
|
|||||||
FROM node:22-alpine3.21 AS builder
|
# 根据目标平台选择基础镜像:amd64/arm64 用 trixie-slim,arm/v7 没有 trixie-slim 发布,回退到 alpine
|
||||||
|
FROM --platform=linux/amd64 node:22-trixie-slim AS base-amd64
|
||||||
# RUN apk add build-base
|
FROM --platform=linux/arm64 node:22-trixie-slim AS base-arm64
|
||||||
# RUN wget -O - https://github.com/jemalloc/jemalloc/releases/download/5.3.0/jemalloc-5.3.0.tar.bz2 | tar -xj && \
|
FROM --platform=linux/arm/v7 node:22-alpine AS base-arm-v7
|
||||||
# cd jemalloc-5.3.0 && \
|
|
||||||
# ./configure && \
|
|
||||||
# make && \
|
|
||||||
# make install
|
|
||||||
|
|
||||||
|
FROM base-${TARGETARCH}${TARGETVARIANT:+-}${TARGETVARIANT} AS builder
|
||||||
|
|
||||||
WORKDIR /workspace/
|
WORKDIR /workspace/
|
||||||
COPY . /workspace/
|
COPY . /workspace/
|
||||||
@@ -14,34 +11,44 @@ COPY . /workspace/
|
|||||||
# https://pnpm.io/zh/migration
|
# https://pnpm.io/zh/migration
|
||||||
RUN npm install -g pnpm@10.33.4
|
RUN npm install -g pnpm@10.33.4
|
||||||
|
|
||||||
#RUN cd /workspace/certd-client && pnpm install && npm run build
|
|
||||||
RUN cp /workspace/certd-client/dist/* /workspace/certd-server/public/ -rf
|
RUN cp /workspace/certd-client/dist/* /workspace/certd-server/public/ -rf
|
||||||
RUN cd /workspace/certd-server && pnpm install && npm run build-on-docker
|
RUN cd /workspace/certd-server && pnpm install && npm run build-on-docker
|
||||||
|
|
||||||
# RUN cd /workspace/certd-server && \
|
|
||||||
# pnpm install --ignore-scripts && \
|
|
||||||
# yes | pnpm approve-builds && \
|
|
||||||
# pnpm rebuild && \
|
|
||||||
# npm run build-on-docker
|
|
||||||
|
|
||||||
|
FROM base-${TARGETARCH}${TARGETVARIANT:+-}${TARGETVARIANT}
|
||||||
FROM node:22-alpine3.21
|
|
||||||
EXPOSE 7001
|
EXPOSE 7001
|
||||||
EXPOSE 7002
|
EXPOSE 7002
|
||||||
|
|
||||||
# 安装jemalloc内存分配器,优化内存占用 -- 基本没用,反而更高了
|
# 根据基础镜像发行版选择包管理器
|
||||||
# COPY --from=builder /usr/local/lib/libjemalloc.so.2 /usr/local/lib/
|
# trixie-slim -> apt-get, alpine -> apk
|
||||||
# ENV LD_PRELOAD=/usr/local/lib/libjemalloc.so.2
|
RUN if [ -f /etc/debian_version ]; then \
|
||||||
|
apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
ca-certificates \
|
||||||
|
gnupg \
|
||||||
|
wget \
|
||||||
|
openssl \
|
||||||
|
netcat-openbsd \
|
||||||
|
iputils-ping \
|
||||||
|
dnsutils \
|
||||||
|
iproute2 \
|
||||||
|
&& wget -O - https://packages.adoptium.net/artifactory/api/gpg/key/public | gpg --dearmor | tee /usr/share/keyrings/adoptium.gpg > /dev/null \
|
||||||
|
&& echo "deb [signed-by=/usr/share/keyrings/adoptium.gpg] https://packages.adoptium.net/artifactory/deb bookworm main" | tee /etc/apt/sources.list.d/adoptium.list \
|
||||||
|
&& apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends temurin-8-jre \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*; \
|
||||||
|
elif [ -f /etc/alpine-release ]; then \
|
||||||
|
apk add --no-cache \
|
||||||
|
openssl \
|
||||||
|
openjdk8-jre; \
|
||||||
|
else \
|
||||||
|
echo "Unsupported base image"; exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
RUN apk add --no-cache openssl
|
|
||||||
RUN apk add --no-cache openjdk8
|
|
||||||
RUN apk add --no-cache gcompat
|
|
||||||
WORKDIR /app/
|
WORKDIR /app/
|
||||||
COPY --from=builder /workspace/certd-server/ /app/
|
|
||||||
|
|
||||||
COPY ./patch/ssh2/*.js /app/node_modules/.pnpm/node_modules/ssh2/lib/protocol/
|
|
||||||
|
|
||||||
|
ENV TERM=xterm
|
||||||
ENV LEGO_VERSION=4.30.1
|
ENV LEGO_VERSION=4.30.1
|
||||||
ENV LEGO_DOWNLOAD_DIR=/app/tools/lego
|
ENV LEGO_DOWNLOAD_DIR=/app/tools/lego
|
||||||
|
|
||||||
@@ -57,14 +64,14 @@ RUN ARCH=$(uname -m) && \
|
|||||||
elif [ "$ARCH" = "aarch64" ]; then \
|
elif [ "$ARCH" = "aarch64" ]; then \
|
||||||
wget -O $LEGO_DOWNLOAD_DIR/lego_v${LEGO_VERSION}_linux_arm64.tar.gz https://github.com/go-acme/lego/releases/download/v${LEGO_VERSION}/lego_v${LEGO_VERSION}_linux_arm64.tar.gz; \
|
wget -O $LEGO_DOWNLOAD_DIR/lego_v${LEGO_VERSION}_linux_arm64.tar.gz https://github.com/go-acme/lego/releases/download/v${LEGO_VERSION}/lego_v${LEGO_VERSION}_linux_arm64.tar.gz; \
|
||||||
else \
|
else \
|
||||||
|
# armv7 不支持lego 不要再尝试了
|
||||||
echo "Unsupported architecture: $ARCH"; \
|
echo "Unsupported architecture: $ARCH"; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
ENV TZ=Asia/Shanghai
|
ENV TZ=Asia/Shanghai
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV MIDWAY_SERVER_ENV=production
|
ENV MIDWAY_SERVER_ENV=production
|
||||||
|
|
||||||
|
COPY --from=builder /workspace/certd-server/ /app/
|
||||||
|
COPY ./patch/ssh2/*.js /app/node_modules/.pnpm/node_modules/ssh2/lib/protocol/
|
||||||
CMD ["node", "--optimize-for-size", "./bootstrap.js"]
|
CMD ["node", "--optimize-for-size", "./bootstrap.js"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ VITE_APP_PM_ENABLED=true
|
|||||||
VITE_APP_TITLE=Certd
|
VITE_APP_TITLE=Certd
|
||||||
VITE_APP_SLOGAN=让你的证书永不过期
|
VITE_APP_SLOGAN=让你的证书永不过期
|
||||||
VITE_APP_COPYRIGHT_YEAR=2021-2026
|
VITE_APP_COPYRIGHT_YEAR=2021-2026
|
||||||
VITE_APP_COPYRIGHT_NAME=handsfree.work
|
VITE_APP_COPYRIGHT_NAME=handfree.work
|
||||||
VITE_APP_COPYRIGHT_URL=https://certd.handsfree.work
|
VITE_APP_COPYRIGHT_URL=https://certd.handfree.work
|
||||||
VITE_APP_LOGO=static/images/logo/logo.svg
|
VITE_APP_LOGO=static/images/logo/logo.svg
|
||||||
VITE_APP_LOGIN_LOGO=static/images/logo/rect-black.svg
|
VITE_APP_LOGIN_LOGO=static/images/logo/rect-black.svg
|
||||||
VITE_APP_PROJECT_PATH=https://github.com/certd/certd
|
VITE_APP_PROJECT_PATH=https://github.com/certd/certd
|
||||||
|
|||||||
@@ -3,6 +3,37 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||||
|
|
||||||
|
## [1.41.4](https://github.com/certd/certd/compare/v1.41.3...v1.41.4) (2026-06-14)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* 修复设置里面不显示tab页签,导致某些页面需要点击查询按钮才有数据出来的bug ([c1b5a35](https://github.com/certd/certd/commit/c1b5a35f90a7d4b41397717b5c27905bc68e1bfb))
|
||||||
|
|
||||||
|
## [1.41.3](https://github.com/certd/certd/compare/v1.41.2...v1.41.3) (2026-06-11)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 首页夜间模式主图切换为黑色背景 ([15484bc](https://github.com/certd/certd/commit/15484bc119fef7a0ca7f3fdab01d665fde47e688))
|
||||||
|
|
||||||
|
## [1.41.2](https://github.com/certd/certd/compare/v1.41.1...v1.41.2) (2026-06-10)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **cert-plugin:** 修复DNS提供商授权无法回显的bug ([016ae86](https://github.com/certd/certd/commit/016ae865b1d914fe5792e77a08e3ab5358df5f89))
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 新增站点证书监控从DNS解析记录批量导入功能 ([f9541fa](https://github.com/certd/certd/commit/f9541fab701e01ba57af061da322204c894adfb8))
|
||||||
|
|
||||||
|
## [1.41.1](https://github.com/certd/certd/compare/v1.41.0...v1.41.1) (2026-06-05)
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* 流水线、监控站点支持导出 ([99fd308](https://github.com/certd/certd/commit/99fd3083f259cdb96fd656f04858dd708d1251c7))
|
||||||
|
* 优化列表页面请求两次的问题 ([5546af5](https://github.com/certd/certd/commit/5546af518e92c765513787ccaf8e856be789bcf9))
|
||||||
|
* 优化邀请注册流程 ([7a71e45](https://github.com/certd/certd/commit/7a71e45799d782d0691606fb42b4236f1d3009b0))
|
||||||
|
* **settings:** 新增NO_PROXY代理排除配置 ([c0df8be](https://github.com/certd/certd/commit/c0df8be83237e323c2c9a5bd02507430a86a00cc))
|
||||||
|
|
||||||
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
# [1.41.0](https://github.com/certd/certd/compare/v1.40.5...v1.41.0) (2026-06-04)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@certd/ui-client",
|
"name": "@certd/ui-client",
|
||||||
"version": "1.41.0",
|
"version": "1.41.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --open",
|
"dev": "vite --open",
|
||||||
@@ -74,7 +74,7 @@
|
|||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"monaco-editor": "^0.52.2",
|
"monaco-editor": "^0.52.2",
|
||||||
"monaco-yaml": "^5.3.1",
|
"monaco-yaml": "^5.3.1",
|
||||||
"nanoid": "^4.0.0",
|
"nanoid": "^5.0.7",
|
||||||
"node-forge": "^1.3.1",
|
"node-forge": "^1.3.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
@@ -93,7 +93,6 @@
|
|||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"theme-colors": "^0.1.0",
|
"theme-colors": "^0.1.0",
|
||||||
"vee-validate": "^4.15.0",
|
"vee-validate": "^4.15.0",
|
||||||
"vitest": "^0.34.6",
|
|
||||||
"vue": "^3.4.21",
|
"vue": "^3.4.21",
|
||||||
"vue-cropperjs": "^5.0.0",
|
"vue-cropperjs": "^5.0.0",
|
||||||
"vue-echarts": "^7.0.3",
|
"vue-echarts": "^7.0.3",
|
||||||
@@ -106,8 +105,8 @@
|
|||||||
"zod-defaults": "^0.1.3"
|
"zod-defaults": "^0.1.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@certd/lib-iframe": "^1.41.0",
|
"@certd/lib-iframe": "^1.41.4",
|
||||||
"@certd/pipeline": "^1.41.0",
|
"@certd/pipeline": "^1.41.4",
|
||||||
"@rollup/plugin-commonjs": "^25.0.7",
|
"@rollup/plugin-commonjs": "^25.0.7",
|
||||||
"@rollup/plugin-node-resolve": "^15.2.3",
|
"@rollup/plugin-node-resolve": "^15.2.3",
|
||||||
"@types/chai": "^4.3.12",
|
"@types/chai": "^4.3.12",
|
||||||
@@ -115,8 +114,8 @@
|
|||||||
"@types/mocha": "^10.0.6",
|
"@types/mocha": "^10.0.6",
|
||||||
"@types/node": "^18",
|
"@types/node": "^18",
|
||||||
"@types/nprogress": "^0.2.3",
|
"@types/nprogress": "^0.2.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
"@typescript-eslint/eslint-plugin": "^8.26.1",
|
||||||
"@typescript-eslint/parser": "^7.2.0",
|
"@typescript-eslint/parser": "^8.26.1",
|
||||||
"@vitejs/plugin-legacy": "^5.3.2",
|
"@vitejs/plugin-legacy": "^5.3.2",
|
||||||
"@vitejs/plugin-vue": "^5.0.4",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||||
@@ -129,7 +128,7 @@
|
|||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"dependency-cruiser": "^16.2.3",
|
"dependency-cruiser": "^16.2.3",
|
||||||
"dot": "^1.1.3",
|
"dot": "^1.1.3",
|
||||||
"eslint": "8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB |
+7
-3
@@ -14,7 +14,7 @@ export default {
|
|||||||
default: undefined,
|
default: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
emits: ["update:modelValue", "selected-change"],
|
emits: ["update:modelValue", "selected-change", "change"],
|
||||||
setup(props: any, ctx: any) {
|
setup(props: any, ctx: any) {
|
||||||
const options = ref<any[]>([]);
|
const options = ref<any[]>([]);
|
||||||
|
|
||||||
@@ -33,7 +33,8 @@ export default {
|
|||||||
// if (props.modelValue == null && options.value.length > 0) {
|
// if (props.modelValue == null && options.value.length > 0) {
|
||||||
// ctx.emit("update:modelValue", options.value[0].value);
|
// ctx.emit("update:modelValue", options.value[0].value);
|
||||||
// }
|
// }
|
||||||
onSelectedChange(props.modelValue);
|
//这里需要一个第一次的selected-change事件,外部表单字段有情况会用到选中的option
|
||||||
|
onSelectedChange(props.modelValue, true);
|
||||||
}
|
}
|
||||||
onCreate();
|
onCreate();
|
||||||
|
|
||||||
@@ -41,9 +42,12 @@ export default {
|
|||||||
ctx.emit("update:modelValue", value);
|
ctx.emit("update:modelValue", value);
|
||||||
onSelectedChange(value);
|
onSelectedChange(value);
|
||||||
}
|
}
|
||||||
function onSelectedChange(value: any) {
|
function onSelectedChange(value: any, isFirst: boolean = false) {
|
||||||
if (value) {
|
if (value) {
|
||||||
const option = options.value.find(item => item.value == value);
|
const option = options.value.find(item => item.value == value);
|
||||||
|
if (!isFirst) {
|
||||||
|
ctx.emit("change", value);
|
||||||
|
}
|
||||||
if (option) {
|
if (option) {
|
||||||
ctx.emit("selected-change", option);
|
ctx.emit("selected-change", option);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export default {
|
|||||||
subdomainConfirmTitle: "Subdomain Confirmation",
|
subdomainConfirmTitle: "Subdomain Confirmation",
|
||||||
subdomainConfirmContent: "{domain} appears to be a subdomain. Only delegated subdomains and free second-level subdomains need to be maintained here. Otherwise certificate application may fail. Continue?",
|
subdomainConfirmContent: "{domain} appears to be a subdomain. Only delegated subdomains and free second-level subdomains need to be maintained here. Otherwise certificate application may fail. Continue?",
|
||||||
importFromProvider: "Import from Domain Provider",
|
importFromProvider: "Import from Domain Provider",
|
||||||
|
importFromResolveRecords: "Import from DNS Records",
|
||||||
syncExpirationDate: "Sync Domain Expiration Time",
|
syncExpirationDate: "Sync Domain Expiration Time",
|
||||||
syncTaskSubmitted: "Sync task submitted",
|
syncTaskSubmitted: "Sync task submitted",
|
||||||
syncExpirationProgress: "Sync Domain Expiration Progress",
|
syncExpirationProgress: "Sync Domain Expiration Progress",
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export default {
|
|||||||
pipelineContent: "Pipeline Content",
|
pipelineContent: "Pipeline Content",
|
||||||
scheduledTaskCount: "Scheduled Task Count",
|
scheduledTaskCount: "Scheduled Task Count",
|
||||||
deployTaskCount: "Deployment Task Count",
|
deployTaskCount: "Deployment Task Count",
|
||||||
|
certDomains: "Certificate Domains",
|
||||||
remainingValidity: "Remaining Validity",
|
remainingValidity: "Remaining Validity",
|
||||||
effectiveTime: "Effective time",
|
effectiveTime: "Effective time",
|
||||||
expiryTime: "Expiry Time",
|
expiryTime: "Expiry Time",
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export default {
|
|||||||
pi: {
|
pi: {
|
||||||
validTime: "Piepline Valid Time",
|
validTime: "Piepline Valid Time",
|
||||||
validTimeHelper: "Not filled in means permanent validity",
|
validTimeHelper: "Not filled in means permanent validity",
|
||||||
|
permanentValid: "Permanent",
|
||||||
},
|
},
|
||||||
types: {
|
types: {
|
||||||
certApply: "Cert Apply",
|
certApply: "Cert Apply",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export default {
|
|||||||
"The domain name configured here serves as a proxy for verifying other domains. When other domains apply for certificates, they map to this domain via CNAME for ownership verification. The advantage is that any domain can apply for a certificate this way without providing an AccessSecret.",
|
"The domain name configured here serves as a proxy for verifying other domains. When other domains apply for certificates, they map to this domain via CNAME for ownership verification. The advantage is that any domain can apply for a certificate this way without providing an AccessSecret.",
|
||||||
cnameLinkText: "CNAME principle and usage instructions",
|
cnameLinkText: "CNAME principle and usage instructions",
|
||||||
cnameDomain: "CNAME Domain",
|
cnameDomain: "CNAME Domain",
|
||||||
cnameDomainPlaceholder: "cname.handsfree.work",
|
cnameDomainPlaceholder: "cname.handfree.work",
|
||||||
cnameDomainHelper:
|
cnameDomainHelper:
|
||||||
"Requires a domain registered with a DNS provider on the right (or you can transfer other domain DNS servers here).\nOnce the CNAME domain is set, it cannot be changed. It is recommended to use a first-level subdomain.",
|
"Requires a domain registered with a DNS provider on the right (or you can transfer other domain DNS servers here).\nOnce the CNAME domain is set, it cannot be changed. It is recommended to use a first-level subdomain.",
|
||||||
cnameDomainPattern: "Domain name cannot contain *",
|
cnameDomainPattern: "Domain name cannot contain *",
|
||||||
|
|||||||
@@ -40,4 +40,8 @@ export default {
|
|||||||
pluginManagement: "Plugin Management",
|
pluginManagement: "Plugin Management",
|
||||||
pluginBetaWarning: "Custom plugins are in BETA and may have breaking changes in future",
|
pluginBetaWarning: "Custom plugins are in BETA and may have breaking changes in future",
|
||||||
pleaseSelectRecord: "Please select records first",
|
pleaseSelectRecord: "Please select records first",
|
||||||
|
clearRuntimeDeps: "Clear Runtime Deps Cache",
|
||||||
|
clearRuntimeDepsTooltip: "Restart the certd container after clearing, otherwise cached modules will not be reloaded",
|
||||||
|
clearRuntimeDepsConfirm: "Are you sure to clear the runtime dependencies cache? Please restart the certd container afterwards to ensure dependencies are reloaded.",
|
||||||
|
clearRuntimeDepsSuccess: "Runtime dependencies cache cleared successfully, please restart the certd container",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -111,6 +111,9 @@ export default {
|
|||||||
httpsProxyPlaceholder: "http://192.168.1.2:18010/",
|
httpsProxyPlaceholder: "http://192.168.1.2:18010/",
|
||||||
saveThenTestTitle: "Save first, then click test",
|
saveThenTestTitle: "Save first, then click test",
|
||||||
httpsProxyHelper: "Usually both proxies are the same, save first then test",
|
httpsProxyHelper: "Usually both proxies are the same, save first then test",
|
||||||
|
noProxy: "Proxy Bypass",
|
||||||
|
noProxyPlaceholder: "localhost,127.0.0.1,.example.com,192.168.*",
|
||||||
|
noProxyHelper: "Configure NO_PROXY. Separate entries with commas, spaces, or line breaks; matched requests bypass the proxy. \nExample: localhost,127.0.0.1,.example.com,192.168.*",
|
||||||
dualStackNetwork: "Dual Stack Network",
|
dualStackNetwork: "Dual Stack Network",
|
||||||
ipv4Priority: "IPv4 Priority",
|
ipv4Priority: "IPv4 Priority",
|
||||||
ipv6Priority: "IPv6 Priority",
|
ipv6Priority: "IPv6 Priority",
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ export default {
|
|||||||
subdomainConfirmTitle: "子域名确认",
|
subdomainConfirmTitle: "子域名确认",
|
||||||
subdomainConfirmContent: "检测到{domain}为子域名,只有托管子域名和免费二级子域名才需要在此处维护,否则会导致申请证书失败,请确认是否继续?",
|
subdomainConfirmContent: "检测到{domain}为子域名,只有托管子域名和免费二级子域名才需要在此处维护,否则会导致申请证书失败,请确认是否继续?",
|
||||||
importFromProvider: "从域名提供商导入",
|
importFromProvider: "从域名提供商导入",
|
||||||
|
importFromResolveRecords: "从解析记录导入",
|
||||||
syncExpirationDate: "同步域名过期时间",
|
syncExpirationDate: "同步域名过期时间",
|
||||||
syncTaskSubmitted: "同步任务已提交",
|
syncTaskSubmitted: "同步任务已提交",
|
||||||
syncExpirationProgress: "同步域名过期时间进度",
|
syncExpirationProgress: "同步域名过期时间进度",
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ export default {
|
|||||||
pipelineContent: "流水线内容",
|
pipelineContent: "流水线内容",
|
||||||
scheduledTaskCount: "定时任务数",
|
scheduledTaskCount: "定时任务数",
|
||||||
deployTaskCount: "部署任务数",
|
deployTaskCount: "部署任务数",
|
||||||
|
certDomains: "证书域名",
|
||||||
remainingValidity: "到期剩余",
|
remainingValidity: "到期剩余",
|
||||||
effectiveTime: "生效时间",
|
effectiveTime: "生效时间",
|
||||||
expiryTime: "过期时间",
|
expiryTime: "过期时间",
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export default {
|
|||||||
pi: {
|
pi: {
|
||||||
validTime: "流水线有效期",
|
validTime: "流水线有效期",
|
||||||
validTimeHelper: "不填则为永久有效",
|
validTimeHelper: "不填则为永久有效",
|
||||||
|
permanentValid: "永久有效",
|
||||||
},
|
},
|
||||||
types: {
|
types: {
|
||||||
certApply: "证书申请",
|
certApply: "证书申请",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ export default {
|
|||||||
cnameDescription: "此处配置的域名作为其他域名校验的代理,当别的域名需要申请证书时,通过CNAME映射到此域名上来验证所有权。好处是任何域名都可以通过此方式申请证书,也无需填写AccessSecret。",
|
cnameDescription: "此处配置的域名作为其他域名校验的代理,当别的域名需要申请证书时,通过CNAME映射到此域名上来验证所有权。好处是任何域名都可以通过此方式申请证书,也无需填写AccessSecret。",
|
||||||
cnameLinkText: "CNAME功能原理及使用说明",
|
cnameLinkText: "CNAME功能原理及使用说明",
|
||||||
cnameDomain: "CNAME域名",
|
cnameDomain: "CNAME域名",
|
||||||
cnameDomainPlaceholder: "cname.handsfree.work",
|
cnameDomainPlaceholder: "cname.handfree.work",
|
||||||
cnameDomainHelper: "需要一个右边DNS提供商注册的域名(也可以将其他域名的dns服务器转移到这几家来)。\nCNAME域名一旦确定不可修改,建议使用一级子域名",
|
cnameDomainHelper: "需要一个右边DNS提供商注册的域名(也可以将其他域名的dns服务器转移到这几家来)。\nCNAME域名一旦确定不可修改,建议使用一级子域名",
|
||||||
cnameDomainPattern: "域名不能使用星号",
|
cnameDomainPattern: "域名不能使用星号",
|
||||||
cnameProviderSubdomain: "托管子域名",
|
cnameProviderSubdomain: "托管子域名",
|
||||||
|
|||||||
@@ -40,4 +40,8 @@ export default {
|
|||||||
pluginManagement: "插件管理",
|
pluginManagement: "插件管理",
|
||||||
pluginBetaWarning: "自定义插件处于BETA测试版,后续可能会有破坏性变更",
|
pluginBetaWarning: "自定义插件处于BETA测试版,后续可能会有破坏性变更",
|
||||||
pleaseSelectRecord: "请先勾选记录",
|
pleaseSelectRecord: "请先勾选记录",
|
||||||
|
clearRuntimeDeps: "清理第三方依赖缓存",
|
||||||
|
clearRuntimeDepsTooltip: "清除后需重启 certd 容器,否则已缓存模块不会重新读取",
|
||||||
|
clearRuntimeDepsConfirm: "确定要清理第三方依赖缓存吗?清理后请重启 certd 容器以确保重新加载依赖。",
|
||||||
|
clearRuntimeDepsSuccess: "第三方依赖缓存清理成功,请重启 certd 容器",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -108,6 +108,9 @@ export default {
|
|||||||
httpsProxyPlaceholder: "http://192.168.1.2:18010/",
|
httpsProxyPlaceholder: "http://192.168.1.2:18010/",
|
||||||
saveThenTestTitle: "保存后,再点击测试",
|
saveThenTestTitle: "保存后,再点击测试",
|
||||||
httpsProxyHelper: "一般这两个代理填一样的,保存后再测试",
|
httpsProxyHelper: "一般这两个代理填一样的,保存后再测试",
|
||||||
|
noProxy: "代理排除",
|
||||||
|
noProxyPlaceholder: "localhost,127.0.0.1,.example.com,192.168.*",
|
||||||
|
noProxyHelper: "配置NO_PROXY,多个地址可用英文逗号、空格或换行分隔,命中的请求将不走代理\n例如:localhost,127.0.0.1,.example.com,192.168.*",
|
||||||
dualStackNetwork: "双栈网络",
|
dualStackNetwork: "双栈网络",
|
||||||
ipv4Priority: "IPV4优先",
|
ipv4Priority: "IPV4优先",
|
||||||
ipv6Priority: "IPV6优先",
|
ipv6Priority: "IPV6优先",
|
||||||
|
|||||||
@@ -107,6 +107,9 @@ function install(app: App, options: any = {}) {
|
|||||||
scroll: {
|
scroll: {
|
||||||
x: 960,
|
x: 960,
|
||||||
},
|
},
|
||||||
|
rowSelection: {
|
||||||
|
fixed: "left",
|
||||||
|
},
|
||||||
size: "small",
|
size: "small",
|
||||||
pagination: false,
|
pagination: false,
|
||||||
onResizeColumn: (w: number, col: any) => {
|
onResizeColumn: (w: number, col: any) => {
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export const certdResources = [
|
|||||||
isMenu: true,
|
isMenu: true,
|
||||||
icon: "ion:duplicate-outline",
|
icon: "ion:duplicate-outline",
|
||||||
auth: true,
|
auth: true,
|
||||||
|
keepAlive: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -282,6 +283,7 @@ export const certdResources = [
|
|||||||
meta: {
|
meta: {
|
||||||
icon: "ion:barcode-outline",
|
icon: "ion:barcode-outline",
|
||||||
auth: true,
|
auth: true,
|
||||||
|
keepAlive: true,
|
||||||
isMenu: true,
|
isMenu: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -350,6 +352,7 @@ export const certdResources = [
|
|||||||
},
|
},
|
||||||
icon: "ion:gift-outline",
|
icon: "ion:gift-outline",
|
||||||
auth: true,
|
auth: true,
|
||||||
|
keepAlive: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -389,7 +392,7 @@ export const certdResources = [
|
|||||||
meta: {
|
meta: {
|
||||||
show: () => {
|
show: () => {
|
||||||
const settingStore = useSettingStore();
|
const settingStore = useSettingStore();
|
||||||
return settingStore.isComm;
|
return settingStore.isInviteCommissionEnabled;
|
||||||
},
|
},
|
||||||
icon: "ion:gift-outline",
|
icon: "ion:gift-outline",
|
||||||
auth: true,
|
auth: true,
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ export const sysResources = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "certd.sysResources.headerMenus",
|
title: "certd.sysResources.headerMenus",
|
||||||
name: "HeaderMenus",
|
name: "SettingsHeaderMenus",
|
||||||
path: "/sys/settings/header-menus",
|
path: "/sys/settings/header-menus",
|
||||||
component: "/sys/settings/header-menus/index.vue",
|
component: "/sys/settings/header-menus/index.vue",
|
||||||
meta: {
|
meta: {
|
||||||
@@ -128,7 +128,7 @@ export const sysResources = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "certd.sysResources.sysAccess",
|
title: "certd.sysResources.sysAccess",
|
||||||
name: "SysAccess",
|
name: "SysAccessManager",
|
||||||
path: "/sys/access",
|
path: "/sys/access",
|
||||||
component: "/sys/access/index.vue",
|
component: "/sys/access/index.vue",
|
||||||
meta: {
|
meta: {
|
||||||
@@ -311,7 +311,7 @@ export const sysResources = [
|
|||||||
},
|
},
|
||||||
icon: "ion:bag-check",
|
icon: "ion:bag-check",
|
||||||
permission: "sys:settings:edit",
|
permission: "sys:settings:edit",
|
||||||
keepAlive: true,
|
keepAlive: false,
|
||||||
auth: true,
|
auth: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ export type InviteSetting = {
|
|||||||
export type SysPrivateSetting = {
|
export type SysPrivateSetting = {
|
||||||
httpProxy?: string;
|
httpProxy?: string;
|
||||||
httpsProxy?: string;
|
httpsProxy?: string;
|
||||||
|
noProxy?: string;
|
||||||
commonHeaders?: string;
|
commonHeaders?: string;
|
||||||
reverseProxies?: any;
|
reverseProxies?: any;
|
||||||
dnsResultOrder?: string;
|
dnsResultOrder?: string;
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { resetAllStores, useAccessStore } from "/@/vben/stores";
|
|||||||
|
|
||||||
import { useUserStore as vbenUserStore } from "/@/vben/stores/modules/user";
|
import { useUserStore as vbenUserStore } from "/@/vben/stores/modules/user";
|
||||||
import { request } from "/@/api/service";
|
import { request } from "/@/api/service";
|
||||||
|
import { inviteUtils } from "/@/utils/util.invite";
|
||||||
|
|
||||||
interface UserState {
|
interface UserState {
|
||||||
userInfo: Nullable<UserInfoRes>;
|
userInfo: Nullable<UserInfoRes>;
|
||||||
@@ -66,6 +67,9 @@ export const useUserStore = defineStore({
|
|||||||
},
|
},
|
||||||
async register(user: RegisterReq) {
|
async register(user: RegisterReq) {
|
||||||
await UserApi.register(user);
|
await UserApi.register(user);
|
||||||
|
if (user.inviteCode) {
|
||||||
|
inviteUtils.clear();
|
||||||
|
}
|
||||||
notification.success({
|
notification.success({
|
||||||
message: "注册成功,请登录",
|
message: "注册成功,请登录",
|
||||||
});
|
});
|
||||||
@@ -85,6 +89,9 @@ export const useUserStore = defineStore({
|
|||||||
let loginRes: any = null;
|
let loginRes: any = null;
|
||||||
if (loginType === "sms") {
|
if (loginType === "sms") {
|
||||||
loginRes = await UserApi.loginBySms(params as SmsLoginReq);
|
loginRes = await UserApi.loginBySms(params as SmsLoginReq);
|
||||||
|
if ((params as SmsLoginReq).inviteCode) {
|
||||||
|
inviteUtils.clear();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
loginRes = await UserApi.login(params as LoginReq);
|
loginRes = await UserApi.login(params as LoginReq);
|
||||||
}
|
}
|
||||||
@@ -136,12 +143,12 @@ export const useUserStore = defineStore({
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("注销登录请求失败:", e);
|
console.error("注销登录请求失败:", e);
|
||||||
}
|
}
|
||||||
|
// 第三方登录注销
|
||||||
|
this.oauthLogout();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.resetState();
|
this.resetState();
|
||||||
resetAllStores();
|
resetAllStores();
|
||||||
// 第三方登录注销
|
|
||||||
await this.oauthLogout();
|
|
||||||
goLogin && router.push("/login");
|
goLogin && router.push("/login");
|
||||||
mitter.emit("app.logout");
|
mitter.emit("app.logout");
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8,4 +8,45 @@
|
|||||||
.vben-normal-menu__item.is-active {
|
.vben-normal-menu__item.is-active {
|
||||||
background-color: #3b3b3b !important;
|
background-color: #3b3b3b !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cd-table {
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
border-bottom: 1px solid #303030;
|
||||||
|
border-left: 1px solid #303030;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: #1f1f1f;
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
border-top: 1px solid #303030;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-right: 1px solid #303030;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
&:last-child {
|
||||||
|
border-right: 1px solid #303030;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td.position-sticky-right {
|
||||||
|
background-color: #141414;
|
||||||
|
}
|
||||||
|
|
||||||
|
.position-sticky-right::before {
|
||||||
|
background: #303030;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.hover-color:hover td {
|
||||||
|
background: #262626;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-active {
|
||||||
|
background: #1f3a23;
|
||||||
|
color: #81c784;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { onActivated, onMounted } from "vue";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可靠的页面刷新钩子:
|
||||||
|
* - 如果组件实际被 KeepAlive 缓存命中,由 onActivated 触发 init;
|
||||||
|
* - 如果没有被缓存,由 onMounted 兜底触发,避免不刷新也不触发 onActivated。
|
||||||
|
*/
|
||||||
|
export function useMounted(init: () => void | Promise<void>) {
|
||||||
|
let activated = false;
|
||||||
|
|
||||||
|
onActivated(() => {
|
||||||
|
activated = true;
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 让 onActivated 有机会先执行;组件未被 KeepAlive 缓存时 onActivated 不会触发,由这里兜底。
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!activated) {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -41,6 +41,10 @@ export const inviteUtils = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
localStorage.removeItem(INVITE_STORAGE_KEY);
|
||||||
|
},
|
||||||
|
|
||||||
captureFromLocation() {
|
captureFromLocation() {
|
||||||
const hashQuery = window.location.hash?.split("?")[1] || "";
|
const hashQuery = window.location.hash?.split("?")[1] || "";
|
||||||
const search = window.location.search?.replace(/^\?/, "") || "";
|
const search = window.location.search?.replace(/^\?/, "") || "";
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, onActivated, onMounted } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
import { useMounted } from "/@/use/use-mounted";
|
||||||
import { useFs } from "@fast-crud/fast-crud";
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
import createCrudOptions from "./crud";
|
import createCrudOptions from "./crud";
|
||||||
import { createAccessApi } from "/@/views/certd/access/api";
|
import { createAccessApi } from "/@/views/certd/access/api";
|
||||||
@@ -23,14 +24,7 @@ export default defineComponent({
|
|||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const api = createAccessApi("user");
|
const api = createAccessApi("user");
|
||||||
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api, permission: { isProjectPermission: true } } });
|
const { crudBinding, crudRef, crudExpose } = useFs({ createCrudOptions, context: { api, permission: { isProjectPermission: true } } });
|
||||||
|
useMounted(() => crudExpose.doRefresh());
|
||||||
// 页面打开后获取列表数据
|
|
||||||
onMounted(() => {
|
|
||||||
crudExpose.doRefresh();
|
|
||||||
});
|
|
||||||
onActivated(() => {
|
|
||||||
crudExpose.doRefresh();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
crudBinding,
|
crudBinding,
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, onActivated, onMounted } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
import { useMounted } from "/@/use/use-mounted";
|
||||||
import { useFs } from "@fast-crud/fast-crud";
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
import createCrudOptions from "./crud";
|
import createCrudOptions from "./crud";
|
||||||
import { createAddonApi } from "./api";
|
import { createAddonApi } from "./api";
|
||||||
@@ -26,14 +27,7 @@ export default defineComponent({
|
|||||||
createCrudOptions,
|
createCrudOptions,
|
||||||
context: { api, permission: { isProjectPermission: true } },
|
context: { api, permission: { isProjectPermission: true } },
|
||||||
});
|
});
|
||||||
|
useMounted(() => crudExpose.doRefresh());
|
||||||
// 页面打开后获取列表数据
|
|
||||||
onMounted(() => {
|
|
||||||
crudExpose.doRefresh();
|
|
||||||
});
|
|
||||||
onActivated(() => {
|
|
||||||
crudExpose.doRefresh();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
crudBinding,
|
crudBinding,
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, onActivated, onMounted } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
import { useMounted } from "/@/use/use-mounted";
|
||||||
import { useFs } from "@fast-crud/fast-crud";
|
import { useFs } from "@fast-crud/fast-crud";
|
||||||
import createCrudOptions from "./crud";
|
import createCrudOptions from "./crud";
|
||||||
|
|
||||||
@@ -24,14 +25,7 @@ export default defineComponent({
|
|||||||
permission: { isProjectPermission: true },
|
permission: { isProjectPermission: true },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
useMounted(() => crudExpose.doRefresh());
|
||||||
// 页面打开后获取列表数据
|
|
||||||
onMounted(() => {
|
|
||||||
crudExpose.doRefresh();
|
|
||||||
});
|
|
||||||
onActivated(() => {
|
|
||||||
crudExpose.doRefresh();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
crudBinding,
|
crudBinding,
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ export default function ({ crudExpose }: CreateCrudOptionsProps): CreateCrudOpti
|
|||||||
},
|
},
|
||||||
rowHandle: {
|
rowHandle: {
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
width: 120,
|
width: 200,
|
||||||
buttons: {
|
buttons: {
|
||||||
edit: {
|
edit: {
|
||||||
click: ({ row }) => openForm(row),
|
click: ({ row }) => openForm(row),
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user