演示

插件环境

两层配置:基础设施用 process env / base .env(Postgres、Redis);插件作用域用 @InjectEnv 的 Env——持久化在 env 表,支持 isPublic / isSecret。

两层配置

  • Base process env — DB_*、Redis、MODE、路径(base/backend/.env 或 Compose);用 process.env 读取
  • Plugin Env(@InjectEnv)— 当前插件作用域的键值;启动时从 Postgres 加载;可通过 ERP env UI / /env API 修改
  • 机密不要进 git — 放在 .env(本地)或 env 表(运行时)
  • 完整 base process-env 目录 — 见 Environment variables

Base 环境(.env)

base 应用与 worker 的基础设施连接配置。通常来自 base/backend/.env 与 Docker Compose。完整键列表见 Environment variables。

  • DB_HOST、DB_PORT、DB_USERNAME、DB_PASSWORD、DB_SCHEMA — Postgres
  • DB_SYNC — 可选 TypeORM synchronize(仅开发)
  • REDIS_HOST、REDIS_PORT、REDIS_PASSWORD — 缓存、队列、BullMQ cron
  • Compose 中 DB_HOST 通常是服务名 db
  • 主机上本地 psql / module seed 使用 localhost / 127.0.0.1
TSbase/backend/.env (example)
1DB_HOST=db 2DB_PORT=5432 3DB_USERNAME=postgres 4DB_PASSWORD=postgres 5DB_SCHEMA=quan_erp 6DB_SYNC=false 7REDIS_HOST=redis 8REDIS_PORT=6379 9REDIS_PASSWORD=

@InjectEnv

在模块、服务、工作流节点等 DI 类上注入 Env。实例绑定当前插件——get/set 只作用于该插件的键。

TSXinject
1import { 2 Env, 3 InjectEnv, 4 Module, 5 OnInit, 6 Service, 7} from "@quan-erp/shared-backend-core"; 8import metadata from "../../module.metadata.json" with { type: "json" }; 9 10@Service() 11export class PaymentConfigService { 12 @InjectEnv() 13 private env: Env; 14 15 getApiKey() { 16 return this.env.get("API_KEY"); 17 } 18} 19 20@Module({ 21 name: metadata.name, 22 providers: [PaymentConfigService], 23 controllers: [], 24 entities: [], 25}) 26export class MyPluginModule { 27 @InjectEnv() 28 env: Env; 29 30 @OnInit() 31 async init() { 32 // seed defaults — see below 33 } 34}

Env API

  • get(key) — 读取插件作用域值(密钥会解密)
  • set(key, value, options?) — 写入;InjectEnv 默认 { isPublic: false, sync: true, isSecret: false }
  • sync() — 将内存注册表持久化到 env 表
  • onChanged(key, callback) — 键变更时回调
  • clear() — 清除本插件的 env 条目
  • all() — 本插件全部键及 value / isPublic / isSecret
TSXget / set / sync
1import { Env, InjectEnv, OnInit, Service } from "@quan-erp/shared-backend-core"; 2 3@Service() 4export class IntegrationSettingsService { 5 @InjectEnv() 6 private env: Env; 7 8 @OnInit() 9 async init() { 10 if (!this.env.get("API_BASE_URL")) { 11 await this.env.set("API_BASE_URL", "https://api.example.com", { 12 isPublic: true, 13 isSecret: false, 14 sync: true, 15 }); 16 } 17 18 if (!this.env.get("API_KEY")) { 19 await this.env.set("API_KEY", "", { 20 isPublic: false, 21 isSecret: true, 22 sync: true, 23 }); 24 } 25 26 this.env.onChanged("API_KEY", () => { 27 // reload clients that cache the key 28 }); 29 } 30 31 async rotateKey(next: string) { 32 await this.env.set("API_KEY", next, { 33 isSecret: true, 34 sync: true, 35 }); 36 } 37}

set() 选项

  • sync — 立即写入 Postgres
  • isPublic — 出现在 GET /env/public
  • isSecret — 静态加密;列表中掩码
  • API key / token / 密码使用 isSecret: true
  • 仅对可安全给 UI 读取的非敏感值使用 isPublic: true

持久化与启动

启动时核心从 env 表加载到 PluginEnvConfigManager。通过 set({ sync: true }) 或 env.sync() 经 EnvService 持久化。

  • 表:env(EnvEntity)— pluginName、key、value、isPublic、isSecret
  • 启动:PluginEnvConfigManager.loadFromDB()
  • 密钥加密存储;get() 解密
  • 管理端批量更新后可能广播重启状态

HTTP 接口(核心)

  • GET /env — owner;密钥掩码
  • GET /env/public — 仅 isPublic: true
  • GET /env/db — owner;EnvService 原始行
  • PUT /env — 设置单个键并 sync
  • PUT /env/many — 批量设置后 syncToDB
  • 插件代码应使用 @InjectEnv(),不要调用这些 HTTP 路由

何时用哪种

  • process.env / .env — 主机、端口、Redis、Compose、@Database 连接选项
  • @InjectEnv — 运维可在 ERP UI 修改、无需重新部署的插件设置
  • DeveloperConfigService — 类型化开发者配置(不同于 Env)
  • SettingEntity / SettingService — 用户/系统设置 UI(不同于 Env)

仅本地操作

从 .env 读取 DB_* 的 module 种子等脚本只能针对本地/开发主机(localhost / 127.0.0.1)。切勿对 UAT 或生产执行。

  • 本地 INSERT 模式见 Module seed
  • 若 DB_HOST 是远程 UAT/生产主机,立即停止

包版本

将插件 package.json 中的 @quan-erp/* 版本与 base / shared-backend-core 对齐,保证 Env 注入与加密辅助兼容。

检查清单

  • 基础设施机密放在 base/backend/.env
  • 在属性上使用 @InjectEnv()(不支持构造函数注入)
  • 在 @OnInit 中用明确的 isPublic / isSecret / sync 种子缺失键
  • API key 使用 isSecret: true
  • 仅对安全的 UI 可读值使用 isPublic
  • 需要跨重启持久化时调用 sync() 或 sync: true