NocoBase EnvVariableInput 组件详解:面向$env环境变量的安全输入方案
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
EnvVariableInput是 NocoBase client-v2 中面向环境变量的单行输入组件,它只暴露$env命名空间,专门用于密钥、凭证、连接参数等敏感字段的配置。读完本文你将掌握该组件的完整用法、password遮盖模式的底层机制、值与{{ $env.X.Y }}表达式之间的双向转换原理,以及它如何与 environment-variables 插件协同工作。
组件定位与适用场景
在 NocoBase 的业务系统构建中,很多字段的值不应硬编码,而应引用部署环境提供的变量——例如 S3 的 Access Key、OAuth 的 client secret、SMTP 连接参数等。官方文档对EnvVariableInput的定位是:
EnvVariableInput是面向环境变量的输入组件。它只暴露$env命名空间,适合密钥、凭证和连接参数。
它本质上是通用 VariableInput 的受限变体:在源码 EnvVariableInput.tsx 中,组件内部以namespaces={ENV_NAMESPACES}(即['$env'])调用VariableInput,把变量选择器的可选范围牢牢限定在$env一棵树上,避免用户误选$user等其他命名空间的变量。
典型使用场景包括:
- 表单中的密钥/凭证字段(Access Key Secret、API Key、OAuth Secret);
- 连接参数(Base URL、SMTP 端口);
- 任何「要么填字面值、要么引用环境变量」的单行短文本字段。
如果你需要字段同时支持类型化常量(数字、布尔、日期)和变量引用,请改用 TypedVariableInput;如果只是普通字面量输入,直接使用 Antd 原生Input即可。
基本用法
从@nocobase/client-v2引入组件后,配合 AntdForm.Item即可直接使用:
import { EnvVariableInput } from '@nocobase/client-v2'; <Form.Item name={['options', 'accessKeySecret']} label={t('Access Key Secret')}> <EnvVariableInput password /> </Form.Item>;name={['options', 'accessKeySecret']}将值嵌套存储在表单数据的options.accessKeySecret中;password开启遮盖模式(详见下文);- 组件是受控组件,通过
value/onChange与表单联动,因此也可以不依赖Form.Item单独使用。
仓库中的官方演示 env-variable-input.tsx 给出了一个更完整的形态:用initialValues预置{{ $env.ACCESS_KEY_SECRET }}表达式,配合placeholder="Input secret or select env variable"提示用户既可输入密钥字面值,也可选择环境变量:
import React from 'react'; import { Application, EnvVariableInput, Plugin } from '@nocobase/client-v2'; import { Form } from 'antd'; function DemoPage() { return ( <Form layout="vertical" style={{ maxWidth: 420 }} initialValues={{ accessKeySecret: '{{ $env.ACCESS_KEY_SECRET }}' }} > <Form.Item name="accessKeySecret" label="Access Key Secret"> <EnvVariableInput password placeholder="Input secret or select env variable" /> </Form.Item> </Form> ); }password 模式:密钥遮盖的实现原理
password是该组件最具安全价值的能力。官方文档的描述是:
开启
password后,非变量字面值会用 AntdInput.Password遮盖;变量表达式仍然可以通过变量选择器编辑。
也就是说,遮盖是有条件的。从源码 EnvVariableInput.tsx 可以看到完整判定逻辑:
const isVariableExpr = (value?: string) => typeof value === 'string' && /\{\{\s*[^{}]+?\s*\}\}/.test(value); if (password && rest.value && !isVariableExpr(rest.value)) { return ( <Input.Password disabled={rest.disabled} placeholder={rest.placeholder} value={rest.value} onChange={(event) => rest.onChange?.(event.target.value)} autoFocus /> ); } return <VariableInput {...rest} namespaces={ENV_NAMESPACES} converters={converters} />;具体行为:
| 当前值 | password=false | password=true |
|---|---|---|
字面值(如my-secret) | 普通可编辑文本 | AntdInput.Password遮盖显示,可继续编辑 |
变量表达式(如{{ $env.API_KEY }}) | 渲染为变量 pill,可编辑 | 仍渲染为变量 pill,不被遮盖,仍可通过变量选择器编辑 |
这套行为被测试用例 EnvVariableInput.test.tsx 严格验证:明文值my-secret在password下输入框的type属性为password;而{{ $env.API_KEY }}在password=true时依旧以带nb-variable-tag样式的 pill 展示,且不会以明文文本出现。
这样设计的原因很直观:变量表达式本身不包含敏感信息,只是对$env树上某个键的引用,真正敏感的是被引用的值(存储于服务端);而用户在输入框里直接敲入的明文密钥才需要遮盖保护。
API 一览
官方文档给出的完整参数如下:
| 参数 | 类型 | 说明 |
|---|---|---|
value | string | 当前值 |
onChange | (value: string) => void | 值变化回调 |
addonBefore | React.ReactNode | 前置内容 |
disabled | boolean | 是否禁用 |
password | boolean | 是否遮盖非变量字面值 |
placeholder | string | 占位文字 |
从源码 EnvVariableInputProps 可以看到这些 props 与接口一一对应。其中disabled在两种渲染分支(Input.Password与变量编辑器)下都会生效:测试 EnvVariableInput.test.tsx 验证了禁用时编辑器contenteditable为false;placeholder在变量编辑分支下会映射到data-placeholder属性(见测试 L156-L164)。
值格式:{{ $env.X.Y }}与变量的双向转换
EnvVariableInput存储的值是形如{{ $env.ACCESS_KEY_SECRET }}、{{ $env.foo.bar }}的字符串——这是 NocoBase 服务端模板(Handlebars 风格)兼容的表达式格式。为了让变量选择器的内部路径表示(如['$env', 'foo', 'bar'])与存储字符串互相转换,组件导出了两个工具函数,并作为converters注入底层VariableHybridInput:
parseEnvPath:字符串 → 路径数组
源码 parseEnvPath 只接受「整个输入恰好是一个$env表达式」的情况(锚定正则^\{\{\s*(\$env\.[^{}]+?)\s*\}\}$),并将其拆分为路径:
parseEnvPath('{{ $env.API_KEY }}') // => ['$env', 'API_KEY'] parseEnvPath('{{ $env.foo.bar.baz }}') // => ['$env', 'foo', 'bar', 'baz'] parseEnvPath(' {{ $env.API_KEY }} ') // => ['$env', 'API_KEY'](自动去空白) parseEnvPath('plain value') // => undefined parseEnvPath('{{ $user.name }}') // => undefined(非 $env 命名空间) parseEnvPath('prefix {{ $env.API_KEY }}') // => undefined(混合内容) parseEnvPath('') // => undefined以上行为在测试 EnvVariableInput.test.tsx 中逐一断言。注意「混合内容」(前缀/后缀夹杂变量)会被判定为 undefined,这正是单行纯变量输入与 VariableTextArea 多行自由文本输入的区别。
formatEnvPath:路径数组 → 字符串
formatEnvPath 负责把变量选择器选中的 meta tree 节点还原为服务端可用的表达式,保证值「经 API 往返后字节稳定」:
formatEnvPath({ paths: ['$env', 'API_KEY'] }) // => '{{ $env.API_KEY }}' formatEnvPath({ paths: ['$env', 'foo', 'bar'] }) // => '{{ $env.foo.bar }}' formatEnvPath({ paths: ['$user', 'name'] }) // => undefined(拒绝非 $env) formatEnvPath({ paths: ['$env'] }) // => undefined(仅有根节点) formatEnvPath(undefined) // => undefined函数会先检查paths[0] !== '$env' || paths.length < 2,只有选中了$env下至少一级属性时才会生成表达式(测试见 L81-L102)。这两个转换器连同ENV_EXPR_REGEXP(全局匹配$env变量用于渲染 pill)一起,由组件通过useMemo构造并注入VariableInput:
const converters = useMemo<VariableHybridInputConverters>( () => ({ formatPathToValue: formatEnvPath, parseValueToPath: parseEnvPath, variableRegExp: ENV_EXPR_REGEXP, }), [], );这种「自定义 converters 收敛到单一命名空间」的模式,正是VariableInput文档中converters参数所描述的典型用途(见 variable-input.md)。
底层依赖:$env变量树从哪来
EnvVariableInput本身不产生变量数据,它消费的是 flow-engine context 上的$env属性树。组件源码注释明确指出:
The
$envtree is provided by the environment-variables plugin'sflowEngine.context.defineProperty('$env', ...); this component degrades gracefully to an empty picker when no env variables are defined.
也就是说,当环境中没有定义任何环境变量时,变量选择器打开后为空,组件仍可正常用于输入字面值——这是「优雅降级」设计。
$env树的注册逻辑位于 environment-variables 插件,见 registerEnvProperty.ts。核心要点:
- 数据来源:通过 API
environmentVariables?paginate=false惰性拉取环境变量列表(首次读取时请求,并由 flow-engine context 缓存,选择器展开时不会重复请求); - meta 树结构:每个变量名对应一个
{ type: 'string', title: name }的属性节点,其中type: 'secret'的变量也被建模为字符串类型; - 框架无关:该注册函数不依赖 React hooks 或
@nocobase/client,通过参数注入apiClient和翻译函数t,可同时服务于 client-v2 与 v1 两套运行时。
服务端对应的数据集合定义在 environmentVariables.ts,包含三个字段:
| 字段 | 类型 | 说明 |
|---|---|---|
name | string(主键) | 变量名,需匹配^[A-Za-z][A-Za-z0-9_]*$(见 re.ts) |
type | string | default或secret |
value | text | 变量值 |
变量名只能以字母开头、由字母数字下划线组成,这个约束保证了{{ $env.X.Y }}表达式能被可靠解析。字段命名空间中$env这个约定也贯穿于 flow-engine 的工作流变量选项,例如 useWorkflowVariableOptions.tsx 中同样会读取该属性树。
实战建议
- 密钥字段务必开启
password:accessKeySecret、apiKey、oauthSecret这类字段直接加password属性,明文字面值会被Input.Password遮盖; - 值优先用表达式而非明文:在 environment-variables 插件 中先定义好变量,再在表单里通过变量选择器引用
{{ $env.XXX }},让密钥只存在于服务端; - 注意变量命名约束:环境变量名必须匹配
^[A-Za-z][A-Za-z0-9_]*$,含连字符或中文的键无法注册; - 理解降级行为:未安装/未启用 environment-variables 插件,或变量列表为空时,选择器为空但不影响字面值输入;
- 区分兄弟组件:纯字面量用 Antd
Input,纯$env变量用EnvVariableInput,通用变量用 VariableInput,常量+变量混合用 TypedVariableInput,多行模板文本用 VariableTextArea。
相关链接
- VariableInput — 通用单行变量输入
- TypedVariableInput — 同时支持常量和变量
- 组件源码 与 单元测试
- $env 变量注册实现 与 数据集合定义
- 官方演示 Demo
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考