222 lines
5.0 KiB
Go
222 lines
5.0 KiB
Go
package models
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
func GetLocalNode() NodeInfo {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
return cfg.Node
|
|
}
|
|
|
|
func UpdateLocalNode(n NodeInfo) error {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if n.Hostname != "" {
|
|
cfg.Node.Hostname = n.Hostname
|
|
}
|
|
if n.Description != "" {
|
|
cfg.Node.Description = n.Description
|
|
}
|
|
if n.ResticPassword != "" {
|
|
cfg.Node.ResticPassword = n.ResticPassword
|
|
}
|
|
return persist()
|
|
}
|
|
|
|
// GetResticPassword returns the node-wide restic repository password used to
|
|
// encrypt/decrypt every restic backup target on this node.
|
|
func GetResticPassword() string {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
return cfg.Node.ResticPassword
|
|
}
|
|
|
|
func GetUpdateHistory() []UpdateRecord {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
result := make([]UpdateRecord, len(cfg.Node.UpdateHistory))
|
|
copy(result, cfg.Node.UpdateHistory)
|
|
return result
|
|
}
|
|
|
|
func GetUpdateRecord(id string) (UpdateRecord, error) {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
for _, r := range cfg.Node.UpdateHistory {
|
|
if r.ID == id {
|
|
return r, nil
|
|
}
|
|
}
|
|
return UpdateRecord{}, errors.New("update record not found")
|
|
}
|
|
|
|
func AddUpdateRecord(r UpdateRecord) (UpdateRecord, error) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
r.ID = fmt.Sprintf("upd_%d", time.Now().UnixNano())
|
|
r.Timestamp = time.Now().UTC()
|
|
cfg.Node.UpdateHistory = append(cfg.Node.UpdateHistory, r)
|
|
return r, persist()
|
|
}
|
|
|
|
func DeleteUpdateRecord(id string) error {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
for i, r := range cfg.Node.UpdateHistory {
|
|
if r.ID == id {
|
|
cfg.Node.UpdateHistory = append(cfg.Node.UpdateHistory[:i], cfg.Node.UpdateHistory[i+1:]...)
|
|
return persist()
|
|
}
|
|
}
|
|
return errors.New("update record not found")
|
|
}
|
|
|
|
func GetNodeBackupConfig() BackupConfig {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
return cfg.Node.Backup
|
|
}
|
|
|
|
func UpdateNodeBackupConfig(bc BackupConfig) error {
|
|
mu.Lock()
|
|
history := cfg.Node.Backup.History
|
|
cfg.Node.Backup = bc
|
|
cfg.Node.Backup.History = history
|
|
err := persist()
|
|
mu.Unlock()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
SyncSchedule()
|
|
return nil
|
|
}
|
|
|
|
func AppendNodeBackupExecution(ex BackupExecution) error {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
cfg.Node.Backup.History = append(cfg.Node.Backup.History, ex)
|
|
now := time.Now().UTC()
|
|
cfg.Node.Backup.LastRun = &now
|
|
for i, t := range cfg.Node.Backup.Targets {
|
|
if t.ID == ex.TargetID {
|
|
cfg.Node.Backup.Targets[i].LastRun = &now
|
|
break
|
|
}
|
|
}
|
|
return persist()
|
|
}
|
|
|
|
// GetNodeBackupTargets lists the node's individual backup target definitions.
|
|
func GetNodeBackupTargets() []BackupTarget {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
result := make([]BackupTarget, len(cfg.Node.Backup.Targets))
|
|
copy(result, cfg.Node.Backup.Targets)
|
|
return result
|
|
}
|
|
|
|
// GetNodeBackupTarget returns a single node backup target by ID.
|
|
func GetNodeBackupTarget(tid string) (BackupTarget, error) {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
for _, t := range cfg.Node.Backup.Targets {
|
|
if t.ID == tid {
|
|
return t, nil
|
|
}
|
|
}
|
|
return BackupTarget{}, errors.New("backup target not found")
|
|
}
|
|
|
|
// AddNodeBackupTarget creates a new backup target definition for the node.
|
|
func AddNodeBackupTarget(t BackupTarget) (BackupTarget, error) {
|
|
if err := validateBackupTarget(t); err != nil {
|
|
return BackupTarget{}, err
|
|
}
|
|
mu.Lock()
|
|
t.ID = fmt.Sprintf("bt_%d", time.Now().UnixNano())
|
|
cfg.Node.Backup.Targets = append(cfg.Node.Backup.Targets, t)
|
|
err := persist()
|
|
mu.Unlock()
|
|
if err != nil {
|
|
return BackupTarget{}, err
|
|
}
|
|
SyncSchedule()
|
|
return t, nil
|
|
}
|
|
|
|
// UpdateNodeBackupTarget replaces an existing node backup target definition.
|
|
func UpdateNodeBackupTarget(tid string, t BackupTarget) (BackupTarget, error) {
|
|
if err := validateBackupTarget(t); err != nil {
|
|
return BackupTarget{}, err
|
|
}
|
|
mu.Lock()
|
|
found := false
|
|
for i, existing := range cfg.Node.Backup.Targets {
|
|
if existing.ID == tid {
|
|
t.ID = tid
|
|
t.LastRun = existing.LastRun
|
|
cfg.Node.Backup.Targets[i] = t
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
mu.Unlock()
|
|
return BackupTarget{}, errors.New("backup target not found")
|
|
}
|
|
err := persist()
|
|
mu.Unlock()
|
|
if err != nil {
|
|
return BackupTarget{}, err
|
|
}
|
|
SyncSchedule()
|
|
return t, nil
|
|
}
|
|
|
|
// DeleteNodeBackupTarget removes a node backup target definition.
|
|
func DeleteNodeBackupTarget(tid string) error {
|
|
mu.Lock()
|
|
found := false
|
|
for i, t := range cfg.Node.Backup.Targets {
|
|
if t.ID == tid {
|
|
cfg.Node.Backup.Targets = append(cfg.Node.Backup.Targets[:i], cfg.Node.Backup.Targets[i+1:]...)
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
mu.Unlock()
|
|
return errors.New("backup target not found")
|
|
}
|
|
err := persist()
|
|
mu.Unlock()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
SyncSchedule()
|
|
return nil
|
|
}
|
|
|
|
// RunNodeBackup runs the node's backup for the given target IDs (nil/empty
|
|
// means all targets), appending an execution record per target.
|
|
func RunNodeBackup(targetIDs []string) error {
|
|
bc := GetNodeBackupConfig()
|
|
targets := filterTargets(bc.Targets, targetIDs)
|
|
if len(targets) == 0 {
|
|
return errors.New("no matching backup targets")
|
|
}
|
|
sourcePath := bc.SourcePath
|
|
if sourcePath == "" {
|
|
sourcePath = ConfigPath()
|
|
}
|
|
results := RunBackup("node", targets, sourcePath)
|
|
for _, ex := range results {
|
|
_ = AppendNodeBackupExecution(ex)
|
|
}
|
|
return nil
|
|
}
|