diff --git a/.trae/skills/fast-crud-page-dev/SKILL.md b/.trae/skills/fast-crud-page-dev/SKILL.md index 778dd29f4..43b7bbed4 100644 --- a/.trae/skills/fast-crud-page-dev/SKILL.md +++ b/.trae/skills/fast-crud-page-dev/SKILL.md @@ -77,6 +77,58 @@ container:{}, //容器配置 ,对应fs-container - 有固定操作栏、统计区、说明区时,这些区域应 `flex: none`,把剩余空间交给表格区域。 - 修改嵌入式 Fast Crud 页面后,要检查空数据、少量数据和多页数据时表格高度、分页器和空状态是否仍在预期区域内。 +## 内置 CRUD 按钮 + +只要在 `request` 中配置了 `addRequest`、`editRequest`、`delRequest`,Fast Crud 会自动在 `rowHandle` 渲染新增、编辑、删除按钮并完成对应操作,**不需要手写 `openDeleteConfirm`、`openEditDialog` 等方法**。 + +```typescript +// crud.tsx +const addRequest = async ({ form }: AddReq) => await api.AddObj(form); +const editRequest = async ({ form, row }: EditReq) => { + form.id = row.id; + return await api.UpdateObj(form); +}; +const delRequest = async ({ row }: DelReq) => await api.DelObj(row.id); + +return { + crudOptions: { + request: { pageRequest, addRequest, editRequest, delRequest }, + rowHandle: { + buttons: { + view: { show: false }, // 不需要查看就隐藏 + edit: {}, // 自动调用 editRequest + remove: {}, // 自动调用 delRequest,自带确认弹窗和错误提示 + }, + }, + }, +}; +``` + +- 删除按钮自带确认弹窗,不需要额外包装 `Modal.confirm`。 +- 只有**自定义操作**(如禁用、审核、生成激活码)才需要在 `rowHandle.buttons` 中手写 `click` 处理方法。 +- 如果不需要某列操作,直接把对应 key 去掉或设 `show: false`。 + +## compute 动态计算 + +当 `rowHandle.buttons` 的 `show`、`disabled` 等属性需要根据行数据动态决定时,**必须使用 `compute` 包裹**,不能直接传函数。 + +```typescript +import { compute } from "@fast-crud/fast-crud"; + +// WRONG: 直接传函数 +show: ({ row }) => row.status === "unused" + +// CORRECT: 用 compute 包裹 +show: compute(({ row }) => row.status === "unused") +``` + +`compute` 基于 Vue 的 `computed`,但额外支持上下文参数。适用位置: +- `rowHandle.buttons` 的 `show`、`disabled` 等属性 +- `columns.key.column` 的 `show`、`cellRender` 等 +- `columns.key.form` / `search` 的表单字段属性 + +参考文档:http://fast-crud.docmirror.cn/guide/advance/compute.html + ## 代码习惯 - 页面命名、API 命名、权限标识和路由结构要贴近同目录已有页面。 diff --git a/AGENTS.md b/AGENTS.md index 05d393942..5c9e122d5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -185,7 +185,11 @@ Certd 是一个支持私有化部署的 SSL/TLS 证书自动化管理平台。 - 遵守单一职责原则:一个方法只负责一个清晰的业务步骤或技术步骤。流程编排方法可以串联多个步骤,但具体的校验、计算、持久化、状态变更、展示数据组装应尽量拆到命名明确的小方法中;不要让一个方法同时承担查询、校验、计算、写库、格式化返回等过多职责。 - 后端方法参数超过 3 个时,尽量改为对象参数传入;需要传入 `manager` / `EntityManager` 做事务传播的方法,必须使用对象参数,不要把 `manager` 作为位置参数藏在参数列表末尾。 - 后端 service 层只有存在事务链路传播需求时才定义 `ctx`,不要为了将来可能需要而提前给普通方法加 `ctx`。事务链路方法统一采用 `method(ctx, req)` 形式,`ctx` 放第一位并承载 `manager?: EntityManager` 等横切上下文,业务参数放在 `req` 对象里,例如 `settleCommission({ manager }, { tradeId, userId, amount })`。无事务链路需求的普通查询、纯函数和简单私有方法继续使用明确参数。 -- service 内部需要根据事务上下文选择 Repository 时,优先使用 `BaseService.getRepo(ctx, entity)`;不要在业务方法里反复写 `ctx.manager?.getRepository(Entity) || this.xxxRepository`。`ctx` 类型统一从 `BaseService` 导出的 `ServiceContext` 复用,不要在每个 service 里重复定义。 +- service 内部需要根据事务上下文选择 Repository 时,优先使用 `BaseService.getRepo(ctx, EntityClass)`;不要在业务方法里反复写 `ctx.manager?.getRepository(Entity) || this.xxxRepository`。拿到 repo 后 save/update/delete/find 都能做,不需要再包一层 `saveEntity` 之类的单一用途方法。`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/docs/guide/plugins/access.md b/docs/guide/plugins/access.md index 25e434094..024716912 100644 --- a/docs/guide/plugins/access.md +++ b/docs/guide/plugins/access.md @@ -23,62 +23,63 @@ | 19.| **微软云Azure授权** | | | 20.| **BIND9 DNS 授权** | 通过 SSH 连接到 BIND9 服务器,使用 nsupdate 命令管理 DNS 记录 | | 21.| **CacheFly** | CacheFly | -| 22.| **EAB授权** | ZeroSSL证书申请需要EAB授权 | -| 23.| **google cloud** | 谷歌云授权 | -| 24.| **cloudflare授权** | | -| 25.| **中国移动CND授权** | | -| 26.| **授权插件示例** | 这是一个示例授权插件,用于演示如何实现一个授权插件 | -| 27.| **dns.la授权** | | -| 28.| **彩虹DNS** | 彩虹DNS管理系统授权 | -| 29.| **多吉云** | | -| 30.| **Dokploy授权** | | -| 31.| **farcdn授权** | | -| 32.| **FlexCDN授权** | | -| 33.| **Gcore** | Gcore | -| 34.| **Github授权** | | -| 35.| **godaddy授权** | | -| 36.| **HiPM DNSMgr** | HiPM DNSMgr API Token 授权 | -| 37.| **金山云授权** | | -| 38.| **FTP授权** | | -| 39.| **七牛OSS授权** | | -| 40.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 | -| 41.| **s3/minio授权** | S3/minio oss授权 | -| 42.| **namesilo授权** | | -| 43.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 | -| 44.| **Nginx Proxy Manager 授权** | 用于登录 Nginx Proxy Manager,并为代理主机证书部署提供授权。 | -| 45.| **1panel授权** | 账号和密码 | -| 46.| **支付宝** | | -| 47.| **白山云授权** | | -| 48.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 | -| 49.| **cdnfly授权** | | -| 50.| **k8s授权** | | -| 51.| **括彩云cdn授权** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) | -| 52.| **LeCDN授权** | | -| 53.| **lucky** | | -| 54.| **猫云授权** | | -| 55.| **plesk授权** | | -| 56.| **长亭雷池授权** | | -| 57.| **群晖登录授权** | | -| 58.| **uniCloud** | unicloud授权 | -| 59.| **微信支付** | | -| 60.| **易盾rcdn授权** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) | -| 61.| **易发云短信** | sms.yfyidc.cn/ | -| 62.| **易盾DCDN授权** | https://user.yiduncdn.com | -| 63.| **易支付** | | -| 64.| **proxmox** | | -| 65.| **Spaceship.com 授权** | Spaceship.com API 授权插件 | -| 66.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器授权 | -| 67.| **UCloud授权** | 优刻得授权 | -| 68.| **又拍云** | | -| 69.| **网宿授权** | | -| 70.| **西部数码授权** | | -| 71.| **我爱云授权** | 我爱云CDN | -| 72.| **新网授权(代理方式)** | | -| 73.| **新网授权** | | -| 74.| **新网互联授权** | 仅支持代理账号,ip需要加入白名单 | -| 75.| **Zenlayer授权** | Zenlayer授权 | -| 76.| **GoEdge授权** | | -| 77.| **雨云授权** | https://app.rainyun.com/ | +| 22.| **ACME账号** | 用于复用ACME账号私钥和账号地址,证书申请时不再临时创建账号 | +| 23.| **EAB授权** | ZeroSSL证书申请需要EAB授权 | +| 24.| **google cloud** | 谷歌云授权 | +| 25.| **cloudflare授权** | | +| 26.| **中国移动CND授权** | | +| 27.| **授权插件示例** | 这是一个示例授权插件,用于演示如何实现一个授权插件 | +| 28.| **dns.la授权** | | +| 29.| **彩虹DNS** | 彩虹DNS管理系统授权 | +| 30.| **多吉云** | | +| 31.| **Dokploy授权** | | +| 32.| **farcdn授权** | | +| 33.| **FlexCDN授权** | | +| 34.| **Gcore** | Gcore | +| 35.| **Github授权** | | +| 36.| **godaddy授权** | | +| 37.| **HiPM DNSMgr** | HiPM DNSMgr API Token 授权 | +| 38.| **金山云授权** | | +| 39.| **FTP授权** | | +| 40.| **七牛OSS授权** | | +| 41.| **腾讯云COS授权** | 腾讯云对象存储授权,包含地域和存储桶 | +| 42.| **s3/minio授权** | S3/minio oss授权 | +| 43.| **namesilo授权** | | +| 44.| **Next Terminal 授权** | 用于访问 Next Terminal API 的授权配置 | +| 45.| **Nginx Proxy Manager 授权** | 用于登录 Nginx Proxy Manager,并为代理主机证书部署提供授权。 | +| 46.| **1panel授权** | 账号和密码 | +| 47.| **支付宝** | | +| 48.| **白山云授权** | | +| 49.| **宝塔云WAF授权** | 用于连接和管理宝塔云WAF服务的授权配置 | +| 50.| **cdnfly授权** | | +| 51.| **k8s授权** | | +| 52.| **括彩云cdn授权** | 括彩云CDN,每月免费30G,[注册即领](https://kuocaicdn.com/register?code=8mn536rrzfbf8) | +| 53.| **LeCDN授权** | | +| 54.| **lucky** | | +| 55.| **猫云授权** | | +| 56.| **plesk授权** | | +| 57.| **长亭雷池授权** | | +| 58.| **群晖登录授权** | | +| 59.| **uniCloud** | unicloud授权 | +| 60.| **微信支付** | | +| 61.| **易盾rcdn授权** | 易盾CDN,每月免费30G,[注册即领](https://rhcdn.yiduncdn.com/register?code=8mn536rrzfbf8) | +| 62.| **易发云短信** | sms.yfyidc.cn/ | +| 63.| **易盾DCDN授权** | https://user.yiduncdn.com | +| 64.| **易支付** | | +| 65.| **proxmox** | | +| 66.| **Spaceship.com 授权** | Spaceship.com API 授权插件 | +| 67.| **Technitium DNS Server** | Technitium DNS Server 自建DNS服务器授权 | +| 68.| **UCloud授权** | 优刻得授权 | +| 69.| **又拍云** | | +| 70.| **网宿授权** | | +| 71.| **西部数码授权** | | +| 72.| **我爱云授权** | 我爱云CDN | +| 73.| **新网授权(代理方式)** | | +| 74.| **新网授权** | | +| 75.| **新网互联授权** | 仅支持代理账号,ip需要加入白名单 | +| 76.| **Zenlayer授权** | Zenlayer授权 | +| 77.| **GoEdge授权** | | +| 78.| **雨云授权** | https://app.rainyun.com/ | diff --git a/packages/ui/certd-server/db/migration/v10048__activation_code.sql b/packages/ui/certd-server/db/migration/v10048__activation_code.sql new file mode 100644 index 000000000..64dc9af76 --- /dev/null +++ b/packages/ui/certd-server/db/migration/v10048__activation_code.sql @@ -0,0 +1,29 @@ +-- 激活码表 +CREATE TABLE "cd_product_activation_code" +( + "id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, + "code" varchar(50) NOT NULL, + "product_id" integer NOT NULL, + "duration" integer NOT NULL, + "batch_no" varchar(50) NOT NULL DEFAULT '', + "status" varchar(20) NOT NULL DEFAULT 'unused', + "used_user_id" integer, + "used_time" integer, + "expire_time" integer, + "disabled_time" integer, + "exported" integer NOT NULL DEFAULT 0, + "export_time" integer, + "remark" varchar(500) NOT NULL DEFAULT '', + "create_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP), + "update_time" datetime NOT NULL DEFAULT (CURRENT_TIMESTAMP) +); + +CREATE UNIQUE INDEX "index_activation_code_code" ON "cd_product_activation_code" ("code"); +CREATE INDEX "index_activation_code_batch_no" ON "cd_product_activation_code" ("batch_no"); +CREATE INDEX "index_activation_code_status" ON "cd_product_activation_code" ("status"); +CREATE INDEX "index_activation_code_expire_time" ON "cd_product_activation_code" ("expire_time"); +CREATE INDEX "index_activation_code_exported" ON "cd_product_activation_code" ("exported"); + +-- cd_user_suite 增加激活码来源追溯 +ALTER TABLE "cd_user_suite" + ADD COLUMN "activation_code_id" integer; diff --git a/packages/ui/certd-server/metadata/access_acmeAccount.yaml b/packages/ui/certd-server/metadata/access_acmeAccount.yaml new file mode 100644 index 000000000..0987e7bcb --- /dev/null +++ b/packages/ui/certd-server/metadata/access_acmeAccount.yaml @@ -0,0 +1,129 @@ +name: acmeAccount +title: ACME账号 +desc: 用于复用ACME账号私钥和账号地址,证书申请时不再临时创建账号 +icon: ph:certificate +subtype: caType +input: + caType: + title: 颁发机构 + component: + name: a-select + options: + - value: letsencrypt + label: Let's Encrypt + - value: letsencrypt_staging + label: Let's Encrypt测试环境 + - value: google + label: Google + - value: zerossl + label: ZeroSSL + - value: litessl + label: litessl + - value: sslcom + label: SSL.com + required: true + mergeScript: |2- + + return { + component: { + disabled: ctx.compute(({form})=> !!form.access?.account) + } + } + + email: + title: 邮箱 + component: + placeholder: user@example.com + rules: + - type: email + message: 请输入正确的邮箱 + required: true + mergeScript: |2- + + return { + component: { + disabled: ctx.compute(({form})=> !!form.access?.account) + } + } + + directoryUrl: + title: ACME Directory URL + component: + placeholder: 自定义ACME服务端点 + helper: 自定义ACME时必填,其他颁发机构默认自动使用内置端点 + required: false + mergeScript: |2- + + return { + show: false, + } + + eabKid: + title: EAB KID + component: + placeholder: 需要EAB的颁发机构生成账号时填写 + helper: >- + 需要提供EAB授权 + + ZeroSSL:请前往[zerossl开发者中心](https://app.zerossl.com/developer),生成 'EAB + Credentials' + + Google:请查看[google获取eab帮助文档](https://certd.docmirror.cn/guide/use/google/),用过一次后会绑定邮箱,后续复用EAB要用同一个邮箱 + + SSL.com:[SSL.com账号页面](https://secure.ssl.com/account),然后点击api + credentials链接,然后点击编辑按钮,查看Secret key和HMAC key + + litessl:[litesslEAB页面](https://freessl.cn/automation/eab-manager),然后点击新增EAB + required: false + encrypt: true + mergeScript: |2- + + return { + show: ctx.compute(({form})=>{ + const caType = form.access?.caType; + return ['google','zerossl','sslcom','litessl'].includes(caType); + }), + component: { + disabled: ctx.compute(({form})=> !!form.access?.account) + } + } + + eabHmacKey: + title: EAB HMAC Key + component: + placeholder: 需要EAB的颁发机构生成账号时填写 + required: false + encrypt: true + mergeScript: |2- + + return { + show: ctx.compute(({form})=>{ + const caType = form.access?.caType; + return ['google','zerossl','sslcom','litessl'].includes(caType); + }), + component: { + disabled: ctx.compute(({form})=> !!form.access?.account) + } + } + + account: + title: ACME账号信息 + component: + name: refresh-input + action: GenerateAccount + buttonText: 生成ACME账号 + successMessage: ACME账号已生成,请保存授权配置 + required: true + helper: 请生成ACME账号,账号一旦生成不允许修改 + encrypt: true + mergeScript: |2- + + return { + component: { + disabled: ctx.compute(({form})=> !!form.access?.account) + } + } + +pluginType: access +type: builtIn +scriptFilePath: /plugins/plugin-cert/access/acme-account-access.js diff --git a/packages/ui/certd-server/metadata/deploy_CertApply.yaml b/packages/ui/certd-server/metadata/deploy_CertApply.yaml index 1329e33f0..4e11abab3 100644 --- a/packages/ui/certd-server/metadata/deploy_CertApply.yaml +++ b/packages/ui/certd-server/metadata/deploy_CertApply.yaml @@ -51,6 +51,12 @@ input: required: true order: -1 helper: 请输入邮箱 + version: + title: 版本 + value: 2 + isSys: true + show: false + order: 0 challengeType: title: 域名验证方式 value: dns @@ -60,6 +66,8 @@ input: options: - value: dns label: DNS直接验证 + - value: dns-persist + label: DNS持久验证 - value: cname label: CNAME代理验证 - value: http @@ -80,6 +88,9 @@ input: 4. 多DNS提供商:每个域名可以选择独立的DNS提供商 5. 自动匹配:此处无需选择校验方式,需要在[域名管理](#/certd/cert/domain)中提前配置好校验方式 + + 6. DNS持久验证:需要先配置ACME账号和_validation-persist持久TXT记录,续期时不再增删DNS记录;当前仅 + Let's Encrypt 测试环境可以申请 order: 0 dnsProviderType: title: DNS解析服务商 @@ -103,7 +114,7 @@ input: required: true helper: |- 您的域名注册商,或者域名的dns服务器属于哪个平台 - 如果这里没有,请选择CNAME代理验证校验方式 + 如果这里没有,请选择CNAME代理验证 order: 0 dnsProviderAccess: title: DNS解析授权 @@ -141,18 +152,30 @@ input: }), defaultType: ctx.compute(({form})=>{ return form.challengeType || 'cname' + }), + caType: ctx.compute(({form})=>{ + return form.sslProvider + }), + acmeAccountAccessId: ctx.compute(({form})=>{ + return form.acmeAccountAccessId + }), + commonAcmeAccountAccessId: ctx.compute(({form})=>{ + const key = form.sslProvider + 'CommonAcmeAccountAccessId'; + return form[key] }) }, show: ctx.compute(({form})=>{ - return form.challengeType === 'cname' || form.challengeType === 'http' || form.challengeType === 'dnses' + return form.challengeType === 'cname' || form.challengeType === 'http' || form.challengeType === 'dnses' || form.challengeType === 'dns-persist' }), helper: ctx.compute(({form})=>{ if(form.challengeType === 'cname' ){ return '请按照上面的提示,给要申请证书的域名添加CNAME记录,添加后,点击验证,验证成功后不要删除记录,申请和续期证书会一直用它' }else if (form.challengeType === 'http'){ - return '请按照上面的提示,给每个域名设置文件上传配置,证书申请过程中会上传校验文件到网站根目录的.well-known/acme-challenge/目录下' - }else if (form.challengeType === 'http'){ + return '请按照上面的提示,给每个域名设置文件上传配置,证书申请过程中会上传校验文件到网站根目录文件夹下,请确保该校验文件可以公网http访问到' + }else if (form.challengeType === 'dnses'){ return '给每个域名单独配置dns提供商' + }else if (form.challengeType === 'dns-persist'){ + return '请先创建并校验_validation-persist TXT持久记录,校验成功后才能提交流水线;当前仅 Let\'s Encrypt 测试环境可以申请' } }) } @@ -195,21 +218,41 @@ input: isSys: true show: false order: 0 + googleCommonAcmeAccountAccessId: + title: Google公共ACME账号 + isSys: true + show: false + order: 0 zerosslCommonEabAccessId: title: ZeroSSL公共EAB授权 isSys: true show: false order: 0 + zerosslCommonAcmeAccountAccessId: + title: ZeroSSL公共ACME账号 + isSys: true + show: false + order: 0 sslcomCommonEabAccessId: title: SSL.com公共EAB授权 isSys: true show: false order: 0 + sslcomCommonAcmeAccountAccessId: + title: SSL.com公共ACME账号 + isSys: true + show: false + order: 0 litesslCommonEabAccessId: title: litessl公共EAB授权 isSys: true show: false order: 0 + litesslCommonAcmeAccountAccessId: + title: litessl公共ACME账号 + isSys: true + show: false + order: 0 eabAccessId: title: EAB授权 component: @@ -233,7 +276,16 @@ input: return { show: ctx.compute(({form})=>{ - console.log("show",form) + if (form.version === 2) { + return false + } + if(form.acmeAccountAccessId){ + return false + } + const commonAcmeKey = form.sslProvider + 'CommonAcmeAccountAccessId'; + if (form[commonAcmeKey]) { + return false + } return (form.sslProvider === 'zerossl' && !form.zerosslCommonEabAccessId) || (form.sslProvider === 'google' && !form.googleCommonEabAccessId) || (form.sslProvider === 'sslcom' && !form.sslcomCommonEabAccessId) @@ -241,6 +293,36 @@ input: }) } + order: 0 + acmeAccountAccessId: + title: ACME账号 + component: + name: access-selector + type: acmeAccount + required: false + helper: 请选择颁发机构对应的ACME账号 + mergeScript: |2- + + return { + show: ctx.compute(({form})=>{ + const commonKey = form.sslProvider + 'CommonAcmeAccountAccessId'; + if (form[commonKey]) { + return false + } + return !!form.sslProvider + }), + component:{ + subtype: ctx.compute(({form})=> form.sslProvider) + }, + required: ctx.compute(({form})=>{ + const commonKey = form.sslProvider + 'CommonAcmeAccountAccessId'; + if (form[commonKey]) { + return false + } + return form.version === 2 + }) + } + order: 0 googleAccessId: title: 服务账号授权 @@ -257,6 +339,15 @@ input: return { show: ctx.compute(({form})=>{ + if (form.version === 2) { + return false + } + if(form.acmeAccountAccessId){ + return false + } + if(form.googleCommonAcmeAccountAccessId){ + return false + } return form.sslProvider === 'google' && !form.googleCommonEabAccessId }) } diff --git a/packages/ui/certd-server/metadata/deploy_CertApplyGetFormAliyun.yaml b/packages/ui/certd-server/metadata/deploy_CertApplyGetFormAliyun.yaml index 1895e34c2..8c91b9bfc 100644 --- a/packages/ui/certd-server/metadata/deploy_CertApplyGetFormAliyun.yaml +++ b/packages/ui/certd-server/metadata/deploy_CertApplyGetFormAliyun.yaml @@ -81,7 +81,6 @@ input: search: false pager: true single: true - pageSize: 50 watches: - certDomains - accessId @@ -96,7 +95,7 @@ input: }, } - helper: 订阅模式的证书订单 Id(在新建流水线时暂时无法获取,可以先随便填个数字,先创建,进入流水线编辑页面再获取选择即可) + helper: 订阅模式的证书订单 Id order: 0 pfxPassword: title: 证书加密密码 diff --git a/packages/ui/certd-server/src/configuration.ts b/packages/ui/certd-server/src/configuration.ts index 8eaaef70c..f24c45feb 100644 --- a/packages/ui/certd-server/src/configuration.ts +++ b/packages/ui/certd-server/src/configuration.ts @@ -134,5 +134,6 @@ export class MainConfiguration { }); logger.info("当前环境:", this.app.getEnv()); // prod + } }