docs(dobotai): guide container credential input
This commit is contained in:
@@ -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。它不读取或打印环境文件中的值,也不停止旧服务。生产环境必须独立审查、备份和验证,不复用测试镜像以外的未验证构建。
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user