Files
2026-07-27 14:41:04 +02:00

94 lines
2.0 KiB
Go

package models
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"sync"
)
var (
mu sync.RWMutex
cfg *AppConfig
cfgPath string
)
func init() {
cfgPath = resolveCfgPath()
hostname, _ := os.Hostname()
cfg = &AppConfig{
Node: NodeInfo{
Hostname: hostname,
UpdateHistory: []UpdateRecord{},
Backup: BackupConfig{
Targets: []BackupTarget{},
History: []BackupExecution{},
},
},
Nodes: []RemoteNode{},
Services: []Service{},
}
if data, err := os.ReadFile(cfgPath); err == nil {
_ = json.Unmarshal(data, cfg)
}
ensureSlices()
}
func ensureSlices() {
if cfg.Node.UpdateHistory == nil {
cfg.Node.UpdateHistory = []UpdateRecord{}
}
if cfg.Node.Backup.Targets == nil {
cfg.Node.Backup.Targets = []BackupTarget{}
}
if cfg.Node.Backup.History == nil {
cfg.Node.Backup.History = []BackupExecution{}
}
if cfg.Nodes == nil {
cfg.Nodes = []RemoteNode{}
}
if cfg.Services == nil {
cfg.Services = []Service{}
}
}
func resolveCfgPath() string {
const etcPath = "/etc/nodemaster/nodemaster.conf"
if err := os.MkdirAll(filepath.Dir(etcPath), 0755); err == nil {
if f, err := os.OpenFile(etcPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600); err == nil {
f.Close()
return etcPath
}
}
home, err := os.UserHomeDir()
if err != nil {
home = "."
}
return filepath.Join(home, ".nodemaster.conf")
}
func persist() error {
data, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return err
}
if err := os.MkdirAll(filepath.Dir(cfgPath), 0755); err != nil {
return fmt.Errorf("mkdir: %w", err)
}
// 0600: the config now embeds the restic repository password, so it must
// not be group/world-readable. Chmod covers pre-existing files that were
// created before this field existed (WriteFile's mode only applies on
// create, not to files that already exist with looser permissions).
if err := os.WriteFile(cfgPath, data, 0600); err != nil {
return err
}
_ = os.Chmod(cfgPath, 0600)
return nil
}
// ConfigPath returns the resolved config file path.
func ConfigPath() string {
return cfgPath
}