Files
nodemaster/models/services.go
T

317 lines
7.0 KiB
Go
Raw Normal View History

2026-07-27 14:41:04 +02:00
package models
import (
"errors"
"fmt"
"time"
)
func GetAllServices() []Service {
mu.RLock()
defer mu.RUnlock()
result := make([]Service, len(cfg.Services))
copy(result, cfg.Services)
return result
}
func GetService(id string) (Service, error) {
mu.RLock()
defer mu.RUnlock()
for _, s := range cfg.Services {
if s.ID == id {
return s, nil
}
}
return Service{}, errors.New("service not found")
}
func AddService(s Service) (Service, error) {
mu.Lock()
s.ID = fmt.Sprintf("svc_%d", time.Now().UnixNano())
if s.Backup != nil {
if s.Backup.Targets == nil {
s.Backup.Targets = []BackupTarget{}
}
if s.Backup.History == nil {
s.Backup.History = []BackupExecution{}
}
}
cfg.Services = append(cfg.Services, s)
err := persist()
mu.Unlock()
if err != nil {
return Service{}, err
}
SyncSchedule()
return s, nil
}
func UpdateService(id string, s Service) (Service, error) {
mu.Lock()
found := false
for i, svc := range cfg.Services {
if svc.ID == id {
s.ID = id
if s.Backup != nil && svc.Backup != nil {
s.Backup.History = svc.Backup.History
}
cfg.Services[i] = s
found = true
break
}
}
if !found {
mu.Unlock()
return Service{}, errors.New("service not found")
}
err := persist()
mu.Unlock()
if err != nil {
return Service{}, err
}
SyncSchedule()
return s, nil
}
func DeleteService(id string) error {
mu.Lock()
found := false
for i, s := range cfg.Services {
if s.ID == id {
cfg.Services = append(cfg.Services[:i], cfg.Services[i+1:]...)
found = true
break
}
}
if !found {
mu.Unlock()
return errors.New("service not found")
}
err := persist()
mu.Unlock()
if err != nil {
return err
}
SyncSchedule()
return nil
}
func UpdateServiceStatus(id, status string) error {
mu.Lock()
defer mu.Unlock()
for i, s := range cfg.Services {
if s.ID == id {
cfg.Services[i].Status = status
return persist()
}
}
return errors.New("service not found")
}
func AppendServiceBackupExecution(id string, ex BackupExecution) error {
mu.Lock()
defer mu.Unlock()
for i, s := range cfg.Services {
if s.ID == id {
if cfg.Services[i].Backup == nil {
cfg.Services[i].Backup = &BackupConfig{
Targets: []BackupTarget{},
History: []BackupExecution{},
}
}
cfg.Services[i].Backup.History = append(cfg.Services[i].Backup.History, ex)
now := time.Now().UTC()
cfg.Services[i].Backup.LastRun = &now
for j, t := range cfg.Services[i].Backup.Targets {
if t.ID == ex.TargetID {
cfg.Services[i].Backup.Targets[j].LastRun = &now
break
}
}
return persist()
}
}
return errors.New("service not found")
}
// GetServiceBackupTargets lists a service's individual backup target definitions.
func GetServiceBackupTargets(id string) ([]BackupTarget, error) {
mu.RLock()
defer mu.RUnlock()
for _, s := range cfg.Services {
if s.ID == id {
if s.Backup == nil {
return []BackupTarget{}, nil
}
result := make([]BackupTarget, len(s.Backup.Targets))
copy(result, s.Backup.Targets)
return result, nil
}
}
return nil, errors.New("service not found")
}
// GetServiceBackupTarget returns a single backup target by ID for a service.
func GetServiceBackupTarget(id, tid string) (BackupTarget, error) {
mu.RLock()
defer mu.RUnlock()
for _, s := range cfg.Services {
if s.ID == id {
if s.Backup == nil {
return BackupTarget{}, errors.New("backup target not found")
}
for _, t := range s.Backup.Targets {
if t.ID == tid {
return t, nil
}
}
return BackupTarget{}, errors.New("backup target not found")
}
}
return BackupTarget{}, errors.New("service not found")
}
// AddServiceBackupTarget creates a new backup target definition for a service.
func AddServiceBackupTarget(id string, t BackupTarget) (BackupTarget, error) {
if err := validateBackupTarget(t); err != nil {
return BackupTarget{}, err
}
mu.Lock()
idx := -1
for i, s := range cfg.Services {
if s.ID == id {
idx = i
break
}
}
if idx == -1 {
mu.Unlock()
return BackupTarget{}, errors.New("service not found")
}
if cfg.Services[idx].Backup == nil {
cfg.Services[idx].Backup = &BackupConfig{
Targets: []BackupTarget{},
History: []BackupExecution{},
}
}
t.ID = fmt.Sprintf("bt_%d", time.Now().UnixNano())
cfg.Services[idx].Backup.Targets = append(cfg.Services[idx].Backup.Targets, t)
err := persist()
mu.Unlock()
if err != nil {
return BackupTarget{}, err
}
SyncSchedule()
return t, nil
}
// UpdateServiceBackupTarget replaces an existing backup target definition for a service.
func UpdateServiceBackupTarget(id, tid string, t BackupTarget) (BackupTarget, error) {
if err := validateBackupTarget(t); err != nil {
return BackupTarget{}, err
}
mu.Lock()
svcIdx := -1
for i, s := range cfg.Services {
if s.ID == id {
svcIdx = i
break
}
}
if svcIdx == -1 || cfg.Services[svcIdx].Backup == nil {
mu.Unlock()
return BackupTarget{}, errors.New("service not found")
}
found := false
for i, existing := range cfg.Services[svcIdx].Backup.Targets {
if existing.ID == tid {
t.ID = tid
t.LastRun = existing.LastRun
cfg.Services[svcIdx].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
}
// DeleteServiceBackupTarget removes a backup target definition from a service.
func DeleteServiceBackupTarget(id, tid string) error {
mu.Lock()
svcIdx := -1
for i, s := range cfg.Services {
if s.ID == id {
svcIdx = i
break
}
}
if svcIdx == -1 || cfg.Services[svcIdx].Backup == nil {
mu.Unlock()
return errors.New("service not found")
}
found := false
for i, t := range cfg.Services[svcIdx].Backup.Targets {
if t.ID == tid {
cfg.Services[svcIdx].Backup.Targets = append(
cfg.Services[svcIdx].Backup.Targets[:i],
cfg.Services[svcIdx].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
}
// RunServiceBackup runs a service's backup for the given target IDs (nil/empty
// means all targets), honoring StopOnBackup, and appends an execution record
// per target.
func RunServiceBackup(id string, targetIDs []string) error {
svc, err := GetService(id)
if err != nil {
return err
}
if svc.Backup == nil || len(svc.Backup.Targets) == 0 {
return errors.New("no backup targets configured for this service")
}
targets := filterTargets(svc.Backup.Targets, targetIDs)
if len(targets) == 0 {
return errors.New("no matching backup targets")
}
sourcePath := svc.BackupFolder
if svc.Backup.SourcePath != "" {
sourcePath = svc.Backup.SourcePath
}
if svc.StopOnBackup {
_ = StopService(svc)
}
results := RunBackup("svc:"+id, targets, sourcePath)
for _, ex := range results {
_ = AppendServiceBackupExecution(id, ex)
}
if svc.StopOnBackup {
_ = StartService(svc)
}
return nil
}