package aitools import ( "encoding/json" "fmt" "os" "path/filepath" "strings" "opsxdb/server/internal/db" ) // SavedConnection 与桌面 store SavedConnection JSON 对齐(磁盘形态,密码可为 ox1. 混淆)。 // Logan.Li type SavedConnection struct { ID string `json:"id"` Name string `json:"name"` Driver string `json:"driver"` Host string `json:"host"` Port int `json:"port"` User string `json:"user"` Password string `json:"password"` Database string `json:"database"` Service string `json:"service,omitempty"` SSL bool `json:"ssl,omitempty"` SSLMode string `json:"sslMode,omitempty"` SSLCA string `json:"sslCA,omitempty"` SSLCert string `json:"sslCert,omitempty"` SSLKey string `json:"sslKey,omitempty"` RedisMode string `json:"redisMode,omitempty"` RedisNodes string `json:"redisNodes,omitempty"` MasterName string `json:"masterName,omitempty"` SentinelPassword string `json:"sentinelPassword,omitempty"` SSHEnabled bool `json:"sshEnabled,omitempty"` SSHHost string `json:"sshHost,omitempty"` SSHPort int `json:"sshPort,omitempty"` SSHUser string `json:"sshUser,omitempty"` SSHAuth string `json:"sshAuth,omitempty"` SSHPassword string `json:"sshPassword,omitempty"` SSHPrivateKey string `json:"sshPrivateKey,omitempty"` SSHPassphrase string `json:"sshPassphrase,omitempty"` SavePassword *bool `json:"savePassword,omitempty"` } // ConnectionSummary 列表对外字段,永不含密码。 type ConnectionSummary struct { ID string `json:"id"` Name string `json:"name"` Driver string `json:"driver"` Host string `json:"host"` Port int `json:"port"` Database string `json:"database"` } func ConnectionsPath() (string, error) { dir, err := opsxdbDir() if err != nil { return "", err } return filepath.Join(dir, "connections.json"), nil } // LoadConnections 读 ~/.opsxdb/connections.json。 func LoadConnections() ([]SavedConnection, error) { path, err := ConnectionsPath() if err != nil { return nil, err } b, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return nil, nil } return nil, err } var list []SavedConnection if err := json.Unmarshal(b, &list); err != nil { return nil, fmt.Errorf("connections.json: %w", err) } return list, nil } // SaveConnections 写 ~/.opsxdb/connections.json(0600);供桌面双写 API 调用。 func SaveConnections(list []SavedConnection) error { path, err := ConnectionsPath() if err != nil { return err } if err := os.MkdirAll(filepath.Dir(path), 0700); err != nil { return err } b, err := json.MarshalIndent(list, "", " ") if err != nil { return err } f, err := os.CreateTemp(filepath.Dir(path), ".connections-*.json") if err != nil { return err } defer os.Remove(f.Name()) if _, err := f.Write(b); err != nil { f.Close() return err } if err := f.Close(); err != nil { return err } return os.Rename(f.Name(), path) } func FindConnection(id string) (*SavedConnection, error) { list, err := LoadConnections() if err != nil { return nil, err } for i := range list { if list[i].ID == id { c := list[i] return &c, nil } } return nil, fmt.Errorf("connection not found: %s", id) } // ToConnReq 映射为 db.ConnReq(解码 ox1. 密码)。 func (c SavedConnection) ToConnReq() db.ConnReq { sslOn := c.SSL || (c.SSLMode != "" && c.SSLMode != "disable") mode := c.SSLMode if sslOn { if mode == "" || mode == "disable" { mode = "require" } } else { mode = "disable" } return db.ConnReq{ Driver: c.Driver, Host: c.Host, Port: c.Port, User: c.User, Password: DecodeSecret(c.Password), Database: c.Database, Service: c.Service, SSL: sslOn, SSLMode: mode, SSLCA: c.SSLCA, SSLCert: c.SSLCert, SSLKey: c.SSLKey, RedisMode: orDefault(c.RedisMode, "standalone"), RedisNodes: c.RedisNodes, MasterName: c.MasterName, SentinelPassword: DecodeSecret(c.SentinelPassword), SSHEnabled: c.SSHEnabled, SSHHost: c.SSHHost, SSHPort: c.SSHPort, SSHUser: c.SSHUser, SSHAuth: c.SSHAuth, SSHPassword: DecodeSecret(c.SSHPassword), SSHPrivateKey: c.SSHPrivateKey, SSHPassphrase: DecodeSecret(c.SSHPassphrase), } } func (c SavedConnection) Summary() ConnectionSummary { return ConnectionSummary{ ID: c.ID, Name: c.Name, Driver: c.Driver, Host: c.Host, Port: c.Port, Database: c.Database, } } func orDefault(s, def string) string { if strings.TrimSpace(s) == "" { return def } return s }