317 lines
6.4 KiB
Go
317 lines
6.4 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log/slog"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"github.com/google/uuid"
|
|
"mesh-drop/internal/security"
|
|
)
|
|
|
|
// WindowState 定义窗口状态
|
|
type WindowState struct {
|
|
Width int `json:"width"`
|
|
Height int `json:"height"`
|
|
}
|
|
|
|
var Version = "next"
|
|
|
|
type Language string
|
|
|
|
const (
|
|
LanguageEnglish Language = "en"
|
|
LanguageChinese Language = "zh-Hans"
|
|
)
|
|
|
|
type configData struct {
|
|
WindowState WindowState `json:"window_state"`
|
|
ID string `json:"id"`
|
|
PrivateKey string `json:"private_key"`
|
|
PublicKey string `json:"public_key"`
|
|
SavePath string `json:"save_path"`
|
|
HostName string `json:"host_name"`
|
|
AutoAccept bool `json:"auto_accept"`
|
|
SaveHistory bool `json:"save_history"`
|
|
TrustedPeer map[string]string `json:"trusted_peer"` // ID -> PublicKey
|
|
|
|
Language Language `json:"language"`
|
|
CloseToSystray bool `json:"close_to_systray"`
|
|
}
|
|
|
|
type Config struct {
|
|
mu sync.RWMutex
|
|
data configData
|
|
configPath string
|
|
}
|
|
|
|
func GetConfigDir() string {
|
|
configPath, err := os.UserConfigDir()
|
|
if err != nil {
|
|
configPath = "/tmp"
|
|
}
|
|
return filepath.Join(configPath, "mesh-drop")
|
|
}
|
|
|
|
func GetUserHomeDir() string {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "/tmp"
|
|
}
|
|
return home
|
|
}
|
|
|
|
// New 读取配置
|
|
func Load(defaultState WindowState) *Config {
|
|
configDir := GetConfigDir()
|
|
_ = os.MkdirAll(configDir, 0o750)
|
|
configFile := filepath.Join(configDir, "config.json")
|
|
|
|
// 设置默认值
|
|
defaultSavePath := filepath.Join(GetUserHomeDir(), "Downloads")
|
|
defaultHostName, err := os.Hostname()
|
|
if err != nil {
|
|
defaultHostName = "localhost"
|
|
}
|
|
|
|
cfgData := configData{
|
|
WindowState: defaultState,
|
|
SavePath: defaultSavePath,
|
|
AutoAccept: false,
|
|
SaveHistory: true,
|
|
Language: LanguageEnglish,
|
|
CloseToSystray: false,
|
|
ID: uuid.New().String(),
|
|
HostName: defaultHostName,
|
|
TrustedPeer: make(map[string]string),
|
|
}
|
|
|
|
fileBytes, err := os.ReadFile(
|
|
configFile,
|
|
)
|
|
if err != nil {
|
|
if !os.IsNotExist(err) {
|
|
slog.Error("Failed to read config file", "error", err)
|
|
} else {
|
|
slog.Info("Config file not found, creating new one")
|
|
}
|
|
} else {
|
|
if err := json.Unmarshal(fileBytes, &cfgData); err != nil {
|
|
slog.Error("Failed to unmarshal config", "error", err)
|
|
}
|
|
}
|
|
|
|
config := Config{
|
|
data: cfgData,
|
|
configPath: configFile,
|
|
}
|
|
|
|
// 确保默认保存路径存在
|
|
err = os.MkdirAll(defaultSavePath, 0o750)
|
|
if err != nil {
|
|
slog.Error("Failed to create default save path", "path", defaultSavePath, "error", err)
|
|
}
|
|
|
|
// 如果没有密钥对,生成新的
|
|
if config.data.PrivateKey == "" || config.data.PublicKey == "" {
|
|
priv, pub, err := security.GenerateKey()
|
|
if err != nil {
|
|
slog.Error("Failed to generate identity keys", "error", err)
|
|
} else {
|
|
config.data.PrivateKey = priv
|
|
config.data.PublicKey = pub
|
|
}
|
|
}
|
|
|
|
// 初始化 TrustedPeer map if nil
|
|
if config.data.TrustedPeer == nil {
|
|
config.data.TrustedPeer = make(map[string]string)
|
|
}
|
|
|
|
// 保存
|
|
if err := config.Save(); err != nil {
|
|
slog.Error("Failed to save config", "error", err)
|
|
}
|
|
|
|
return &config
|
|
}
|
|
|
|
// Save 保存配置到磁盘
|
|
func (c *Config) Save() error {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.save()
|
|
}
|
|
|
|
func (c *Config) save() error {
|
|
dir := filepath.Dir(c.configPath)
|
|
if err := os.MkdirAll(dir, 0o750); err != nil {
|
|
return err
|
|
}
|
|
|
|
jsonData, err := json.MarshalIndent(c.data, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 设置配置文件权限为 0600 (仅所有者读写)
|
|
if c.configPath != "" {
|
|
if err := os.WriteFile(c.configPath, jsonData, 0o600); err != nil {
|
|
slog.Warn("Failed to write config file", "error", err)
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// update 是一个辅助函数,用于在锁保护下更新配置并保存
|
|
func (c *Config) update(fn func()) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
fn()
|
|
|
|
if err := c.save(); err != nil {
|
|
slog.Error("Failed to save config", "error", err)
|
|
}
|
|
}
|
|
|
|
// SetSavePath 修改配置
|
|
func (c *Config) SetSavePath(savePath string) {
|
|
c.update(func() {
|
|
c.data.SavePath = savePath
|
|
_ = os.MkdirAll(savePath, 0o750)
|
|
})
|
|
}
|
|
|
|
func (c *Config) GetSavePath() string {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.data.SavePath
|
|
}
|
|
|
|
func (c *Config) SetHostName(hostName string) {
|
|
c.update(func() {
|
|
c.data.HostName = hostName
|
|
})
|
|
}
|
|
|
|
func (c *Config) GetHostName() string {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.data.HostName
|
|
}
|
|
|
|
func (c *Config) GetID() string {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.data.ID
|
|
}
|
|
|
|
func (c *Config) SetAutoAccept(autoAccept bool) {
|
|
c.update(func() {
|
|
c.data.AutoAccept = autoAccept
|
|
})
|
|
}
|
|
|
|
func (c *Config) GetAutoAccept() bool {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.data.AutoAccept
|
|
}
|
|
|
|
func (c *Config) SetSaveHistory(saveHistory bool) {
|
|
c.update(func() {
|
|
c.data.SaveHistory = saveHistory
|
|
})
|
|
}
|
|
|
|
func (c *Config) GetSaveHistory() bool {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.data.SaveHistory
|
|
}
|
|
|
|
func (c *Config) GetVersion() string {
|
|
return Version
|
|
}
|
|
|
|
func (c *Config) SetWindowState(state WindowState) {
|
|
c.update(func() {
|
|
c.data.WindowState = state
|
|
})
|
|
}
|
|
|
|
func (c *Config) GetWindowState() WindowState {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.data.WindowState
|
|
}
|
|
|
|
func (c *Config) AddTrust(peerID string, publicKey string) {
|
|
c.update(func() {
|
|
if c.data.TrustedPeer == nil {
|
|
c.data.TrustedPeer = make(map[string]string)
|
|
}
|
|
c.data.TrustedPeer[peerID] = publicKey
|
|
})
|
|
}
|
|
|
|
func (c *Config) GetTrusted() map[string]string {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.data.TrustedPeer
|
|
}
|
|
|
|
func (c *Config) RemoveTrust(peerID string) {
|
|
c.update(func() {
|
|
delete(c.data.TrustedPeer, peerID)
|
|
})
|
|
}
|
|
|
|
func (c *Config) IsTrusted(peerID string) bool {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
_, exists := c.data.TrustedPeer[peerID]
|
|
return exists
|
|
}
|
|
|
|
func (c *Config) SetLanguage(language Language) {
|
|
c.update(func() {
|
|
c.data.Language = language
|
|
})
|
|
}
|
|
|
|
func (c *Config) GetLanguage() Language {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.data.Language
|
|
}
|
|
|
|
func (c *Config) SetCloseToSystray(closeToSystray bool) {
|
|
c.update(func() {
|
|
c.data.CloseToSystray = closeToSystray
|
|
})
|
|
}
|
|
|
|
func (c *Config) GetCloseToSystray() bool {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.data.CloseToSystray
|
|
}
|
|
|
|
func (c *Config) GetPrivateKey() string {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.data.PrivateKey
|
|
}
|
|
|
|
func (c *Config) GetPublicKey() string {
|
|
c.mu.RLock()
|
|
defer c.mu.RUnlock()
|
|
return c.data.PublicKey
|
|
}
|