From 42fcb91f2eba58f688f694a6463bcc8849422b04 Mon Sep 17 00:00:00 2001 From: xiaojunnuo Date: Sat, 20 Jun 2026 00:35:13 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=AE=8C=E5=96=84=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E4=BE=9D=E8=B5=96=E5=8A=A8=E6=80=81=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .codex/agent-rules/backend.md | 36 --- .codex/agent-rules/coding-style.md | 26 -- .codex/agent-rules/frontend.md | 31 --- .codex/agent-rules/plugins.md | 42 --- .codex/agent-rules/testing.md | 26 -- .codex/repo-map.md | 106 -------- AGENTS.md | 249 ++++++++++++++---- packages/core/acme-client/package.json | 3 +- packages/core/basic/package.json | 4 +- packages/core/pipeline/package.json | 4 +- packages/core/pipeline/src/access/api.ts | 11 +- .../core/pipeline/src/notification/api.ts | 11 +- packages/core/pipeline/src/plugin/api.ts | 11 +- packages/core/pipeline/src/service/emit.ts | 2 +- packages/core/pipeline/src/service/index.ts | 1 + packages/core/pipeline/src/service/runtime.ts | 27 ++ packages/libs/lib-huawei/package.json | 4 +- packages/libs/lib-iframe/package.json | 4 +- packages/libs/lib-jdcloud/package.json | 4 +- packages/libs/lib-k8s/package.json | 4 +- packages/libs/lib-server/.eslintrc.json | 21 +- packages/libs/lib-server/package.json | 6 +- .../src/user/access/service/access-getter.ts | 7 +- .../libs/lib-server/src/user/addon/api/api.ts | 14 +- packages/libs/midway-flyway-js/.eslintrc.json | 21 +- packages/libs/midway-flyway-js/package.json | 4 +- packages/plugins/plugin-cert/package.json | 4 +- packages/plugins/plugin-lib/package.json | 4 +- .../plugin-lib/src/cert/dns-provider/base.ts | 11 +- .../locales/langs/en-US/certd/sys-plugin.ts | 3 + .../locales/langs/zh-CN/certd/sys-plugin.ts | 3 + .../certd-client/src/views/sys/plugin/api.ts | 7 + .../src/views/sys/plugin/crud.tsx | 19 +- packages/ui/certd-server/package.json | 38 +-- packages/ui/certd-server/src/configuration.ts | 1 - .../sys/settings/sys-settings-controller.ts | 9 + .../src/modules/basic/sms/tencent-sms.ts | 4 +- .../modules/plugin/service/plugin-service.ts | 2 +- .../runtime-deps/npm-registry-resolver.ts | 4 +- .../runtime-deps/runtime-deps-service.test.ts | 38 ++- .../runtime-deps/runtime-deps-service.ts | 77 ++++-- .../modules/sys/nettest/nettest-service.ts | 5 +- .../dns-provider/aliyun-dns-provider.ts | 2 +- .../plugin/deploy-to-ack/index.ts | 2 +- .../plugin/deploy-to-alb/index.ts | 2 +- .../plugin/deploy-to-apig/index.ts | 2 +- .../plugin/deploy-to-apigateway/index.ts | 4 +- .../plugin/deploy-to-cdn/index.ts | 2 +- .../plugin/deploy-to-dcdn/index.ts | 2 +- .../plugin/deploy-to-fc/index.ts | 10 +- .../plugin/deploy-to-nlb/index.ts | 2 +- .../plugin/deploy-to-oss/index.ts | 2 +- .../plugin/deploy-to-slb/index.ts | 2 +- .../deploy-to-waf/deploy-to-waf-cloud.ts | 2 +- .../deploy-to-waf/deploy-to-waf-cname.ts | 2 +- .../plugin-aws-cn/libs/aws-iam-client.ts | 2 +- .../plugins/plugin-deploy-to-cloudfront.ts | 4 +- .../src/plugins/plugin-aws/libs/aws-client.ts | 16 +- .../plugins/plugin-deploy-to-cloudfront.ts | 4 +- .../src/plugins/plugin-azure/access.ts | 4 +- .../src/plugins/plugin-cmcc/cmcc-client.ts | 2 +- .../plugins/plugin-dynadot/dns-provider.ts | 12 +- .../plugin-lib/aliyun/access/alioss-access.ts | 2 +- .../plugin-lib/aliyun/access/aliyun-access.ts | 2 +- .../plugin-lib/aliyun/lib/aliyun-client-v2.ts | 8 +- .../plugin-lib/aliyun/lib/base-client.ts | 10 +- .../plugin-lib/aliyun/lib/oss-client.ts | 2 +- .../plugin-lib/aliyun/lib/ssl-client.ts | 2 +- .../src/plugins/plugin-lib/oss/impls/s3.ts | 10 +- .../plugin-lib/tencent/lib/cos-client.ts | 7 +- 70 files changed, 528 insertions(+), 503 deletions(-) delete mode 100644 .codex/agent-rules/backend.md delete mode 100644 .codex/agent-rules/coding-style.md delete mode 100644 .codex/agent-rules/frontend.md delete mode 100644 .codex/agent-rules/plugins.md delete mode 100644 .codex/agent-rules/testing.md delete mode 100644 .codex/repo-map.md create mode 100644 packages/core/pipeline/src/service/runtime.ts diff --git a/.codex/agent-rules/backend.md b/.codex/agent-rules/backend.md deleted file mode 100644 index 91d85f288..000000000 --- a/.codex/agent-rules/backend.md +++ /dev/null @@ -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`。 diff --git a/.codex/agent-rules/coding-style.md b/.codex/agent-rules/coding-style.md deleted file mode 100644 index adae4165c..000000000 --- a/.codex/agent-rules/coding-style.md +++ /dev/null @@ -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 方法或已有公共工具;抽象要服务于减少真实重复和降低修改风险,不要为了形式上的“复用”制造过度设计。 - -## 单一职责 - -遵守单一职责原则:一个方法只负责一个清晰的业务步骤或技术步骤。流程编排方法可以串联多个步骤,但具体的校验、计算、持久化、状态变更、展示数据组装应尽量拆到命名明确的小方法中;不要让一个方法同时承担查询、校验、计算、写库、格式化返回等过多职责。 diff --git a/.codex/agent-rules/frontend.md b/.codex/agent-rules/frontend.md deleted file mode 100644 index 601c31179..000000000 --- a/.codex/agent-rules/frontend.md +++ /dev/null @@ -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 ` -- ESLint:`packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix ` - -不要为了格式化无关文件而扩大 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` 里手写输入框。 diff --git a/.codex/agent-rules/plugins.md b/.codex/agent-rules/plugins.md deleted file mode 100644 index 8c47ea8ea..000000000 --- a/.codex/agent-rules/plugins.md +++ /dev/null @@ -1,42 +0,0 @@ -# 流水线与插件规则 - -项目最关键的架构概念是证书流水线。核心导出、关键抽象、插件目录和共享 helper 位置见 `.codex/repo-map.md`。 - -插件是核心能力,不是边缘功能。新增服务商、DNS 验证、证书部署、通知方式等能力,通常应该放在插件包里,或放在 `packages/ui/certd-server/src/plugins//` 下。 - -## 改动归属 - -修改证书申请、验证、部署或通知行为时,先判断改动属于哪里: - -- 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`,再进入具体实现。若用户在插件开发中指出更好的做法,应总结并更新对应技能。 diff --git a/.codex/agent-rules/testing.md b/.codex/agent-rules/testing.md deleted file mode 100644 index 89db85ff2..000000000 --- a/.codex/agent-rules/testing.md +++ /dev/null @@ -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 构建。 diff --git a/.codex/repo-map.md b/.codex/repo-map.md deleted file mode 100644 index a41064935..000000000 --- a/.codex/repo-map.md +++ /dev/null @@ -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 ` -- 前端改动文件 ESLint 修复:`packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix ` - -不要主动运行 `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..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 仓库前应先检查该锁文件或占用状态。 diff --git a/AGENTS.md b/AGENTS.md index c8e369ae2..443a9d69f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,73 +1,210 @@ # Certd 开发 Agent 上下文 -这个文件是给在本仓库工作的开发 agent 看的常驻项目说明。进入仓库后先读本文,再按任务读取对应导航或规则文件,避免每次重新全量扫描项目。 - -仓库代码导航、目录地图、常用入口和参考文件见 `.codex/repo-map.md`。更细的开发规则拆在 `.codex/agent-rules/` 下;本文只保留最高优先级的规则、架构边界和工作方式。 +进入仓库后先读本文。本文同时包含常驻规则、仓库地图、常用入口和验证命令;不要依赖分散规则文件。 ## 项目定位 -Certd 是支持私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web 管理台和后端服务,用于证书申请、续期、部署、监控、通知和开放 API 集成。 +Certd 是可私有化部署的 SSL/TLS 证书自动化管理平台,提供 Web 管理台和后端服务,用于证书申请、续期、部署、监控、通知和开放 API 集成。 -核心产品模型是“证书流水线”: +核心模型是“证书流水线”: -- 通过 ACME 申请证书 -- 使用 DNS-01、HTTP-01、CNAME 代理或服务商集成完成域名验证 -- 将证书转换或导出为 pem、pfx、der、jks、p7b 等格式 -- 部署证书到主机、Nginx、Kubernetes、CDN、云厂商、面板等目标 -- 通知用户,并监控站点证书过期时间 +- 通过 ACME 申请证书。 +- 完成 DNS-01、HTTP-01、CNAME 代理或服务商验证。 +- 导出 pem、pfx、der、jks、p7b 等证书格式。 +- 部署到主机、Nginx、Kubernetes、CDN、云厂商、面板等目标。 +- 通知用户,并监控站点证书过期时间。 -系统会保存证书、云厂商凭据、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、单一职责等通用代码风格 +系统会保存证书、云厂商凭据、SSH 信息、API Key 等敏感数据。始终按私有化/本地部署产品处理,避免泄露本地数据和配置。 ## 仓库边界 -这是一个 pnpm + lerna 的 monorepo。核心定位: - -- `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` 只是占位脚本。 +- 根仓库是 pnpm + lerna monorepo;不要引入 npm/yarn lockfile。 +- `packages/pro/` 是独立 Git 工作区;修改商业版代码后,必须在 `packages/pro` 内单独执行 `git status` / `git diff`。 - 不要把 `packages/ui/certd-server/data/`、`logs/`、生成的 metadata/dist 等运行时或构建产物纳入改动,除非任务明确要求。 -- 做数据库结构变更时,添加或更新迁移脚本,不要依赖 TypeORM 自动同步。 -- 做插件相关任务时,先读取对应 `.trae/skills//SKILL.md`,再进入具体实现。 -- 后端 service 拼接可选 `projectId` 查询条件时,不要直接写 `{ userId, projectId }`;应使用 `BaseService.buildUserProjectQuery(userId, projectId)`,只有 `projectId != null` 时才加入查询条件。 +- 例外:分析插件动态依赖时,可以只读查看后端数据目录下的 `./data/.runtime-deps`;该目录用于 runtime-deps 动态安装第三方 SDK,不应纳入提交。 -## 工作方式 +核心包: -- 先读本文;需要代码导航、目录入口、参考文件或验证命令时读 `.codex/repo-map.md`。 -- 任务涉及后端、前端、插件、测试或代码风格时,先读取 `.codex/agent-rules/` 下对应规则文件,再查看具体代码。 -- 在 PowerShell 中读取中文、Markdown、locale、文档类文件时,显式使用 `Get-Content -Encoding utf8`;如果仍乱码,再执行 `[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()` 后重试。 -- 在 PowerShell 中使用 `rg` 搜索包含引号、括号、反斜杠等特殊字符的模式时,优先用单引号包裹整个 pattern,例如 `rg 'await import\("tencentcloud-sdk-nodejs' packages/ui/certd-server/src -g '*.ts'`;不要在双引号字符串里再直接写未转义的 `"`,否则 PowerShell 会截断参数并把后半段当成文件路径,出现 `The string is missing the terminator` 或 `rg: xxx: 系统找不到指定的文件`。 +- `packages/ui/certd-server`:后端服务。 +- `packages/ui/certd-client`:前端 Web 管理台。 +- `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 `。 +- 前端改动文件 ESLint 修复:`packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix `。 +- 后端改动文件 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/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..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`。 -- 做服务商、DNS、部署、通知相关任务时,先看 `packages/ui/certd-server/src/plugins`,再看 `packages/plugins/plugin-lib` 里的共享辅助能力。 -- 优先沿用现有模块、插件、服务模式,再考虑新增抽象;避免为了形式上的“复用”制造过度设计。 -- 为了提升可读性,不要把一个方法调用链直接塞进另一个方法的参数里;应先用有意义的局部变量承载返回值,再把变量传入下一步调用。 -- 实现新功能或修复行为缺陷前,优先补对应单元测试并确认红灯,再实现代码并跑聚焦验证。确实不适合先写测试时,在回复中说明原因和替代验证方式。 -- 后补单元测试时,先按正确行为写预期;如果红灯需要修改既有实现,先向用户确认这是 bug 还是既有需求,避免未经确认改变行为。 +- 不要运行前端 `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 ` 和 `packages\ui\certd-client\node_modules\.bin\eslint.cmd --fix `。 +- 列表管理、后台管理、记录查询、CRUD 表格页面优先使用 Fast Crud;开发或重构前读 `.trae/skills/fast-crud-page-dev/SKILL.md`。 +- 只有轻量只读展示、强交互自定义界面或既有页面模式明显不适合 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//`。 +- 做服务商、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;只有跨包影响明显时再考虑更大范围构建。 - -## 架构边界 - -插件是核心能力,不是边缘功能。新增服务商、DNS 验证、证书部署、通知方式等能力,通常应该放在插件包里,或放在 `packages/ui/certd-server/src/plugins//` 下。 - -修改证书申请、验证、部署或通知行为时,先判断改动属于 ACME client、pipeline 核心抽象、后端 module/service/entity/controller、具体插件实现,还是前端 view/form/schema。 - -如果只是某个服务商或部署目标的问题,不要轻易修改共享 pipeline/core 行为,除非确实是可复用的公共能力。 diff --git a/packages/core/acme-client/package.json b/packages/core/acme-client/package.json index 25a3ab6c6..4b0fb6231 100644 --- a/packages/core/acme-client/package.json +++ b/packages/core/acme-client/package.json @@ -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});\"", "test:unit": "cross-env NODE_ENV=unittest npm run before-test:unit && cross-env NODE_ENV=unittest tsc -p tsconfig.test.json --skipLibCheck && cross-env NODE_ENV=unittest mocha -t 60000 \"dist-test/**/*.test.js\"", "pub": "npm publish", - "compile": "tsc --skipLibCheck --watch" + "compile": "tsc --skipLibCheck --watch", + "format": "prettier --write src" }, "repository": { "type": "git", diff --git a/packages/core/basic/package.json b/packages/core/basic/package.json index 8c5bd1d53..6c8acf2ba 100644 --- a/packages/core/basic/package.json +++ b/packages/core/basic/package.json @@ -15,7 +15,9 @@ "test": "mocha --loader=ts-node/esm", "test:unit": "cross-env NODE_ENV=unittest mocha --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"", "pub": "npm publish", - "compile": "tsc --skipLibCheck --watch" + "compile": "tsc --skipLibCheck --watch", + "format": "prettier --write src", + "lint": "eslint --fix" }, "dependencies": { "async-lock": "^1.4.1", diff --git a/packages/core/pipeline/package.json b/packages/core/pipeline/package.json index 27262c389..463bdf3e2 100644 --- a/packages/core/pipeline/package.json +++ b/packages/core/pipeline/package.json @@ -16,7 +16,9 @@ "test": "mocha --loader=ts-node/esm", "test:unit": "cross-env NODE_ENV=unittest mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"", "pub": "npm publish", - "compile": "tsc --skipLibCheck --watch" + "compile": "tsc --skipLibCheck --watch", + "format": "prettier --write src", + "lint": "eslint --fix" }, "dependencies": { "@certd/basic": "^1.41.4", diff --git a/packages/core/pipeline/src/access/api.ts b/packages/core/pipeline/src/access/api.ts index 218637167..36b017fe8 100644 --- a/packages/core/pipeline/src/access/api.ts +++ b/packages/core/pipeline/src/access/api.ts @@ -3,7 +3,7 @@ import { FormItemProps } from "../dt/index.js"; import { HttpClient, ILogger, utils } from "@certd/basic"; import * as _ from "lodash-es"; import { PluginRequestHandleReq } from "../plugin/index.js"; -import { IServiceGetter } from "../service/index.js"; +import { IRuntimeDepsService, IServiceGetter } from "../service/index.js"; // export type AccessRequestHandleReqInput = { // id?: number; @@ -48,16 +48,13 @@ export type AccessContext = { export abstract class BaseAccess implements IAccess { ctx!: AccessContext; - runtimeDepsService?: { - ensureRuntimeDependencies(pluginKeys: string | string[]): Promise; - importRuntime(specifier: string): Promise; - }; + runtimeDepsService?: IRuntimeDepsService; async importRuntime(specifier: string) { if (!this.runtimeDepsService) { return await import(specifier); } - return await this.runtimeDepsService.importRuntime(specifier); + return await this.runtimeDepsService.importRuntime(specifier, this.ctx.logger); } async setCtx(ctx: AccessContext) { @@ -66,7 +63,7 @@ export abstract class BaseAccess implements IAccess { this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService"); } if (this.runtimeDepsService && this.ctx.define?.name) { - await this.runtimeDepsService.ensureRuntimeDependencies(`access:${this.ctx.define.name}`); + await this.runtimeDepsService.ensureRuntimeDependencies({ pluginKeys: `access:${this.ctx.define.name}`, logger: this.ctx.logger }); } } diff --git a/packages/core/pipeline/src/notification/api.ts b/packages/core/pipeline/src/notification/api.ts index e19b3f127..130d12833 100644 --- a/packages/core/pipeline/src/notification/api.ts +++ b/packages/core/pipeline/src/notification/api.ts @@ -3,7 +3,7 @@ import { Registrable } from "../registry/index.js"; import { FormItemProps, HistoryResult, Pipeline } from "../dt/index.js"; import { HttpClient, ILogger, utils } from "@certd/basic"; import * as _ from "lodash-es"; -import { IEmailService, IServiceGetter } from "../service/index.js"; +import { IEmailService, IRuntimeDepsService, IServiceGetter } from "../service/index.js"; export type NotificationBody = { userId?: number; @@ -89,16 +89,13 @@ export abstract class BaseNotification implements INotification { ctx!: NotificationContext; http!: HttpClient; logger!: ILogger; - runtimeDepsService?: { - ensureRuntimeDependencies(pluginKeys: string | string[]): Promise; - importRuntime(specifier: string): Promise; - }; + runtimeDepsService?: IRuntimeDepsService; async importRuntime(specifier: string) { if (!this.runtimeDepsService) { return await import(specifier); } - return await this.runtimeDepsService.importRuntime(specifier); + return await this.runtimeDepsService.importRuntime(specifier, this.logger); } async doSend(body: NotificationBody) { @@ -116,7 +113,7 @@ export abstract class BaseNotification implements INotification { this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService"); } if (this.runtimeDepsService && this.ctx.define?.name) { - await this.runtimeDepsService.ensureRuntimeDependencies(`notification:${this.ctx.define.name}`); + await this.runtimeDepsService.ensureRuntimeDependencies({ pluginKeys: `notification:${this.ctx.define.name}`, logger: this.logger }); } } setDefine = (define: NotificationDefine) => { diff --git a/packages/core/pipeline/src/plugin/api.ts b/packages/core/pipeline/src/plugin/api.ts index ddeefcc5e..ed2241d16 100644 --- a/packages/core/pipeline/src/plugin/api.ts +++ b/packages/core/pipeline/src/plugin/api.ts @@ -10,7 +10,7 @@ import { INotificationService } from "../notification/index.js"; import { Registrable } from "../registry/index.js"; import { IPluginConfigService } from "../service/config.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 = { typeName: string; @@ -150,16 +150,13 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin { logger!: ILogger; http!: HttpClient; accessService!: IAccessService; - runtimeDepsService?: { - ensureRuntimeDependencies(pluginKeys: string | string[]): Promise; - importRuntime(specifier: string): Promise; - }; + runtimeDepsService?: IRuntimeDepsService; async importRuntime(specifier: string) { if (!this.runtimeDepsService) { return await import(specifier); } - return await this.runtimeDepsService.importRuntime(specifier); + return await this.runtimeDepsService.importRuntime(specifier, this.logger); } clearLastStatus() { @@ -185,7 +182,7 @@ export abstract class AbstractTaskPlugin implements ITaskPlugin { this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService"); } if (this.runtimeDepsService && this.ctx.define?.name) { - await this.runtimeDepsService.ensureRuntimeDependencies(`plugin:${this.ctx.define.name}`); + await this.runtimeDepsService.ensureRuntimeDependencies({ pluginKeys: `plugin:${this.ctx.define.name}`, logger: this.logger }); } // 将证书加入secret // @ts-ignore diff --git a/packages/core/pipeline/src/service/emit.ts b/packages/core/pipeline/src/service/emit.ts index 70c027fb3..81dd906a7 100644 --- a/packages/core/pipeline/src/service/emit.ts +++ b/packages/core/pipeline/src/service/emit.ts @@ -32,7 +32,7 @@ export class PipelineEmitter { } off(event: string, listener: PipelineEventListener) { 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) { diff --git a/packages/core/pipeline/src/service/index.ts b/packages/core/pipeline/src/service/index.ts index 6e992c8ba..b0f88f918 100644 --- a/packages/core/pipeline/src/service/index.ts +++ b/packages/core/pipeline/src/service/index.ts @@ -3,6 +3,7 @@ export * from "./cname.js"; export * from "./config.js"; export * from "./url.js"; export * from "./emit.js"; +export * from "./runtime.js"; export type IServiceGetter = { get: (name: string) => Promise; }; diff --git a/packages/core/pipeline/src/service/runtime.ts b/packages/core/pipeline/src/service/runtime.ts new file mode 100644 index 000000000..38207bac8 --- /dev/null +++ b/packages/core/pipeline/src/service/runtime.ts @@ -0,0 +1,27 @@ +/** + * 运行时动态导入函数类型 + */ +export type ImportRuntime = (specifier: string, logger?: ILogger) => Promise; + +/** + * 日志接口 + */ +export type ILogger = { + info: (message: string) => void; +}; + +/** + * 运行时依赖服务参数 + */ +export type EnsureRuntimeDepsOptions = { + pluginKeys: string | string[]; + logger?: ILogger; +}; + +/** + * 运行时依赖服务接口 + */ +export interface IRuntimeDepsService { + ensureRuntimeDependencies(options: EnsureRuntimeDepsOptions): Promise; + importRuntime: ImportRuntime; +} diff --git a/packages/libs/lib-huawei/package.json b/packages/libs/lib-huawei/package.json index d90d5a463..86b925520 100644 --- a/packages/libs/lib-huawei/package.json +++ b/packages/libs/lib-huawei/package.json @@ -13,7 +13,9 @@ "preview": "vite preview", "test:unit": "cross-env NODE_ENV=unittest echo no unit tests", "pub": "npm publish", - "compile": "npm run build" + "compile": "npm run build", + "format": "prettier --write src", + "lint": "eslint --fix" }, "dependencies": { "axios": "^1.9.0", diff --git a/packages/libs/lib-iframe/package.json b/packages/libs/lib-iframe/package.json index afdd3cd4c..b1025ac2b 100644 --- a/packages/libs/lib-iframe/package.json +++ b/packages/libs/lib-iframe/package.json @@ -16,7 +16,9 @@ "preview": "vite preview", "test:unit": "cross-env NODE_ENV=unittest echo no unit tests", "pub": "npm publish", - "compile": "npm run build" + "compile": "npm run build", + "format": "prettier --write src", + "lint": "eslint --fix" }, "dependencies": { "nanoid": "^4.0.0" diff --git a/packages/libs/lib-jdcloud/package.json b/packages/libs/lib-jdcloud/package.json index 7cdaefa64..b5dddf97b 100644 --- a/packages/libs/lib-jdcloud/package.json +++ b/packages/libs/lib-jdcloud/package.json @@ -11,7 +11,9 @@ "dev-build": "npm run build", "test:unit": "cross-env NODE_ENV=unittest echo no unit tests", "pub": "npm publish", - "compile": "npm run build" + "compile": "npm run build", + "format": "prettier --write src", + "lint": "eslint --fix" }, "author": "", "license": "Apache", diff --git a/packages/libs/lib-k8s/package.json b/packages/libs/lib-k8s/package.json index 5084e6bbe..6598e5dcf 100644 --- a/packages/libs/lib-k8s/package.json +++ b/packages/libs/lib-k8s/package.json @@ -16,7 +16,9 @@ "preview": "vite preview", "test:unit": "cross-env NODE_ENV=unittest echo no unit tests", "pub": "npm publish", - "compile": "tsc --skipLibCheck --watch" + "compile": "tsc --skipLibCheck --watch", + "format": "prettier --write src", + "lint": "eslint --fix" }, "dependencies": { "@certd/basic": "^1.41.4", diff --git a/packages/libs/lib-server/.eslintrc.json b/packages/libs/lib-server/.eslintrc.json index 8d20e22b9..eeb127a3d 100644 --- a/packages/libs/lib-server/.eslintrc.json +++ b/packages/libs/lib-server/.eslintrc.json @@ -1,7 +1,22 @@ { - "extends": "./node_modules/mwts/", - "ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"], + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + "prettier" + ], "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" } } diff --git a/packages/libs/lib-server/package.json b/packages/libs/lib-server/package.json index 3d95daf69..e8abf1904 100644 --- a/packages/libs/lib-server/package.json +++ b/packages/libs/lib-server/package.json @@ -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\"", "test1": "midway-bin test --ts -V -f test/blank.test.ts -t 'hash-check'", "cov": "midway-bin cov --ts", - "lint": "mwts check", - "lint:fix": "mwts fix", "prepublish": "npm run build", "pub": "npm publish", - "compile": "tsc --skipLibCheck --watch" + "compile": "tsc --skipLibCheck --watch", + "format": "prettier --write src", + "lint": "eslint --fix" }, "keywords": [], "author": "greper", diff --git a/packages/libs/lib-server/src/user/access/service/access-getter.ts b/packages/libs/lib-server/src/user/access/service/access-getter.ts index 7ef249a65..d84686406 100644 --- a/packages/libs/lib-server/src/user/access/service/access-getter.ts +++ b/packages/libs/lib-server/src/user/access/service/access-getter.ts @@ -1,9 +1,6 @@ -import { IAccessService } from "@certd/pipeline"; +import { IAccessService, IRuntimeDepsService } from "@certd/pipeline"; -export type AccessRuntimeDepsService = { - ensureRuntimeDependencies(pluginKeys: string | string[]): Promise; - importRuntime(specifier: string): Promise; -}; +export type AccessRuntimeDepsService = IRuntimeDepsService; export class AccessGetter implements IAccessService { userId: number; diff --git a/packages/libs/lib-server/src/user/addon/api/api.ts b/packages/libs/lib-server/src/user/addon/api/api.ts index de4a01d01..9d56e3578 100644 --- a/packages/libs/lib-server/src/user/addon/api/api.ts +++ b/packages/libs/lib-server/src/user/addon/api/api.ts @@ -4,6 +4,7 @@ import { accessRegistry, FormItemProps, IAccessService, + IRuntimeDepsService, IServiceGetter, PluginRequestHandleReq, Registrable @@ -66,26 +67,20 @@ export abstract class BaseAddon implements IAddon { ctx!: AddonContext; http!: HttpClient; logger!: ILogger; - runtimeDepsService?: { - ensureRuntimeDependencies(pluginKeys: string | string[]): Promise; - importRuntime(specifier: string): Promise; - }; + runtimeDepsService?: IRuntimeDepsService; async importRuntime(specifier: string) { if (!this.runtimeDepsService) { return await import(specifier); } - return await this.runtimeDepsService.importRuntime(specifier); + return await this.runtimeDepsService.importRuntime(specifier, this.logger); } title!: string; - - // eslint-disable-next-line @typescript-eslint/no-empty-function async onInstance() {} - async getAccess(accessId: string | number, isCommon = false) { if (accessId == null) { throw new Error("您还没有配置授权"); @@ -119,7 +114,6 @@ export abstract class BaseAddon implements IAddon { return res as T; } - async setCtx(ctx: AddonContext) { this.ctx = ctx; this.http = ctx.http; @@ -128,7 +122,7 @@ export abstract class BaseAddon implements IAddon { this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService"); } if (this.runtimeDepsService && this.define?.addonType && this.define?.name) { - await this.runtimeDepsService.ensureRuntimeDependencies(`addon:${this.define.addonType}:${this.define.name}`); + await this.runtimeDepsService.ensureRuntimeDependencies({ pluginKeys: `addon:${this.define.addonType}:${this.define.name}`, logger: this.logger }); } } setDefine = (define:AddonDefine) => { diff --git a/packages/libs/midway-flyway-js/.eslintrc.json b/packages/libs/midway-flyway-js/.eslintrc.json index 8d20e22b9..eeb127a3d 100644 --- a/packages/libs/midway-flyway-js/.eslintrc.json +++ b/packages/libs/midway-flyway-js/.eslintrc.json @@ -1,7 +1,22 @@ { - "extends": "./node_modules/mwts/", - "ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"], + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "plugin:@typescript-eslint/recommended", + "plugin:prettier/recommended", + "prettier" + ], "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" } } diff --git a/packages/libs/midway-flyway-js/package.json b/packages/libs/midway-flyway-js/package.json index 65437fadd..296289168 100644 --- a/packages/libs/midway-flyway-js/package.json +++ b/packages/libs/midway-flyway-js/package.json @@ -17,7 +17,9 @@ "cov": "midway-bin cov --ts", "prepublish": "npm run build", "pub": "npm publish", - "compile": "npm run build" + "compile": "npm run build", + "format": "prettier --write src", + "lint": "eslint --fix" }, "keywords": [], "author": "greper", diff --git a/packages/plugins/plugin-cert/package.json b/packages/plugins/plugin-cert/package.json index 3aa15d1c4..84fc4c2b3 100644 --- a/packages/plugins/plugin-cert/package.json +++ b/packages/plugins/plugin-cert/package.json @@ -15,7 +15,9 @@ "preview": "vite preview", "test:unit": "cross-env NODE_ENV=unittest echo no unit tests", "pub": "npm publish", - "compile": "tsc --skipLibCheck --watch" + "compile": "tsc --skipLibCheck --watch", + "format": "prettier --write src", + "lint": "eslint --fix" }, "dependencies": { "@certd/acme-client": "^1.41.4", diff --git a/packages/plugins/plugin-lib/package.json b/packages/plugins/plugin-lib/package.json index d9ca18fd0..05354cfab 100644 --- a/packages/plugins/plugin-lib/package.json +++ b/packages/plugins/plugin-lib/package.json @@ -15,7 +15,9 @@ "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\"", "pub": "npm publish", - "compile": "tsc --skipLibCheck --watch" + "compile": "tsc --skipLibCheck --watch", + "format": "prettier --write src", + "lint": "eslint --fix" }, "dependencies": { "@alicloud/openapi-client": "^0.4.15", diff --git a/packages/plugins/plugin-lib/src/cert/dns-provider/base.ts b/packages/plugins/plugin-lib/src/cert/dns-provider/base.ts index baaca8731..c104fd75d 100644 --- a/packages/plugins/plugin-lib/src/cert/dns-provider/base.ts +++ b/packages/plugins/plugin-lib/src/cert/dns-provider/base.ts @@ -1,5 +1,5 @@ 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 { CreateRecordOptions, DnsProviderContext, DnsProviderDefine, DnsResolveRecord, DomainRecord, IDnsProvider, RemoveRecordOptions } from "./api.js"; import { dnsProviderRegistry } from "./registry.js"; @@ -7,16 +7,13 @@ export abstract class AbstractDnsProvider implements IDnsProvider { ctx!: DnsProviderContext; http!: HttpClient; logger!: ILogger; - runtimeDepsService?: { - ensureRuntimeDependencies(pluginKeys: string | string[]): Promise; - importRuntime(specifier: string): Promise; - }; + runtimeDepsService?: IRuntimeDepsService; async importRuntime(specifier: string) { if (!this.runtimeDepsService) { return await import(specifier); } - return await this.runtimeDepsService.importRuntime(specifier); + return await this.runtimeDepsService.importRuntime(specifier, this.logger); } usePunyCode(): boolean { @@ -49,7 +46,7 @@ export abstract class AbstractDnsProvider implements IDnsProvider { this.runtimeDepsService = await this.ctx.serviceGetter.get("runtimeDepsService"); } if (this.runtimeDepsService && this.ctx.define?.name) { - await this.runtimeDepsService.ensureRuntimeDependencies(`dnsProvider:${this.ctx.define.name}`); + await this.runtimeDepsService.ensureRuntimeDependencies({ pluginKeys: `dnsProvider:${this.ctx.define.name}`, logger: this.logger }); } } diff --git a/packages/ui/certd-client/src/locales/langs/en-US/certd/sys-plugin.ts b/packages/ui/certd-client/src/locales/langs/en-US/certd/sys-plugin.ts index a04d2ce9b..ff6254014 100644 --- a/packages/ui/certd-client/src/locales/langs/en-US/certd/sys-plugin.ts +++ b/packages/ui/certd-client/src/locales/langs/en-US/certd/sys-plugin.ts @@ -40,4 +40,7 @@ export default { pluginManagement: "Plugin Management", pluginBetaWarning: "Custom plugins are in BETA and may have breaking changes in future", pleaseSelectRecord: "Please select records first", + clearRuntimeDeps: "Clear Runtime Deps Cache", + clearRuntimeDepsConfirm: "Are you sure to clear the runtime dependencies cache? Required dependencies will be reinstalled on the next pipeline execution.", + clearRuntimeDepsSuccess: "Runtime dependencies cache cleared successfully", }; diff --git a/packages/ui/certd-client/src/locales/langs/zh-CN/certd/sys-plugin.ts b/packages/ui/certd-client/src/locales/langs/zh-CN/certd/sys-plugin.ts index d201bd5e9..38d598bd1 100644 --- a/packages/ui/certd-client/src/locales/langs/zh-CN/certd/sys-plugin.ts +++ b/packages/ui/certd-client/src/locales/langs/zh-CN/certd/sys-plugin.ts @@ -40,4 +40,7 @@ export default { pluginManagement: "插件管理", pluginBetaWarning: "自定义插件处于BETA测试版,后续可能会有破坏性变更", pleaseSelectRecord: "请先勾选记录", + clearRuntimeDeps: "清理第三方依赖缓存", + clearRuntimeDepsConfirm: "确定要清理第三方依赖缓存吗?清理后下次执行流水线时将重新安装所需依赖。", + clearRuntimeDepsSuccess: "第三方依赖缓存清理成功", }; diff --git a/packages/ui/certd-client/src/views/sys/plugin/api.ts b/packages/ui/certd-client/src/views/sys/plugin/api.ts index b7504722b..f5c042732 100644 --- a/packages/ui/certd-client/src/views/sys/plugin/api.ts +++ b/packages/ui/certd-client/src/views/sys/plugin/api.ts @@ -143,3 +143,10 @@ export async function GetPluginByName(name: string): Promise { data: { name }, }); } + +export async function ClearRuntimeDeps(): Promise { + return await request({ + url: "/sys/settings/clearRuntimeDeps", + method: "post", + }); +} diff --git a/packages/ui/certd-client/src/views/sys/plugin/crud.tsx b/packages/ui/certd-client/src/views/sys/plugin/crud.tsx index 4ac327fc6..a00e8701f 100644 --- a/packages/ui/certd-client/src/views/sys/plugin/crud.tsx +++ b/packages/ui/certd-client/src/views/sys/plugin/crud.tsx @@ -3,7 +3,7 @@ import { useI18n } from "/src/locales"; import { Ref, ref, computed } from "vue"; import { useRouter } from "vue-router"; import { AddReq, compute, CreateCrudOptionsProps, CreateCrudOptionsRet, DelReq, dict, EditReq, UserPageQuery, UserPageRes } from "@fast-crud/fast-crud"; -import { Modal } from "ant-design-vue"; +import { Modal, message } from "ant-design-vue"; //@ts-ignore import yaml from "js-yaml"; import { usePluginImport } from "./use-import"; @@ -83,6 +83,23 @@ export default function ({ crudExpose, context }: CreateCrudOptionsProps): Creat await openImportDialog({ crudExpose }); }, }, + clearRuntimeDeps: { + show: true, + icon: "ion:trash-outline", + text: t("certd.clearRuntimeDeps"), + type: "primary", + danger: true, + async click() { + Modal.confirm({ + title: t("certd.confirm"), + content: t("certd.clearRuntimeDepsConfirm"), + async onOk() { + await api.ClearRuntimeDeps(); + message.success(t("certd.clearRuntimeDepsSuccess")); + }, + }); + }, + }, }, }, table: { diff --git a/packages/ui/certd-server/package.json b/packages/ui/certd-server/package.json index aa25205f4..9f030419b 100644 --- a/packages/ui/certd-server/package.json +++ b/packages/ui/certd-server/package.json @@ -21,9 +21,9 @@ "test": "cross-env NODE_ENV=unittest mocha", "test:unit": "cross-env NODE_ENV=unittest mocha --no-config --node-option no-warnings --node-option loader=ts-node/esm \"src/**/*.test.ts\"", "cov": "cross-env c8 --all --reporter=text --reporter=lcovonly pnpm run test", - "lint": "mwts check", + "lint:check": "mwts check", "format": "prettier --write src", - "lint:fix": "mwts fix", + "lint": "mwts fix", "ci": "pnpm run cov", "build-only": "cross-env NODE_ENV=production mwtsc -p tsconfig.build.json --cleanOutDir --skipLibCheck", "build": "pnpm run build-only && pnpm run export-metadata", @@ -36,22 +36,11 @@ "flame": "clinic flame -- node ./bootstrap.js", "tsc": "tsc --skipLibCheck", "slimming": "node ./slimming.js", - "pub": "echo 1" + "pub": "echo 1", + "compile": "tsc --skipLibCheck --watch", + "lint1": "eslint --fix" }, "dependencies": { - "@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", - "@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", "@azure/arm-dns": "^5.1.0", "@azure/identity": "^4.13.1", "@certd/acme-client": "^1.41.4", @@ -93,7 +82,6 @@ "@ucloud-sdks/ucloud-sdk-js": "^0.2.4", "@volcengine/openapi": "^1.28.1", "@volcengine/tos-sdk": "^2.9.1", - "ali-oss": "^6.21.0", "alipay-sdk": "^4.13.0", "axios": "^1.9.0", "basic-ftp": "^5.0.5", @@ -144,7 +132,21 @@ "whoiser": "2.0.0-beta.10", "xml2js": "^0.6.2" }, - "lazyDependencies": { + "lazyDependencies": { + "@alicloud/fc20230330": "^4.1.7", + "@alicloud/tea-typescript": "^1.8.0", + "@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-util": "^1.4.10", + "@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", + "ali-oss": "^6.21.0", "tencentcloud-sdk-nodejs": "^4.1.112", "cos-nodejs-sdk-v5": "^2.14.6" }, diff --git a/packages/ui/certd-server/src/configuration.ts b/packages/ui/certd-server/src/configuration.ts index 8eaaef70c..b46563455 100644 --- a/packages/ui/certd-server/src/configuration.ts +++ b/packages/ui/certd-server/src/configuration.ts @@ -31,7 +31,6 @@ process.on("uncaughtException", error => { logger.error("您的服务器不支持监听IPV6格式的地址(::),请配置环境变量: certd_koa_hostname=0.0.0.0"); } }); - // function startHeapLog() { // function format(bytes: any) { // return (bytes / 1024 / 1024).toFixed(2) + ' MB'; diff --git a/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts b/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts index 19092fcb4..af8e1f30d 100644 --- a/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts +++ b/packages/ui/certd-server/src/controller/sys/settings/sys-settings-controller.ts @@ -7,6 +7,7 @@ import { getEmailSettings } from "../../../modules/sys/settings/fix.js"; import { http, logger, utils } from "@certd/basic"; import { CodeService } from "../../../modules/basic/service/code-service.js"; import { SmsServiceFactory } from "../../../modules/basic/sms/factory.js"; +import { RuntimeDepsService } from "../../../modules/runtime-deps/runtime-deps-service.js"; /** */ @@ -23,6 +24,8 @@ export class SysSettingsController extends CrudController { codeService: CodeService; @Inject() addonService: AddonService; + @Inject() + runtimeDepsService: RuntimeDepsService; getService() { return this.service; @@ -216,4 +219,10 @@ export class SysSettingsController extends CrudController { const list = await addonRegistry.getDefineList("oauth"); return this.ok(list); } + + @Post("/clearRuntimeDeps", { description: "sys:settings:edit" }) + async clearRuntimeDeps() { + await this.runtimeDepsService.clearRuntimeDeps(); + return this.ok(true); + } } diff --git a/packages/ui/certd-server/src/modules/basic/sms/tencent-sms.ts b/packages/ui/certd-server/src/modules/basic/sms/tencent-sms.ts index 99cec0c69..fba25b27f 100644 --- a/packages/ui/certd-server/src/modules/basic/sms/tencent-sms.ts +++ b/packages/ui/certd-server/src/modules/basic/sms/tencent-sms.ts @@ -72,7 +72,9 @@ export class TencentSmsService implements ISmsService { this.ctx = ctx; if (this.ctx.runtimeDepsService) { await this.ctx.runtimeDepsService.ensureDependencies({ - "tencentcloud-sdk-nodejs": "^4.1.112", + dependencies: { + "tencentcloud-sdk-nodejs": "^4.1.112", + }, }); } } diff --git a/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts b/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts index 7ddd2e07c..7b578639b 100644 --- a/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts +++ b/packages/ui/certd-server/src/modules/plugin/service/plugin-service.ts @@ -332,7 +332,7 @@ export class PluginService extends BaseService { if (!isBareModuleSpecifier(modulePath)) { return await importLocalModule(modulePath); } - return await this.runtimeDepsService.importRuntime(modulePath); + return await this.runtimeDepsService.importRuntime(modulePath, logger); } private async getPluginClassFromFile(item: any) { diff --git a/packages/ui/certd-server/src/modules/runtime-deps/npm-registry-resolver.ts b/packages/ui/certd-server/src/modules/runtime-deps/npm-registry-resolver.ts index bf57d75c0..3a5f041c9 100644 --- a/packages/ui/certd-server/src/modules/runtime-deps/npm-registry-resolver.ts +++ b/packages/ui/certd-server/src/modules/runtime-deps/npm-registry-resolver.ts @@ -38,9 +38,7 @@ export class NpmRegistryResolver { const candidates = (config?.candidates || []).filter(Boolean); const probes = await Promise.allSettled(candidates.map(registryUrl => this.probe(registryUrl))); - const okList = probes - .map(item => (item.status === "fulfilled" ? item.value : null)) - .filter((item): item is RegistryProbeResult => !!item && item.ok); + const okList = probes.map(item => (item.status === "fulfilled" ? item.value : null)).filter((item): item is RegistryProbeResult => !!item && item.ok); if (okList.length > 0) { okList.sort((a, b) => a.elapsedMs - b.elapsedMs); diff --git a/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.test.ts b/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.test.ts index f6d450dd7..ad017b065 100644 --- a/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.test.ts +++ b/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.test.ts @@ -46,6 +46,7 @@ describe("RuntimeDepsService", () => { } assert.equal(args[0], "install"); assert.ok(args.includes("--ignore-workspace")); + assert.ok(args.includes("--no-frozen-lockfile")); return { stdout: "", stderr: "", code: 0 }; }, } as any; @@ -295,7 +296,7 @@ describe("RuntimeDepsService", () => { target: async () => ({} as any), }); try { - await service.ensureRuntimeDependencies("plugin:runtimeDepsKey"); + await service.ensureRuntimeDependencies({ pluginKeys: "plugin:runtimeDepsKey" }); const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8")); assert.deepEqual(manifest.dependencies, { keyed: "^1.0.0" }); @@ -332,7 +333,7 @@ describe("RuntimeDepsService", () => { target: async () => ({} as any), }); try { - await service.ensureRuntimeDependencies(["access:runtimeDepsArrayAccess", "addon:captcha:runtimeDepsArrayAddon"]); + await service.ensureRuntimeDependencies({ pluginKeys: ["access:runtimeDepsArrayAccess", "addon:captcha:runtimeDepsArrayAddon"] }); const manifest = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8")); assert.deepEqual(manifest.dependencies, { @@ -375,10 +376,7 @@ describe("RuntimeDepsService", () => { it("reports bare dependent plugin names as invalid format", () => { const service = new RuntimeDepsService(); - assert.throws( - () => service.resolvePluginDependencies({ name: "deploy", pluginType: "deploy", dependPlugins: { runtimeDepsBareName: "*" } }), - /插件依赖格式错误/ - ); + assert.throws(() => service.resolvePluginDependencies({ name: "deploy", pluginType: "deploy", dependPlugins: { runtimeDepsBareName: "*" } }), /插件依赖格式错误/); }); it("records runtime install environment state", async () => { @@ -438,10 +436,7 @@ describe("RuntimeDepsService", () => { serviceA.commandRunner = commandRunner as any; serviceB.commandRunner = commandRunner as any; - await Promise.all([ - serviceA.ensureInstalled([{ name: "a", dependPackages: { foo: "^1.0.0" } }]), - serviceB.ensureInstalled([{ name: "a", dependPackages: { foo: "^1.0.0" } }]), - ]); + await Promise.all([serviceA.ensureInstalled([{ name: "a", dependPackages: { foo: "^1.0.0" } }]), serviceB.ensureInstalled([{ name: "a", dependPackages: { foo: "^1.0.0" } }])]); assert.equal(installCount, 1); }); @@ -487,4 +482,27 @@ describe("RuntimeDepsService", () => { } } }); + + it("clears runtime dependency directory", async () => { + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-clear-")); + const runtimeRootDir = path.join(rootDir, ".runtime-deps"); + fs.mkdirSync(path.join(runtimeRootDir, "node_modules", "foo"), { recursive: true }); + fs.writeFileSync(path.join(runtimeRootDir, "package.json"), "{}", "utf8"); + const service = new RuntimeDepsService(); + service.runtimeDepsRootDir = runtimeRootDir; + service.installTimeoutMs = 1000; + + await service.clearRuntimeDeps(); + + assert.equal(fs.existsSync(runtimeRootDir), true); + assert.equal(fs.readdirSync(runtimeRootDir).length, 0); + }); + + it("rejects clearing unexpected runtime dependency path", async () => { + const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "certd-runtime-clear-invalid-")); + const service = new RuntimeDepsService(); + service.runtimeDepsRootDir = rootDir; + + await assert.rejects(() => service.clearRuntimeDeps(), /动态依赖目录配置异常/); + }); }); diff --git a/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.ts b/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.ts index 68d5ee5ae..b71a2d8fd 100644 --- a/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.ts +++ b/packages/ui/certd-server/src/modules/runtime-deps/runtime-deps-service.ts @@ -9,6 +9,7 @@ import { NpmRegistryResolver } from "./npm-registry-resolver.js"; import { Registry, accessRegistry, notificationRegistry, pluginRegistry } from "@certd/pipeline"; import { dnsProviderRegistry } from "@certd/plugin-lib"; import { addonRegistry } from "@certd/lib-server"; +import { logger, ILogger } from "@certd/basic"; export type RuntimeDependencyPluginDefine = { name: string; @@ -72,12 +73,14 @@ type CommandRunnerResult = { }; type CommandRunner = { + // @ts-ignore run(command: string, args: string[], options: { cwd: string; timeoutMs: number; env?: NodeJS.ProcessEnv }): Promise; }; const PROCESS_LOCKS = new Map>(); class DefaultCommandRunner implements CommandRunner { + // @ts-ignore async run(command: string, args: string[], options: { cwd: string; timeoutMs: number; env?: NodeJS.ProcessEnv }): Promise { return await new Promise(resolve => { let stdout = ""; @@ -87,6 +90,7 @@ class DefaultCommandRunner implements CommandRunner { cwd: options.cwd, env: options.env, windowsHide: true, + // @ts-ignore shell: process.platform === "win32", }); @@ -182,18 +186,18 @@ export class RuntimeDepsService { return { dependencies: merged, conflicts }; } - async ensureInstalled(plugins: RuntimeDependencyPluginDefine[]): Promise { + async ensureInstalled(options: { plugins: RuntimeDependencyPluginDefine[]; logger?: ILogger }): Promise { + const { plugins, logger: log } = options; const { dependencies, conflicts } = this.resolveDependenciesFromPlugins(plugins); if (conflicts.length > 0) { const conflict = conflicts[0]; - throw new Error( - `动态依赖版本冲突: ${conflict.packageName} => ${conflict.ranges.map(item => `${item.pluginName}:${item.range}`).join(", ")}` - ); + throw new Error(`动态依赖版本冲突: ${conflict.packageName} => ${conflict.ranges.map(item => `${item.pluginName}:${item.range}`).join(", ")}`); } - return await this.ensureDependencies(dependencies); + return await this.ensureDependencies({ dependencies, logger: log }); } - async ensureDependencies(dependencies: Record): Promise { + async ensureDependencies(options: { dependencies: Record; logger?: ILogger }): Promise { + const { dependencies, logger: log } = options; if (!this.enabled) { return { registryUrl: "", @@ -209,7 +213,7 @@ export class RuntimeDepsService { const dependenciesHash = this.createDependenciesHash(dependencies); let installPromise = this.installPromises.get(dependenciesHash); if (!installPromise) { - installPromise = this.doEnsureInstalled(dependencies).catch(error => { + installPromise = this.doEnsureInstalled({ dependencies, logger: log }).catch(error => { this.installPromises.delete(dependenciesHash); throw error; }); @@ -223,7 +227,8 @@ export class RuntimeDepsService { return this.collectDependencies(expandedPlugins); } - async ensureRuntimeDependencies(pluginKeys: string | string[]): Promise { + async ensureRuntimeDependencies(options: { pluginKeys: string | string[]; logger?: ILogger }): Promise { + const { pluginKeys, logger: log } = options; const keys = Array.isArray(pluginKeys) ? pluginKeys : [pluginKeys]; const pluginDefines = keys.map(pluginKey => this.getDefineByPluginKey(pluginKey)); if (pluginDefines.every(pluginDefine => !pluginDefine.dependPackages && !pluginDefine.dependPlugins)) { @@ -233,19 +238,23 @@ export class RuntimeDepsService { }; } const expandedPluginDefines = pluginDefines.flatMap(pluginDefine => this.resolvePluginDependencies(pluginDefine)); - return await this.ensureInstalled(expandedPluginDefines); + return await this.ensureInstalled({ plugins: expandedPluginDefines, logger: log }); } - private async doEnsureInstalled(dependencies: Record): Promise { + private async doEnsureInstalled(options: { dependencies: Record; logger?: ILogger }): Promise { + let { dependencies } = options; + const log = options.logger || logger; return await this.withInstallLock(async () => { const rootDir = this.getRuntimeDepsRootDir(); const packageJsonPath = path.join(rootDir, "package.json"); const lockPath = path.join(rootDir, "pnpm-lock.yaml"); + log.info(`第三方依赖安装: ${JSON.stringify(dependencies)}`); dependencies = this.mergeInstalledDependencies(this.readManifestDependencies(packageJsonPath), dependencies); const dependenciesHash = this.createDependenciesHash(dependencies); const statePath = path.join(rootDir, "install-state.json"); const currentState = this.readInstallState(statePath); if (currentState?.dependenciesHash === dependenciesHash && fs.existsSync(path.join(rootDir, "node_modules"))) { + log.info("第三方依赖已安装"); return { registryUrl: currentState.registryUrl || "", packageJsonPath }; } const manifest = { @@ -260,10 +269,12 @@ export class RuntimeDepsService { const env = this.buildChildEnv(registryUrl); const command = this.getPnpmCommand(); const pnpmVersion = await this.getPnpmVersion(command, env); - const args = ["install", "--prod", "--ignore-scripts", "--ignore-workspace", "--reporter=append-only"]; + const args = ["install", "--prod", "--ignore-scripts", "--ignore-workspace", "--no-frozen-lockfile", "--reporter=append-only"]; if (registryUrl) { args.push(`--registry=${registryUrl}`); } + + log.info(`开始安装第三方依赖: ${Object.keys(dependencies).join(", ")}`); const result = await this.commandRunner.run(command, args, { cwd: rootDir, timeoutMs: this.installTimeoutMs, @@ -277,6 +288,7 @@ export class RuntimeDepsService { failedAt: new Date().toISOString(), registryUrl, dependenciesHash, + // @ts-ignore nodeVersion: process.version, pnpmVersion, lockFileExists: fs.existsSync(lockPath), @@ -288,35 +300,37 @@ export class RuntimeDepsService { installedAt: new Date().toISOString(), registryUrl, dependenciesHash, + // @ts-ignore nodeVersion: process.version, pnpmVersion, lockFileExists: fs.existsSync(lockPath), }); + log.info("第三方依赖安装完成"); return { registryUrl, packageJsonPath }; }); } - async importRuntime(specifier: string) { + async importRuntime(specifier: string,logger?:ILogger) { if (this.isNativeImportSpecifier(specifier)) { return await import(specifier); } - const resolved = await this.resolveImportSpecifier(specifier); + const resolved = await this.resolveImportSpecifier(specifier,logger); return await import(pathToFileURL(resolved).href); } - private async resolveImportSpecifier(specifier: string) { + private async resolveImportSpecifier(specifier: string,logger?:ILogger) { try { return this.resolveRuntimeSpecifier(specifier).resolved; } catch (runtimeError: any) { if (!this.isModuleNotFoundError(runtimeError)) { throw runtimeError; } - return await this.resolveMissingRuntimeSpecifier(specifier, runtimeError); + return await this.resolveMissingRuntimeSpecifier(specifier, runtimeError,logger); } } - private async resolveMissingRuntimeSpecifier(specifier: string, runtimeError: any) { + private async resolveMissingRuntimeSpecifier(specifier: string, runtimeError: any,logger?:ILogger) { const packageName = this.parsePackageName(specifier); const lazyRange = this.lazyDependencies?.[packageName]; if (!lazyRange) { @@ -327,7 +341,7 @@ export class RuntimeDepsService { } } try { - await this.ensureLazyDependency(packageName); + await this.ensureLazyDependency(packageName,logger); return this.resolveRuntimeSpecifier(specifier).resolved; } catch (lazyError: any) { return this.resolveProjectSpecifier(specifier, lazyError).resolved; @@ -378,7 +392,7 @@ export class RuntimeDepsService { return parts[0]; } - private async ensureLazyDependency(packageName: string) { + private async ensureLazyDependency(packageName: string,logger?:ILogger) { const range = this.lazyDependencies?.[packageName]; if (!range) { throw new Error(`动态依赖未安装且未配置懒加载版本: ${packageName}`); @@ -386,7 +400,7 @@ export class RuntimeDepsService { const dependencies = { [packageName]: range, }; - await this.ensureDependencies(dependencies); + await this.ensureDependencies({ dependencies,logger }); } private isModuleNotFoundError(error: any) { @@ -485,6 +499,7 @@ export class RuntimeDepsService { while (true) { try { const fd = fs.openSync(lockFile, "wx"); + // @ts-ignore fs.writeFileSync(fd, JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }), "utf8"); return fd; } catch (error: any) { @@ -508,6 +523,28 @@ export class RuntimeDepsService { } } + async clearRuntimeDeps() { + const rootDir = this.getRuntimeDepsRootDir(); + const normalizedRootDir = path.normalize(rootDir); + if (!normalizedRootDir.endsWith(path.normalize(".runtime-deps"))) { + throw new Error(`动态依赖目录配置异常,拒绝清理: ${rootDir}`); + } + await this.withInstallLock(async () => { + if (fs.existsSync(rootDir)) { + const entries = fs.readdirSync(rootDir); + for (const entry of entries) { + if (entry === ".install.lock") { + continue; + } + const entryPath = path.join(rootDir, entry); + fs.rmSync(entryPath, { recursive: true, force: true }); + } + } + this.installPromises.clear(); + return undefined; + }); + } + private readInstallState(statePath: string): any { if (!fs.existsSync(statePath)) { return null; @@ -547,6 +584,7 @@ export class RuntimeDepsService { return dependencies; } + // @ts-ignore private async getPnpmVersion(command: string, env: NodeJS.ProcessEnv) { const rootDir = this.getRuntimeDepsRootDir(); const result = await this.commandRunner.run(command, ["--version"], { @@ -568,6 +606,7 @@ export class RuntimeDepsService { } private buildChildEnv(registryUrl: string) { + // @ts-ignore const env = { ...process.env }; for (const key of ["NODE_OPTIONS", "VSCODE_INSPECTOR_OPTIONS", "NODE_INSPECTOR_PORT", "NODE_DEBUG"]) { if (!env[key]) { diff --git a/packages/ui/certd-server/src/modules/sys/nettest/nettest-service.ts b/packages/ui/certd-server/src/modules/sys/nettest/nettest-service.ts index c40c8eb60..e1dd802d3 100644 --- a/packages/ui/certd-server/src/modules/sys/nettest/nettest-service.ts +++ b/packages/ui/certd-server/src/modules/sys/nettest/nettest-service.ts @@ -41,10 +41,7 @@ export class NetTestService { // 判断测试是否成功 const normalizedOutput = output.toLowerCase(); - const success = this.isWindows() - ? normalizedOutput.includes("端口连接成功") - : normalizedOutput.includes("succeeded!") || normalizedOutput.includes("connected to") || normalizedOutput.includes(" open"); - + const success = this.isWindows() ? normalizedOutput.includes("端口连接成功") : normalizedOutput.includes("succeeded!") || normalizedOutput.includes("connected to") || normalizedOutput.includes(" open"); // 处理结果 return { diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/dns-provider/aliyun-dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/dns-provider/aliyun-dns-provider.ts index 39721031d..d1f27222d 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/dns-provider/aliyun-dns-provider.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/dns-provider/aliyun-dns-provider.ts @@ -16,7 +16,7 @@ export class AliyunDnsProvider extends AbstractDnsProvider { async onInstance() { const access: AliyunAccess = this.ctx.access as AliyunAccess; - this.client = new AliyunClient({ logger: this.logger }); + this.client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); await this.client.init({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts index 5bd447f08..71167bf48 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-ack/index.ts @@ -241,7 +241,7 @@ export class DeployCertToAliyunAckPlugin extends AbstractTaskPlugin { } async getClient(aliyunProvider: any, regionId: string) { - const client = new AliyunClient({ logger: this.logger, useROAClient: true }); + const client = new AliyunClient({ logger: this.logger, useROAClient: true, importRuntime: aliyunProvider.importRuntime.bind(aliyunProvider) }); await client.init({ accessKeyId: aliyunProvider.accessKeyId, accessKeySecret: aliyunProvider.accessKeySecret, diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-alb/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-alb/index.ts index fc820cb12..04ab71ecc 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-alb/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-alb/index.ts @@ -128,7 +128,7 @@ export class AliyunDeployCertToALB extends AbstractTaskPlugin { async onInstance() {} async getLBClient(access: AliyunAccess, region: string) { - const client = new AliyunClient({ logger: this.logger }); + const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); const version = "2020-06-16"; await client.init({ diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apig/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apig/index.ts index 36ed9173a..fd9642a97 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apig/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apig/index.ts @@ -231,7 +231,7 @@ export class DeployCertToAliyunApig extends AbstractTaskPlugin { return { list: records, total: res?.data?.totalSize || 0, - } + }; } async onGetRegionList(data: any) { diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apigateway/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apigateway/index.ts index 521b8fb66..311cee311 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apigateway/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-apigateway/index.ts @@ -134,7 +134,7 @@ export class DeployCertToAliyunApiGateway extends AbstractTaskPlugin { const access = await this.getAccess(this.accessId); const client = access.getClient(this.regionEndpoint); - const pager = new Pager(data) + const pager = new Pager(data); const res = await client.doRequest({ // 接口名称 action: "DescribeApiGroups", @@ -162,7 +162,7 @@ export class DeployCertToAliyunApiGateway extends AbstractTaskPlugin { return { list: records, total: res?.TotalCount || 0, - } + }; } async onGetDomainList(data: any) { diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-cdn/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-cdn/index.ts index 7b6507838..666943042 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-cdn/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-cdn/index.ts @@ -209,7 +209,7 @@ export class DeployCertToAliyunCDN extends AbstractTaskPlugin { } async getClient(access: AliyunAccess) { - const client = new AliyunClient({ logger: this.logger }); + const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); await client.init({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts index c760d664c..25da6228f 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-dcdn/index.ts @@ -143,7 +143,7 @@ export class DeployCertToAliyunDCDN extends AbstractTaskPlugin { } async getClient(access: AliyunAccess) { - const client = new AliyunClient({ logger: this.logger }); + const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); await client.init({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-fc/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-fc/index.ts index 6540681ad..29caf9fed 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-fc/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-fc/index.ts @@ -137,8 +137,8 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin { const client = await this.getClient(access); - const $Util = await import("@alicloud/tea-util"); - const $OpenApi = await import("@alicloud/openapi-client"); + const $Util = await access.importRuntime("@alicloud/tea-util"); + const $OpenApi = await access.importRuntime("@alicloud/openapi-client"); let privateKey = this.cert.key; try { @@ -204,7 +204,7 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin { } async getClient(access: AliyunAccess) { - const $OpenApi = await import("@alicloud/openapi-client"); + const $OpenApi = await access.importRuntime("@alicloud/openapi-client"); const config = new $OpenApi.Config({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, @@ -221,8 +221,8 @@ export class AliyunDeployCertToFC extends AbstractTaskPlugin { const access = await this.getAccess(this.accessId); const client = await this.getClient(access); - const $OpenApi = await import("@alicloud/openapi-client"); - const $Util = await import("@alicloud/tea-util"); + const $OpenApi = await access.importRuntime("@alicloud/openapi-client"); + const $Util = await access.importRuntime("@alicloud/tea-util"); const params = new $OpenApi.Params({ // 接口名称 action: "ListCustomDomains", diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-nlb/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-nlb/index.ts index 27d6c8610..28f937f8e 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-nlb/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-nlb/index.ts @@ -117,7 +117,7 @@ export class AliyunDeployCertToNLB extends AbstractTaskPlugin { async onInstance() {} async getLBClient(access: AliyunAccess, region: string) { - const client = new AliyunClient({ logger: this.logger }); + const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); const version = "2022-04-30"; await client.init({ diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-oss/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-oss/index.ts index 552aa9c75..1691d02dc 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-oss/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-oss/index.ts @@ -196,7 +196,7 @@ export class DeployCertToAliyunOSS extends AbstractTaskPlugin { async getClient(access: AliyunAccess) { // @ts-ignore - const OSS = await import("ali-oss"); + const OSS = await access.importRuntime("ali-oss"); return new OSS.default({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-slb/index.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-slb/index.ts index f4898b5f4..399a4f852 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-slb/index.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-slb/index.ts @@ -130,7 +130,7 @@ export class AliyunDeployCertToSLB extends AbstractTaskPlugin { async onInstance() {} async getLBClient(access: AliyunAccess, region: string) { - const client = new AliyunClient({ logger: this.logger }); + const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); const version = "2014-05-15"; await client.init({ accessKeyId: access.accessKeyId, diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cloud.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cloud.ts index d250a551d..6320f5518 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cloud.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cloud.ts @@ -103,7 +103,7 @@ export class AliyunDeployCertToWafCloud extends AbstractTaskPlugin { async onInstance() {} async getWafClient(access: AliyunAccess) { - const client = new AliyunClient({ logger: this.logger }); + const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); await client.init({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, diff --git a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cname.ts b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cname.ts index b48e740ab..a655798de 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cname.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aliyun/plugin/deploy-to-waf/deploy-to-waf-cname.ts @@ -115,7 +115,7 @@ export class AliyunDeployCertToWaf extends AbstractTaskPlugin { async onInstance() {} async getWafClient(access: AliyunAccess) { - const client = new AliyunClient({ logger: this.logger }); + const client = new AliyunClient({ logger: this.logger, importRuntime: access.importRuntime.bind(access) }); await client.init({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, diff --git a/packages/ui/certd-server/src/plugins/plugin-aws-cn/libs/aws-iam-client.ts b/packages/ui/certd-server/src/plugins/plugin-aws-cn/libs/aws-iam-client.ts index 11083e7da..3fa14366f 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aws-cn/libs/aws-iam-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aws-cn/libs/aws-iam-client.ts @@ -15,7 +15,7 @@ export class AwsIAMClient { } async importCertificate(certInfo: CertInfo, certName: string) { // 创建 IAM 客户端 - const { IAMClient, UploadServerCertificateCommand } = await import("@aws-sdk/client-iam"); + const { IAMClient, UploadServerCertificateCommand } = await this.access.importRuntime("@aws-sdk/client-iam"); const iamClient = new IAMClient({ region: this.region, // 替换为您的 AWS 区域 credentials: { diff --git a/packages/ui/certd-server/src/plugins/plugin-aws-cn/plugins/plugin-deploy-to-cloudfront.ts b/packages/ui/certd-server/src/plugins/plugin-aws-cn/plugins/plugin-deploy-to-cloudfront.ts index 662cf2b1f..ada64eac6 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aws-cn/plugins/plugin-deploy-to-cloudfront.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aws-cn/plugins/plugin-deploy-to-cloudfront.ts @@ -84,7 +84,7 @@ export class AwsCNDeployToCloudFront extends AbstractTaskPlugin { } //部署到CloudFront - const { CloudFrontClient, UpdateDistributionCommand, GetDistributionConfigCommand } = await import("@aws-sdk/client-cloudfront"); + const { CloudFrontClient, UpdateDistributionCommand, GetDistributionConfigCommand } = await this.importRuntime("@aws-sdk/client-cloudfront"); const cloudFrontClient = new CloudFrontClient({ region: this.region, credentials: { @@ -135,7 +135,7 @@ export class AwsCNDeployToCloudFront extends AbstractTaskPlugin { } const access = await this.getAccess(this.accessId); - const { CloudFrontClient, ListDistributionsCommand } = await import("@aws-sdk/client-cloudfront"); + const { CloudFrontClient, ListDistributionsCommand } = await this.importRuntime("@aws-sdk/client-cloudfront"); const cloudFrontClient = new CloudFrontClient({ region: this.region, credentials: { diff --git a/packages/ui/certd-server/src/plugins/plugin-aws/libs/aws-client.ts b/packages/ui/certd-server/src/plugins/plugin-aws/libs/aws-client.ts index 4a29daef9..d5550ee9c 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aws/libs/aws-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aws/libs/aws-client.ts @@ -21,7 +21,7 @@ export class AwsClient { } async importCertificate(certInfo: CertInfo) { // 创建 ACM 客户端 - const { ACMClient, ImportCertificateCommand } = await import("@aws-sdk/client-acm"); + const { ACMClient, ImportCertificateCommand } = await this.access.importRuntime("@aws-sdk/client-acm"); const acmClient = new ACMClient({ region: this.region, // 替换为您的 AWS 区域 credentials: { @@ -49,7 +49,7 @@ export class AwsClient { } async getCallerIdentity() { - const { STSClient, GetCallerIdentityCommand } = await import("@aws-sdk/client-sts"); + const { STSClient, GetCallerIdentityCommand } = await this.access.importRuntime("@aws-sdk/client-sts"); const client = new STSClient({ region: this.access.region || "us-east-1", @@ -68,7 +68,7 @@ export class AwsClient { } async route53ClientGet() { - const { Route53Client } = await import("@aws-sdk/client-route-53"); + const { Route53Client } = await this.access.importRuntime("@aws-sdk/client-route-53"); return new Route53Client({ region: this.region, credentials: { @@ -88,7 +88,7 @@ export class AwsClient { }; } async route53ListHostedZones(name: string): Promise<{ Id: string; Name: string }[]> { - const { ListHostedZonesByNameCommand } = await import("@aws-sdk/client-route-53"); // ES Modules import + const { ListHostedZonesByNameCommand } = await this.access.importRuntime("@aws-sdk/client-route-53"); // ES Modules import const client = await this.route53ClientGet(); const input = { @@ -96,7 +96,7 @@ export class AwsClient { DNSName: name, }; const command = new ListHostedZonesByNameCommand(input); - const response = await this.doRequest(() => client.send(command)); + const response: any = await this.doRequest(() => client.send(command)); if (response.HostedZones.length === 0) { throw new Error(`找不到 HostedZone ${name}`); } @@ -105,7 +105,7 @@ export class AwsClient { } async route53ListHostedZonesPage(req: PageSearch): Promise> { - const { ListHostedZonesByNameCommand } = await import("@aws-sdk/client-route-53"); // ES Modules import + const { ListHostedZonesByNameCommand } = await this.access.importRuntime("@aws-sdk/client-route-53"); // ES Modules import const client = await this.route53ClientGet(); const input: any = { @@ -116,7 +116,7 @@ export class AwsClient { input.DNSName = req.searchKey; } const command = new ListHostedZonesByNameCommand(input); - const response = await this.doRequest(() => client.send(command)); + const response: any = await this.doRequest(() => client.send(command)); let list: any[] = response.HostedZones || []; list = list.map((item: any) => ({ id: item.Id.replace("/hostedzone/", ""), @@ -129,7 +129,7 @@ export class AwsClient { } async route53ChangeRecord(req: { hostedZoneId: string; fullRecord: string; type: string; value: string; action: "UPSERT" | "DELETE" }) { - const { ChangeResourceRecordSetsCommand } = await import("@aws-sdk/client-route-53"); // ES Modules import + const { ChangeResourceRecordSetsCommand } = await this.access.importRuntime("@aws-sdk/client-route-53"); // ES Modules import // const { Route53Client, ChangeResourceRecordSetsCommand } = require("@aws-sdk/client-route-53"); // CommonJS import // import type { Route53ClientConfig } from "@aws-sdk/client-route-53"; const client = await this.route53ClientGet(); diff --git a/packages/ui/certd-server/src/plugins/plugin-aws/plugins/plugin-deploy-to-cloudfront.ts b/packages/ui/certd-server/src/plugins/plugin-aws/plugins/plugin-deploy-to-cloudfront.ts index 8a5b731bf..0b6cd3899 100644 --- a/packages/ui/certd-server/src/plugins/plugin-aws/plugins/plugin-deploy-to-cloudfront.ts +++ b/packages/ui/certd-server/src/plugins/plugin-aws/plugins/plugin-deploy-to-cloudfront.ts @@ -79,7 +79,7 @@ export class AwsDeployToCloudFront extends AbstractTaskPlugin { } //部署到CloudFront - const { CloudFrontClient, UpdateDistributionCommand, GetDistributionConfigCommand } = await import("@aws-sdk/client-cloudfront"); + const { CloudFrontClient, UpdateDistributionCommand, GetDistributionConfigCommand } = await this.importRuntime("@aws-sdk/client-cloudfront"); const cloudFrontClient = new CloudFrontClient({ region: this.region, credentials: { @@ -133,7 +133,7 @@ export class AwsDeployToCloudFront extends AbstractTaskPlugin { } const access = await this.getAccess(this.accessId); - const { CloudFrontClient, ListDistributionsCommand } = await import("@aws-sdk/client-cloudfront"); + const { CloudFrontClient, ListDistributionsCommand } = await this.importRuntime("@aws-sdk/client-cloudfront"); const cloudFrontClient = new CloudFrontClient({ region: this.region, credentials: { diff --git a/packages/ui/certd-server/src/plugins/plugin-azure/access.ts b/packages/ui/certd-server/src/plugins/plugin-azure/access.ts index 2a6ef42de..094a33b2d 100644 --- a/packages/ui/certd-server/src/plugins/plugin-azure/access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-azure/access.ts @@ -122,7 +122,7 @@ export class AzureAccess extends BaseAccess { this.ctx.logger.info(`找到 DNS 区域: ${matchingZone.name}, ID: ${matchingZone.id}`); return { - id: matchingZone.id.split("/").pop()!, + id: matchingZone.id.split("/").pop() || "", name: matchingZone.name, }; } @@ -136,7 +136,7 @@ export class AzureAccess extends BaseAccess { } list = list.map((item: any) => ({ - id: item.id.split("/").pop()!, + id: item.id.split("/").pop() || "", domain: item.name, })); diff --git a/packages/ui/certd-server/src/plugins/plugin-cmcc/cmcc-client.ts b/packages/ui/certd-server/src/plugins/plugin-cmcc/cmcc-client.ts index 857e36920..9060655b9 100644 --- a/packages/ui/certd-server/src/plugins/plugin-cmcc/cmcc-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-cmcc/cmcc-client.ts @@ -96,7 +96,7 @@ export class CmccClient { async getToken(): Promise { // 检查是否有有效的token if (this.isTokenValid()) { - return this.token!; + return this.token; } const datetime = this.getCurrentIsoTime(); diff --git a/packages/ui/certd-server/src/plugins/plugin-dynadot/dns-provider.ts b/packages/ui/certd-server/src/plugins/plugin-dynadot/dns-provider.ts index 7c3c91fcb..48dfe6803 100644 --- a/packages/ui/certd-server/src/plugins/plugin-dynadot/dns-provider.ts +++ b/packages/ui/certd-server/src/plugins/plugin-dynadot/dns-provider.ts @@ -36,10 +36,10 @@ export class DynadotDnsProvider extends AbstractDnsProvider { record_type: type.toLowerCase(), record_value1: value, record_value2: "", - } - ] + }, + ]; - await this.postRecords(domain, {subRecords, mainRecords: [], addToCurrent: true}); + await this.postRecords(domain, { subRecords, mainRecords: [], addToCurrent: true }); this.logger.info("添加域名解析成功:", fullRecord, value); return { @@ -79,8 +79,8 @@ export class DynadotDnsProvider extends AbstractDnsProvider { record_type: "txt", record_value1: "init_txt_by_certd", record_value2: "", - } - ] + }, + ]; } await this.postRecords(domain, { @@ -132,7 +132,7 @@ export class DynadotDnsProvider extends AbstractDnsProvider { return { mainRecords, subRecords }; } - private async postRecords(domain: string, records: { mainRecords: MainRecordItem[]; subRecords: SubRecordItem[] ,addToCurrent: boolean}): Promise { + private async postRecords(domain: string, records: { mainRecords: MainRecordItem[]; subRecords: SubRecordItem[]; addToCurrent: boolean }): Promise { await this.access.doRequest({ method: "POST", path: `/restful/v2/domains/${domain}/records`, diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/alioss-access.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/alioss-access.ts index 69c63ef7a..372325e78 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/alioss-access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/alioss-access.ts @@ -101,7 +101,7 @@ export class AliossAccess extends BaseAccess { async getClient(access: AliyunAccess) { // @ts-ignore - const OSS = await import("ali-oss"); + const OSS = await access.importRuntime("ali-oss"); return new OSS.default({ accessKeyId: access.accessKeyId, accessKeySecret: access.accessKeySecret, diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/aliyun-access.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/aliyun-access.ts index 33e76da51..090cb7dbd 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/aliyun-access.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/access/aliyun-access.ts @@ -45,7 +45,7 @@ export class AliyunAccess extends BaseAccess { } async getStsClient() { - const StsClient = await import("@alicloud/sts-sdk"); + const StsClient = await this.importRuntime("@alicloud/sts-sdk"); // 配置凭证 const sts = new StsClient.default({ diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/aliyun-client-v2.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/aliyun-client-v2.ts index cd2dd3258..11e8a869f 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/aliyun-client-v2.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/aliyun-client-v2.ts @@ -30,7 +30,7 @@ export class AliyunClientV2 { if (this.client) { return this.client; } - const $OpenApi = await import("@alicloud/openapi-client"); + const $OpenApi = await this.access.importRuntime("@alicloud/openapi-client"); // const Credential = await import("@alicloud/credentials"); // //@ts-ignore // const credential = new Credential.default.default({ @@ -52,9 +52,9 @@ export class AliyunClientV2 { async doRequest(req: AliyunClientV2Req) { const client = await this.getClient(); - const $OpenApi = await import("@alicloud/openapi-client"); - const $Util = await import("@alicloud/tea-util"); - const OpenApiUtil = await import("@alicloud/openapi-util"); + const $OpenApi = await this.access.importRuntime("@alicloud/openapi-client"); + const $Util = await this.access.importRuntime("@alicloud/tea-util"); + const OpenApiUtil = await this.access.importRuntime("@alicloud/openapi-util"); const params = new $OpenApi.Params({ // 接口名称 action: req.action, diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/base-client.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/base-client.ts index 4f4c8d78c..b2950ddc4 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/base-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/base-client.ts @@ -1,14 +1,17 @@ import { getGlobalAgents, ILogger } from "@certd/basic"; +import { ImportRuntime } from "@certd/pipeline"; export class AliyunClient { client: any; logger: ILogger; agent: any; useROAClient: boolean; + importRuntime: ImportRuntime; - constructor(opts: { logger: ILogger; useROAClient?: boolean }) { + constructor(opts: { logger: ILogger; useROAClient?: boolean; importRuntime?: ImportRuntime }) { this.logger = opts.logger; this.useROAClient = opts.useROAClient || false; + this.importRuntime = opts.importRuntime || (async (specifier: string) => await import(specifier)); const agents = getGlobalAgents(); this.agent = agents.httpsAgent; } @@ -17,13 +20,12 @@ export class AliyunClient { if (this.useROAClient) { return await this.getROAClient(); } - const Core = await import("@alicloud/pop-core"); + const Core = await this.importRuntime("@alicloud/pop-core"); return Core.default; } async getROAClient() { - const Core = await import("@alicloud/pop-core"); - console.log("aliyun sdk", Core); + const Core = await this.importRuntime("@alicloud/pop-core"); // @ts-ignore return Core.ROAClient; } diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/oss-client.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/oss-client.ts index 9b23309d9..d43c595f0 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/oss-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/oss-client.ts @@ -17,7 +17,7 @@ export class AliossClient { return; } // @ts-ignore - const OSS = await import("ali-oss"); + const OSS = await this.access.importRuntime("ali-oss"); const ossClient = new OSS.default({ accessKeyId: this.access.accessKeyId, accessKeySecret: this.access.accessKeySecret, diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/ssl-client.ts b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/ssl-client.ts index 65703e116..4c26223ad 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/ssl-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/aliyun/lib/ssl-client.ts @@ -55,7 +55,7 @@ export class AliyunSslClient { async getClient() { const access = this.opts.access; - const client = new AliyunClient({ logger: this.opts.logger }); + const client = new AliyunClient({ logger: this.opts.logger, importRuntime: access.importRuntime.bind(access) }); let endpoint = this.opts.endpoint || "cas.aliyuncs.com"; if (this.opts.endpoint == null && this.opts.region) { diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/oss/impls/s3.ts b/packages/ui/certd-server/src/plugins/plugin-lib/oss/impls/s3.ts index a09540941..da3e236a6 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/oss/impls/s3.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/oss/impls/s3.ts @@ -16,7 +16,7 @@ export default class S3OssClientImpl extends BaseOssClient { async init() { // import { S3Client } from "@aws-sdk/client-s3"; //@ts-ignore - const { S3Client } = await import("@aws-sdk/client-s3"); + const { S3Client } = await this.access.importRuntime("@aws-sdk/client-s3"); this.client = new S3Client({ forcePathStyle: true, //@ts-ignore @@ -32,7 +32,7 @@ export default class S3OssClientImpl extends BaseOssClient { async download(filePath: string, savePath: string): Promise { // @ts-ignore - const { GetObjectCommand } = await import("@aws-sdk/client-s3"); + const { GetObjectCommand } = await this.access.importRuntime("@aws-sdk/client-s3"); const key = path.join(this.rootDir, filePath); const params = { Bucket: this.access.bucket, // The name of the bucket. For example, 'sample_bucket_101'. @@ -47,7 +47,7 @@ export default class S3OssClientImpl extends BaseOssClient { async listDir(dir: string): Promise { // @ts-ignore - const { ListObjectsCommand } = await import("@aws-sdk/client-s3"); + const { ListObjectsCommand } = await this.access.importRuntime("@aws-sdk/client-s3"); const dirKey = this.join(this.rootDir, dir); const params = { Bucket: this.access.bucket, // The name of the bucket. For example, 'sample_bucket_101'. @@ -67,7 +67,7 @@ export default class S3OssClientImpl extends BaseOssClient { } async upload(filePath: string, fileContent: Buffer | string) { // @ts-ignore - const { PutObjectCommand } = await import("@aws-sdk/client-s3"); + const { PutObjectCommand } = await this.access.importRuntime("@aws-sdk/client-s3"); const key = path.join(this.rootDir, filePath); this.logger.info(`开始上传文件: ${key}`); const params = { @@ -88,7 +88,7 @@ export default class S3OssClientImpl extends BaseOssClient { } const key = filePath; // @ts-ignore - const { DeleteObjectCommand } = await import("@aws-sdk/client-s3"); + const { DeleteObjectCommand } = await this.access.importRuntime("@aws-sdk/client-s3"); await this.client.send( new DeleteObjectCommand({ Bucket: this.access.bucket, diff --git a/packages/ui/certd-server/src/plugins/plugin-lib/tencent/lib/cos-client.ts b/packages/ui/certd-server/src/plugins/plugin-lib/tencent/lib/cos-client.ts index b733f0e68..7c3a29b4a 100644 --- a/packages/ui/certd-server/src/plugins/plugin-lib/tencent/lib/cos-client.ts +++ b/packages/ui/certd-server/src/plugins/plugin-lib/tencent/lib/cos-client.ts @@ -1,5 +1,6 @@ import { TencentAccess } from "../access.js"; import { ILogger, safePromise } from "@certd/basic"; +import { ImportRuntime } from "@certd/pipeline"; import fs from "fs"; export class TencentCosClient { @@ -7,16 +8,18 @@ export class TencentCosClient { logger: ILogger; region: string; bucket: string; + importRuntime: ImportRuntime; - constructor(opts: { access: TencentAccess; logger: ILogger; region: string; bucket: string }) { + constructor(opts: { access: TencentAccess; logger: ILogger; region: string; bucket: string; importRuntime?: ImportRuntime }) { this.access = opts.access; this.logger = opts.logger; this.bucket = opts.bucket; this.region = opts.region; + this.importRuntime = opts.importRuntime || (async (specifier: string) => await import(specifier)); } async getCosClient() { - const sdk = await import("cos-nodejs-sdk-v5"); + const sdk = await this.importRuntime("cos-nodejs-sdk-v5"); const clientConfig = { SecretId: this.access.secretId, SecretKey: this.access.secretKey,