两层配置
- 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