Files
backup/api.go
2026-01-10 11:52:04 +01:00

805 lines
16 KiB
Go

package main
import (
"encoding/json"
"fmt"
"net/http"
"slices"
"time"
"github.com/gin-gonic/gin"
log "github.com/sirupsen/logrus"
"github.com/tidwall/pretty"
)
func ApiRun(c *gin.Context) {
log.WithFields(log.Fields{}).Debugf("starting")
log.WithFields(log.Fields{}).Debugf("done")
cfg.Run(true)
c.JSON(http.StatusOK, gin.H{
"message": "done",
})
}
func ApiSnapshotAdd(c *gin.Context) {
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("starting")
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("done")
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": fmt.Sprint(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) {
log.WithFields(log.Fields{}).Debugf("starting")
log.WithFields(log.Fields{}).Debugf("done")
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "not implemented",
})
}
func ApiSnapshotList(c *gin.Context) {
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("starting")
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("done")
if app, ok := cfg.apps[c.Param("app")]; ok {
if snapshots, err := app.Snapshots(); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
} else {
c.JSON(http.StatusOK, gin.H{
"message": "done",
"snapshots": snapshots,
})
}
} else {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "no app found",
})
}
}
// FIXME
func ApiScheduleAdd(c *gin.Context) {
log.WithFields(log.Fields{}).Debugf("starting")
log.WithFields(log.Fields{}).Debugf("done")
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "not implemented",
})
}
// FIXME
func ApiScheduleDel(c *gin.Context) {
log.WithFields(log.Fields{}).Debugf("starting")
log.WithFields(log.Fields{}).Debugf("done")
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "not implemented",
})
}
// FIXME
func ApiScheduleList(c *gin.Context) {
log.WithFields(log.Fields{}).Debugf("starting")
log.WithFields(log.Fields{}).Debugf("done")
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "not implemented",
})
}
func ApiRunApp(c *gin.Context) {
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("starting")
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("done")
name := c.Param("app")
found := false
active := false
var (
app *App
err error
)
for _, a := range cfg.Apps {
if a.Name == name {
found = true
active = a.Active
app, err = cfg.NewApp(a.Name, a.Sources, a.Destinations, a.Schedule, a.Before, a.After)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
}
}
if !found {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "no app found",
})
return
}
if !active {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "app not active",
})
return
}
if err := app.RunStandaloneTime(time.Now()); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
} else {
c.JSON(http.StatusOK, gin.H{
"message": "done",
})
}
}
func ApiSave(c *gin.Context) {
log.WithFields(log.Fields{}).Debugf("starting")
log.WithFields(log.Fields{}).Debugf("done")
if err := cfg.Save(true); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
} else {
c.JSON(http.StatusOK, gin.H{
"message": "done",
})
}
}
func ApiConfig(c *gin.Context) {
log.WithFields(log.Fields{}).Debugf("starting")
log.WithFields(log.Fields{}).Debugf("done")
if b, err := cfg.Pretty(true); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
} else {
c.Data(http.StatusOK, "application/json", b)
}
}
func ApiConfigApp(c *gin.Context) {
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("starting")
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("done")
name := c.Param("app")
found := false
CfgLock()
defer CfgUnlock()
for _, app := range cfg.Apps {
if app.Name == name {
found = true
if b, err := app.Pretty(false); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
} else {
c.Data(http.StatusOK, "application/json", b)
}
}
}
if !found {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "no app found",
})
}
}
func ApiAppList(c *gin.Context) {
log.WithFields(log.Fields{}).Debugf("starting")
log.WithFields(log.Fields{}).Debugf("done")
list := make([]string, 0)
CfgLock()
defer CfgUnlock()
for _, app := range cfg.Apps {
list = append(list, app.Name)
}
slices.Sort(list)
b, err := json.Marshal(list)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
c.Data(http.StatusOK, "application/json", pretty.PrettyOptions(b, &pretty.Options{Indent: " "}))
}
func ApiListSchedule(c *gin.Context) {
log.WithFields(log.Fields{"schedule": c.Param("schedule")}).Debugf("starting")
log.WithFields(log.Fields{"schedule": c.Param("schedule")}).Debugf("done")
name := c.Param("schedule")
list := make([]string, 0)
CfgLock()
defer CfgUnlock()
for _, app := range cfg.Apps {
for _, sched := range app.Schedule {
if sched == name && !slices.Contains(list, app.Name) {
list = append(list, app.Name)
}
}
}
slices.Sort(list)
b, err := json.Marshal(list)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
c.Data(http.StatusOK, "application/json", pretty.PrettyOptions(b, &pretty.Options{Indent: " "}))
}
func ApiAppAdd(c *gin.Context) {
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("starting")
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("done")
name := c.Param("app")
found := false
CfgLock()
defer CfgUnlock()
for _, app := range cfg.Apps {
if app.Name == name {
found = true
}
}
if found {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "app already exist",
})
return
}
app := &AppConfig{
Name: name,
Active: false,
}
cfg.Apps = append(cfg.Apps, app)
err := cfg.Save(false)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "done",
})
}
func ApiAppDel(c *gin.Context) {
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("starting")
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("done")
name := c.Param("app")
found := false
CfgLock()
defer CfgUnlock()
for id, app := range cfg.Apps {
if app.Name == name {
if app.Active {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "app still active",
})
return
}
found = true
cfg.Apps[id] = cfg.Apps[len(cfg.Apps)-1]
cfg.Apps = cfg.Apps[:len(cfg.Apps)-1]
}
}
if !found {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "no app found",
})
return
}
err := cfg.Save(false)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "done",
})
}
func ApiAppActivate(c *gin.Context) {
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("starting")
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("done")
name := c.Param("app")
CfgLock()
defer CfgUnlock()
for _, app := range cfg.Apps {
if app.Name == name {
if app.Active {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "app already active",
})
return
}
a, err := cfg.NewApp(app.Name, app.Sources, app.Destinations, app.Schedule, app.Before, app.After)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
cfg.apps[app.Name] = a
}
}
err := cfg.Save(false)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "done",
})
}
func ApiAppDeactivate(c *gin.Context) {
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("starting")
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("done")
name := c.Param("app")
CfgLock()
defer CfgUnlock()
if _, ok := cfg.apps[name]; ok {
delete(cfg.apps, name)
} else {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "app is not active",
})
return
}
for _, app := range cfg.Apps {
if app.Name == name {
app.Active = false
}
}
err := cfg.Save(false)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "done",
})
}
func ApiAppSourceList(c *gin.Context) {
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("starting")
log.WithFields(log.Fields{"app": c.Param("app")}).Debugf("done")
name := c.Param("app")
found := false
app := &AppConfig{}
CfgLock()
defer CfgUnlock()
for _, a := range cfg.Apps {
if a.Name == name {
found = true
app = a
}
}
if !found {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "app does not exist",
})
return
}
b, err := json.Marshal(app.Sources)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
c.Data(http.StatusOK, "application/json", pretty.PrettyOptions(b, &pretty.Options{Indent: " "}))
}
func ApiAppSourceAdd(c *gin.Context) {
app := c.Param("app")
src := c.Param("src")
src = src[1:] // cut first char from wildcard match
log.WithFields(log.Fields{"app": app, "src": src}).Debugf("starting")
log.WithFields(log.Fields{"app": app, "src": src}).Debugf("done")
found := false
ac := &AppConfig{}
CfgLock()
defer CfgUnlock()
for _, a := range cfg.Apps {
if a.Name == app {
found = true
ac = a
}
}
if !found {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "app does not exist",
})
return
}
s := Addr(src)
if s.Box() == "" {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "source box incorrect",
})
return
}
if _, ok := cfg.box[s.Box()]; !ok {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "source box doesn't exist",
})
return
}
if s.Path() == "" {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "source path incorrect",
})
return
}
if slices.Contains(ac.Sources, s.String()) {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "source already exists",
})
return
}
ac.Sources = append(ac.Sources, s.String())
if ac.Active {
a, err := cfg.NewApp(ac.Name, ac.Sources, ac.Destinations, ac.Schedule, ac.Before, ac.After)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
cfg.apps[a.name] = a
}
err := cfg.Save(false)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "done",
})
}
func ApiAppSourceDel(c *gin.Context) {
app := c.Param("app")
src := c.Param("src")
src = src[1:] // cut first char from wildcard match
log.WithFields(log.Fields{"app": app, "src": src}).Debugf("starting")
log.WithFields(log.Fields{"app": app, "src": src}).Debugf("done")
found := false
ac := &AppConfig{}
CfgLock()
defer CfgUnlock()
for _, a := range cfg.Apps {
if a.Name == app {
found = true
ac = a
}
}
if !found {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "app does not exist",
})
return
}
if !slices.Contains(ac.Sources, src) {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "source does not exist",
})
return
}
for id, s := range ac.Sources {
if s == src {
ac.Sources[id] = ac.Sources[len(ac.Sources)-1]
ac.Sources = ac.Sources[:len(ac.Sources)-1]
}
}
if ac.Active {
a, err := cfg.NewApp(ac.Name, ac.Sources, ac.Destinations, ac.Schedule, ac.Before, ac.After)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
cfg.apps[a.name] = a
}
err := cfg.Save(false)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "done",
})
}
func ApiBoxList(c *gin.Context) {
log.WithFields(log.Fields{}).Debugf("starting")
log.WithFields(log.Fields{}).Debugf("done")
list := make([]string, 0)
CfgLock()
defer CfgUnlock()
for name, _ := range cfg.Box {
list = append(list, name)
}
slices.Sort(list)
b, err := json.Marshal(list)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
c.Data(http.StatusOK, "application/json", pretty.PrettyOptions(b, &pretty.Options{Indent: " "}))
}
func ApiBoxAdd(c *gin.Context) {
log.WithFields(log.Fields{"box": c.Param("box")}).Debugf("starting")
log.WithFields(log.Fields{"box": c.Param("box")}).Debugf("done")
name := c.Param("box")
CfgLock()
defer CfgUnlock()
if _, ok := cfg.Box[name]; ok {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "box already exist",
})
return
}
box := &BoxConfig{}
cfg.Box[name] = box
err := cfg.Save(false)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "done",
})
}
func ApiBoxDel(c *gin.Context) {
log.WithFields(log.Fields{"box": c.Param("box")}).Debugf("starting")
log.WithFields(log.Fields{"box": c.Param("box")}).Debugf("done")
name := c.Param("box")
CfgLock()
defer CfgUnlock()
if _, ok := cfg.Box[name]; !ok {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": "no box found",
})
return
}
for _, app := range cfg.Apps {
for _, src := range app.Sources {
a := Addr(src)
if a.Box() == name {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprintf("box used in %s", app.Name),
})
return
}
}
for _, dst := range app.Destinations {
a := Addr(dst)
if a.Box() == name {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprintf("box used in %s", app.Name),
})
return
}
}
for _, bfr := range app.Before {
a := Addr(bfr)
if a.Box() == name {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprintf("box used in %s", app.Name),
})
return
}
}
for _, atr := range app.After {
a := Addr(atr)
if a.Box() == name {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprintf("box used in %s", app.Name),
})
return
}
}
}
delete(cfg.Box, name)
delete(cfg.box, name)
err := cfg.Save(false)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": "error",
"error": fmt.Sprint(err),
})
return
}
c.JSON(http.StatusOK, gin.H{
"message": "done",
})
}