package main import ( "bytes" "encoding/json" "os" "path/filepath" ) const appDirName = "deepseek-tray" // Config 托盘程序配置,存于 %APPDATA%\deepseek-tray\config.json。 type Config struct { WebPort int `json:"webPort"` // dsh web 监听端口(默认 3080) WebHost string `json:"webHost"` // 绑定地址(默认 127.0.0.1) NodePath string `json:"nodePath"` // node 可执行路径(留空自动探测) DshBinJS string `json:"dshBinJs"` // dsh 入口 JS(留空自动探测) AutoStart bool `json:"autoStart"` // 开机自启(注册表 Run 键) AutoUpdate bool `json:"autoUpdate"` // 定期后台更新 UpdateIntervalH int `json:"updateIntervalHours"` // 定期更新间隔(小时) RestartDelaySec int `json:"restartDelaySeconds"` // 异常退出后重启延迟(秒) } func defaultConfig() *Config { return &Config{ WebPort: 3080, WebHost: "127.0.0.1", AutoStart: false, AutoUpdate: false, UpdateIntervalH: 24, RestartDelaySec: 5, } } // appDataDir 返回配置与日志目录(不存在时创建)。 func appDataDir() string { base := os.Getenv("APPDATA") if base == "" { if home, err := os.UserHomeDir(); err == nil { base = home } else { base = "." } } dir := filepath.Join(base, appDirName) _ = os.MkdirAll(dir, 0o755) return dir } func configPath() string { return filepath.Join(appDataDir(), "config.json") } func loadConfig() *Config { cfg := defaultConfig() data, err := os.ReadFile(configPath()) if err != nil { _ = saveConfig(cfg) return cfg } // 容忍 UTF-8 BOM(Windows 记事本等编辑器写入的配置会带 BOM) data = bytes.TrimPrefix(data, []byte{0xEF, 0xBB, 0xBF}) if err := json.Unmarshal(data, cfg); err != nil { return defaultConfig() } if cfg.WebPort <= 0 { cfg.WebPort = 3080 } if cfg.WebHost == "" { cfg.WebHost = "127.0.0.1" } if cfg.UpdateIntervalH <= 0 { cfg.UpdateIntervalH = 24 } if cfg.RestartDelaySec <= 0 { cfg.RestartDelaySec = 5 } return cfg } func saveConfig(cfg *Config) error { data, err := json.MarshalIndent(cfg, "", " ") if err != nil { return err } return os.WriteFile(configPath(), data, 0o644) }