U10a 尾片:config check 子命令(加载校验+脱敏摘要,零副作用) #11
@@ -19,6 +19,22 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// KTD16:同一程序提供运维子命令;默认(无子命令)保持 serve 行为不变。
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
switch os.Args[1] {
|
||||||
|
case "config":
|
||||||
|
if len(os.Args) > 2 && os.Args[2] == "check" {
|
||||||
|
configCheck(os.Args[3:])
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Fprintln(os.Stderr, "用法: server config check [--env path] [--port N] [--host H] [--backend name]")
|
||||||
|
os.Exit(2)
|
||||||
|
case "-h", "--help", "help":
|
||||||
|
fmt.Println("用法: server [serve flags] 启动服务(默认)\n server config check [flags] 加载并校验配置(脱敏摘要,无副作用)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
envPath := flag.String("env", ".env", "环境配置文件路径")
|
envPath := flag.String("env", ".env", "环境配置文件路径")
|
||||||
port := flag.Int("port", 0, "HTTP 服务端口 (默认读取配置文件或 8080)")
|
port := flag.Int("port", 0, "HTTP 服务端口 (默认读取配置文件或 8080)")
|
||||||
host := flag.String("host", "", "HTTP 监听主机 (默认读取配置文件或 127.0.0.1)")
|
host := flag.String("host", "", "HTTP 监听主机 (默认读取配置文件或 127.0.0.1)")
|
||||||
@@ -157,3 +173,40 @@ func shutdownDrainTimeout(cfg *config.Config) time.Duration {
|
|||||||
}
|
}
|
||||||
return at + 30*time.Second
|
return at + 30*time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// configCheck 加载并校验配置:输出脱敏摘要与问题清单。
|
||||||
|
// 不创建 DB、不启动服务、不写业务目录(KTD17 config check 约束)。
|
||||||
|
func configCheck(args []string) {
|
||||||
|
fs := flag.NewFlagSet("config check", flag.ExitOnError)
|
||||||
|
envPath := fs.String("env", ".env", "环境配置文件路径")
|
||||||
|
port := fs.Int("port", 0, "HTTP 端口覆盖")
|
||||||
|
host := fs.String("host", "", "HTTP 主机覆盖")
|
||||||
|
backend := fs.String("backend", "", "智能体后端覆盖")
|
||||||
|
_ = fs.Parse(args)
|
||||||
|
|
||||||
|
cfg, err := config.Load(*envPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "config check: 加载失败: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if *port > 0 {
|
||||||
|
cfg.HTTPPort = *port
|
||||||
|
}
|
||||||
|
if *host != "" {
|
||||||
|
cfg.HTTPHost = *host
|
||||||
|
}
|
||||||
|
if *backend != "" {
|
||||||
|
cfg.AgentBackend = *backend
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(cfg.MaskedSummary())
|
||||||
|
if issues := cfg.Check(); len(issues) > 0 {
|
||||||
|
fmt.Fprintln(os.Stderr, "config check: 未通过")
|
||||||
|
for _, it := range issues {
|
||||||
|
fmt.Fprintf(os.Stderr, " - %s\n", it)
|
||||||
|
}
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
fmt.Println("config check: 通过")
|
||||||
|
}
|
||||||
|
|||||||
@@ -737,3 +737,41 @@ func RedactPostgresDSN(dsn string) string {
|
|||||||
func (c *Config) ContextDSN() string {
|
func (c *Config) ContextDSN() string {
|
||||||
return "postgres://" + c.ContextDbUser + ":" + c.ContextDbPassword + "@" + c.ContextDbHost + ":" + strconv.Itoa(c.ContextDbPort) + "/" + c.ContextDbName + "?sslmode=disable"
|
return "postgres://" + c.ContextDbUser + ":" + c.ContextDbPassword + "@" + c.ContextDbHost + ":" + strconv.Itoa(c.ContextDbPort) + "/" + c.ContextDbName + "?sslmode=disable"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Check 返回配置问题列表(空 = 通过)。只读、无副作用,供 config check
|
||||||
|
// 子命令与部署预检使用;完整分层溯源(explain)随 U10b ConfigSnapshot 提供。
|
||||||
|
func (c *Config) Check() []string {
|
||||||
|
var issues []string
|
||||||
|
if c == nil {
|
||||||
|
return []string{"config is nil"}
|
||||||
|
}
|
||||||
|
if err := validateProductionSecrets(c); err != nil {
|
||||||
|
issues = append(issues, err.Error())
|
||||||
|
}
|
||||||
|
if c.HTTPPort <= 0 || c.HTTPPort > 65535 {
|
||||||
|
issues = append(issues, fmt.Sprintf("http port out of range: %d", c.HTTPPort))
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(c.DataDir) == "" {
|
||||||
|
issues = append(issues, "data dir is empty")
|
||||||
|
}
|
||||||
|
return issues
|
||||||
|
}
|
||||||
|
|
||||||
|
// MaskedSummary 输出脱敏的关键配置摘要(不含任何凭证值)。
|
||||||
|
func (c *Config) MaskedSummary() string {
|
||||||
|
mask := func(s string) string {
|
||||||
|
if strings.TrimSpace(s) == "" {
|
||||||
|
return "<unset>"
|
||||||
|
}
|
||||||
|
return "<set>"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("env=%s data_dir=%s listen=%s:%d backend=%s timeout=%s\n"+
|
||||||
|
"app_db=%s:%d/%s user=%s password=%s\n"+
|
||||||
|
"kb_db=%s:%d/%s user=%s password=%s\n"+
|
||||||
|
"runner_url=%s runner_token=%s agentx_key=%s allow_in_memory=%v",
|
||||||
|
c.Env, c.DataDir, c.HTTPHost, c.HTTPPort, c.AgentBackend, c.AgentTimeout,
|
||||||
|
c.ContextDbHost, c.ContextDbPort, c.ContextDbName, c.ContextDbUser, mask(c.ContextDbPassword),
|
||||||
|
c.KbDbHost, c.KbDbPort, c.KbDbName, c.KbDbUser, mask(c.KbDbPassword),
|
||||||
|
c.PiAgentRunnerURL, mask(c.PiAgentRunnerAuthToken), mask(c.AgentXAPIKey), c.AllowInMemory)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user