From 05054be7957d4c61407af69cb7e2e6be2c29111f Mon Sep 17 00:00:00 2001 From: shoopea Date: Sun, 19 Oct 2025 21:40:30 +0200 Subject: [PATCH] add custom snapshot --- admin.go | 8 +++++ api.go | 66 ++++++++++++++++++++++++++++++++++++- app.go | 95 +++++++++++++++++++++++++++++++++++------------------- config.go | 2 +- version.go | 8 ++--- 5 files changed, 140 insertions(+), 39 deletions(-) diff --git a/admin.go b/admin.go index 848690f..8a028f1 100644 --- a/admin.go +++ b/admin.go @@ -48,6 +48,14 @@ func (a *AdminConfig) Run() { r.GET("/run", ApiRun) r.GET("/run/:app", ApiRunApp) + r.GET("/snapshot/add/:app/:schedule", ApiSnapshotAdd) + r.GET("/snapshot/del/:app/:name", ApiSnapshotDel) + r.GET("/snapshot/list/:app", ApiSnapshotList) + + r.GET("/schedule/list", ApiScheduleList) + r.GET("/schedule/add/:name/:duration", ApiScheduleAdd) + r.GET("/schedule/del/:name", ApiScheduleDel) + r.GET("/save", ApiSave) r.GET("/config", ApiConfig) diff --git a/api.go b/api.go index e93516e..e70c743 100644 --- a/api.go +++ b/api.go @@ -2,6 +2,7 @@ package main import ( "net/http" + "time" "github.com/gin-gonic/gin" ) @@ -13,9 +14,72 @@ func ApiRun(c *gin.Context) { }) } +func ApiSnapshotAdd(c *gin.Context) { + if app, ok := cfg.apps[c.Param("app")]; ok { + schedule := c.Param("schedule") + if _, ok := app.schedule[schedule]; ok { + if err := app.RunStandaloneSchedule(schedule); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": "error", + "error": err, + }) + } else { + c.JSON(http.StatusOK, gin.H{ + "message": "done", + }) + } + } else { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": "error", + "error": "no schedule found", + }) + } + } else { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": "error", + "error": "no app found", + }) + } +} + +func ApiSnapshotDel(c *gin.Context) { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": "error", + "error": "not implemented", + }) +} + +func ApiSnapshotList(c *gin.Context) { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": "error", + "error": "not implemented", + }) +} + +func ApiScheduleAdd(c *gin.Context) { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": "error", + "error": "not implemented", + }) +} + +func ApiScheduleDel(c *gin.Context) { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": "error", + "error": "not implemented", + }) +} + +func ApiScheduleList(c *gin.Context) { + c.JSON(http.StatusInternalServerError, gin.H{ + "message": "error", + "error": "not implemented", + }) +} + func ApiRunApp(c *gin.Context) { if app, ok := cfg.apps[c.Param("app")]; ok { - if err := app.RunFull(); err != nil { + if err := app.RunStandaloneTime(time.Now()); err != nil { c.JSON(http.StatusInternalServerError, gin.H{ "message": "error", "error": err, diff --git a/app.go b/app.go index a623f03..1b81616 100644 --- a/app.go +++ b/app.go @@ -260,6 +260,11 @@ func (a *App) RunSchedule(schedule string, now time.Time) error { log.WithFields(log.Fields{"app": a.name, "schedule": schedule, "now": now}).Debugf("starting") defer log.WithFields(log.Fields{"app": a.name, "schedule": schedule, "now": now}).Debugf("done") + if err := a.SanityCheck(); err != nil { + log.WithFields(log.Fields{"app": a.name, "now": now, "call": "SanityCheck", "error": err}).Errorf("") + return err + } + snapshotName := SnapshotName(schedule, now) log.WithFields(log.Fields{"app": a.name, "schedule": schedule, "now": now, "snapshot": snapshotName}).Debugf("snapshot name") @@ -279,19 +284,31 @@ func (a *App) RunSchedule(schedule string, now time.Time) error { log.WithFields(log.Fields{"app": a.name, "schedule": schedule, "now": now, "call": "RunAfter", "attr": schedule, "error": err}).Errorf("") } + for _, src := range a.sources { + if err := src.SetManaged(true); err != nil { + log.WithFields(log.Fields{"app": a.name, "call": "src.SetManaged", "error": err}).Errorf("") + return err + } + } + + if err := a.Transfer(); err != nil { + log.WithFields(log.Fields{"app": a.name, "call": "Transfer", "error": err}).Errorf("") + return err + } + + if err := a.Cleanup(now); err != nil { + log.WithFields(log.Fields{"app": a.name, "call": "Cleanup", "error": err}).Errorf("") + return err + } + return nil } -func (a *App) Run(now time.Time) (string, error) { +func (a *App) RunTime(now time.Time) (string, error) { log.WithFields(log.Fields{"app": a.name, "now": now}).Debugf("starting") defer log.WithFields(log.Fields{"app": a.name, "now": now}).Debugf("done") - if err := a.SanityCheck(); err != nil { - log.WithFields(log.Fields{"app": a.name, "now": now, "call": "SanityCheck", "error": err}).Errorf("") - return "", err - } - schedule, err := a.NextSchedule(now) if err != nil { log.WithFields(log.Fields{"app": a.name, "call": "NextSchedule", "error": err}).Errorf("") @@ -299,31 +316,7 @@ func (a *App) Run(now time.Time) (string, error) { } log.WithFields(log.Fields{"app": a.name, "now": now, "schedule": schedule}).Debugf("schedule") - if schedule != "" { - if err := a.RunSchedule(schedule, now); err != nil { - log.WithFields(log.Fields{"app": a.name, "call": "NextSchedule", "error": err}).Errorf("") - return "", err - } - } - - for _, src := range a.sources { - if err := src.SetManaged(true); err != nil { - log.WithFields(log.Fields{"app": a.name, "call": "src.SetManaged", "error": err}).Errorf("") - return "", err - } - } - - if err := a.Transfer(); err != nil { - log.WithFields(log.Fields{"app": a.name, "call": "Transfer", "error": err}).Errorf("") - return "", err - } - - if err := a.Cleanup(now); err != nil { - log.WithFields(log.Fields{"app": a.name, "call": "Cleanup", "error": err}).Errorf("") - return "", err - } - - return schedule, nil + return schedule, a.RunSchedule(schedule, now) } func (a *App) NextSchedule(now time.Time) (string, error) { @@ -541,7 +534,7 @@ func (a *App) Boxes() []*Box { return bx } -func (a *App) RunFull() error { +func (a *App) RunStandaloneTime(now time.Time) error { log.WithFields(log.Fields{"app": a.name}).Debugf("starting") defer log.WithFields(log.Fields{"app": a.name}).Debugf("done") @@ -574,7 +567,7 @@ func (a *App) RunFull() error { wg.Wait() - if sched, err := a.Run(time.Now()); err != nil { + if sched, err := a.RunTime(now); err != nil { log.WithFields(log.Fields{"call": "Run", "error": err}).Errorf("") return err } else { @@ -585,3 +578,39 @@ func (a *App) RunFull() error { return nil } + +func (a *App) RunStandaloneSchedule(name string) error { + log.WithFields(log.Fields{"app": a.name, "name": name}).Debugf("starting") + defer log.WithFields(log.Fields{"app": a.name, "name": name}).Debugf("done") + + if cfgRun { + return fmt.Errorf("backup already running") + } + + CfgLock() + defer CfgUnlock() + + cfgRun = true + defer func() { cfgRun = false }() + + boxes := a.Boxes() + + var wg sync.WaitGroup + + // Open boxes + for _, b := range boxes { + wg.Add(1) + go func(box *Box) { + defer wg.Done() + if err := box.Open(); err != nil { + log.WithFields(log.Fields{"name": box.name, "call": "Open", "error": err}).Errorf("") + return + } + }(b) + defer b.Close() + } + + wg.Wait() + + return a.RunSchedule(name, time.Now()) +} diff --git a/config.go b/config.go index f9656b5..c1d0c99 100644 --- a/config.go +++ b/config.go @@ -298,7 +298,7 @@ func (c *Config) Run() { for _, a := range cfg.apps { wg.Add(1) go func(app *App) { - if sched, err := app.Run(e.startTime); err != nil { + if sched, err := app.RunTime(e.startTime); err != nil { e.AddItem(fmt.Sprintf(" - App : Error running %s (%s)", app.name, err)) } else if *debug { if sched != "" { diff --git a/version.go b/version.go index 172d745..443503a 100644 --- a/version.go +++ b/version.go @@ -1,7 +1,7 @@ // Code generated by version.sh (@generated) DO NOT EDIT. package main -var githash = "ace13b6" +var githash = "73f9551" var branch = "master" -var buildstamp = "2025-10-19_12:12:16" -var commits = "114" -var version = "ace13b6-b114 - 2025-10-19_12:12:16" +var buildstamp = "2025-10-19_19:40:05" +var commits = "115" +var version = "73f9551-b115 - 2025-10-19_19:40:05"