docs(dobotai): guide container credential input

This commit is contained in:
姚章浩
2026-09-10 18:31:55 +08:00
parent 9abbb2cb2b
commit 563329320b
3 changed files with 55 additions and 12 deletions
+15
View File
@@ -21,6 +21,21 @@ sudo dobotai-dingtalk-bind-admin
绑定成功后终端自动退出。该命令不会启动第二个钉钉 Stream 消费者。
## 配置项说明与示例
`sudo dobotai-dingtalk-configure` 会在每个输入项前显示相同的说明和示例。现有值永不显示;提示“已配置”时直接回车即可保留原值。秘密项以隐藏输入读取,以下示例均为脱敏占位符。
| 环境变量 | 用途 | 格式示例 |
| --- | --- | --- |
| `DATABASE_URL` | DobotAI独立PostgreSQL连接串,不得填写WeKnora数据库 | `postgres://USER:<数据库密码>@127.0.0.1:55432/dobotai_test?sslmode=disable` |
| `WEKNORA_BASE_URL` | 测试WeKnora地址 | `http://127.0.0.1:8080` |
| `WEKNORA_API_KEY` | DobotAI使用的WeKnora API密钥 | `<你的WeKnora API Key>` |
| `DOBOTAI_INGRESS_TOKEN` | `/v1/qa`调用令牌 | `<随机长字符串>` |
| `DOBOTAI_TENANT_KEY` | 与既有DobotAI数据一致的租户键 | `dobotai-test` |
| `DOBOTAI_AGENT_BINDINGS` | 钉钉会话到WeKnora Agent的映射 | `{"dingtalk:*":"你的Agent ID"}` |
| `DINGTALK_CLIENT_ID` | 钉钉应用Client ID/AppKey | `dingxxxxxxxx` |
| `DINGTALK_CLIENT_SECRET` | 钉钉应用Client Secret/AppSecret | `<你的钉钉 AppSecret>` |
## 安装前置条件
安装脚本只接受已构建、已审查的本地镜像,并在切换前备份原生二进制与Systemd Unit。它不读取或打印环境文件中的值,也不停止旧服务。生产环境必须独立审查、备份和验证,不复用测试镜像以外的未验证构建。
+13 -12
View File
@@ -6,14 +6,14 @@ from pathlib import Path
import sys
FIELDS = (
("DATABASE_URL", "DobotAI PostgreSQL 连接串", True),
("WEKNORA_BASE_URL", "WeKnora 地址", False),
("WEKNORA_API_KEY", "WeKnora API Key", True),
("DOBOTAI_INGRESS_TOKEN", "DobotAI Ingress Token", True),
("DOBOTAI_TENANT_KEY", "DobotAI 租户键", False),
("DOBOTAI_AGENT_BINDINGS", "DobotAI Agent Bindings JSON", False),
("DINGTALK_CLIENT_ID", "钉钉 Client ID", False),
("DINGTALK_CLIENT_SECRET", "钉钉 Client Secret", True),
("DATABASE_URL", "DobotAI PostgreSQL 连接串", "DobotAI独立数据库,不得填写WeKnora数据库。", "postgres://USER:<数据库密码>@127.0.0.1:55432/dobotai_test?sslmode=disable", True),
("WEKNORA_BASE_URL", "WeKnora 地址", "容器通过宿主网络访问测试WeKnora。", "http://127.0.0.1:8080", False),
("WEKNORA_API_KEY", "WeKnora API Key", "在WeKnora中为DobotAI创建的API密钥。", "<你的WeKnora API Key>", True),
("DOBOTAI_INGRESS_TOKEN", "DobotAI Ingress Token", "调用 /v1/qa 时使用的独立长随机令牌。", "<随机长字符串>", True),
("DOBOTAI_TENANT_KEY", "DobotAI 租户键", "必须与现有DobotAI数据和Agent绑定使用同一个租户键。", "dobotai-test", False),
("DOBOTAI_AGENT_BINDINGS", "DobotAI Agent Bindings JSON", "钉钉会话匹配到WeKnora Agent的映射。", '{"dingtalk:*":"你的Agent ID"}', False),
("DINGTALK_CLIENT_ID", "钉钉 Client ID", "钉钉应用的Client ID/AppKey。", "dingxxxxxxxx", False),
("DINGTALK_CLIENT_SECRET", "钉钉 Client Secret", "钉钉应用的Client Secret/AppSecret。", "<你的钉钉 AppSecret>", True),
)
@@ -27,8 +27,9 @@ def parse_env(path: Path):
return lines, values
def prompt(label: str, secret: bool, configured: bool) -> str:
def prompt(label: str, description: str, example: str, secret: bool, configured: bool) -> str:
suffix = "(已配置,直接回车保留)" if configured else "(必填)"
print(f"\n{label}\n用途:{description}\n格式示例:{example}")
if secret:
return getpass.getpass(f"{label}{suffix}: ").strip()
return input(f"{label}{suffix}: ").strip()
@@ -64,11 +65,11 @@ def main():
if not env_file.is_file():
raise SystemExit("DobotAI 环境文件不存在,请先完成部署。")
lines, values = parse_env(env_file)
for key, label, secret in FIELDS:
entered = prompt(label, secret, bool(values.get(key, "").strip()))
for key, label, description, example, secret in FIELDS:
entered = prompt(label, description, example, secret, bool(values.get(key, "").strip()))
if entered:
values[key] = entered
missing = [label for key, label, _ in FIELDS if not values.get(key, "").strip()]
missing = [label for key, label, _, _, _ in FIELDS if not values.get(key, "").strip()]
if missing:
raise SystemExit("以下必填项未配置:" + "、".join(missing))
write_env(env_file, lines, values)
+27
View File
@@ -69,3 +69,30 @@ func TestDobotAIDingTalkCredentialConfigurationNeverPassesSecretsOnCommandLine(t
}
}
}
func TestDobotAIDingTalkCredentialConfigurationExplainsEveryInputWithRedactedExamples(t *testing.T) {
configurator, err := os.ReadFile("../deploy/dobotai-dingtalk/configure_env.py")
if err != nil {
t.Fatal(err)
}
documentation, err := os.ReadFile("../deploy/dobotai-dingtalk/README.md")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(configurator), "格式示例:{example}") || !strings.Contains(string(documentation), "格式示例") {
t.Fatal("configuration guidance must label every example")
}
for _, example := range []string{
"postgres://USER:<数据库密码>@127.0.0.1:55432/dobotai_test?sslmode=disable",
"http://127.0.0.1:8080",
`{"dingtalk:*":"你的Agent ID"}`,
"DINGTALK_CLIENT_SECRET",
} {
if !strings.Contains(string(configurator), example) {
t.Errorf("configurator missing redacted example %q", example)
}
if !strings.Contains(string(documentation), example) {
t.Errorf("documentation missing redacted example %q", example)
}
}
}