- 补入库:server/internal/db/dump.go、redis 分页/扫描预算测试、opsxdb-mcp main_test、 packages/opsxdb-mcp package-lock、docs 审计与对比文档等 37 个新文件 - 含结构扁平化进行中的 80 处修改与 155 处旧结构删除(server/ src/ src-tauri/ 为当前有效源) - 构建产物(bin/target/dist)与签名私钥仍按规则排除
180 lines
4.8 KiB
Go
180 lines
4.8 KiB
Go
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
|
||
}
|