From e64b610fed3d1c6ef911451ee9f506260a211fa4 Mon Sep 17 00:00:00 2001 From: BroodjeAap Date: Mon, 30 Jan 2023 20:21:05 +0000 Subject: [PATCH] added backup restore functionality, refactor of backup test --- main.go | 149 ++++++++++++++++++++++++++++++---- static/backup.js | 17 ++++ static/backup.ts | 21 +++++ templates/backup/restore.html | 69 ++++++++++++++++ templates/backup/view.html | 36 ++++++-- todo.md | 4 +- 6 files changed, 271 insertions(+), 25 deletions(-) create mode 100644 static/backup.js create mode 100644 static/backup.ts create mode 100644 templates/backup/restore.html diff --git a/main.go b/main.go index 8f15b50..70ab606 100644 --- a/main.go +++ b/main.go @@ -197,7 +197,8 @@ func (web *Web) initRouter() { web.router.GET("/backup/view", web.backupView) web.router.GET("/backup/create", web.backupCreate) - web.router.GET("/backup/test/:id", web.backupTest) + web.router.POST("/backup/test", web.backupTest) + web.router.POST("/backup/restore", web.backupRestore) web.router.SetTrustedProxies(nil) } @@ -223,6 +224,7 @@ func (web *Web) initTemplates() { web.templates.Add("backupView", template.Must(template.ParseFS(templatesFS, "base.html", "backup/view.html"))) web.templates.Add("backupTest", template.Must(template.ParseFS(templatesFS, "base.html", "backup/test.html"))) + web.templates.Add("backupRestore", template.Must(template.ParseFS(templatesFS, "base.html", "backup/restore.html"))) web.templates.Add("500", template.Must(template.ParseFS(templatesFS, "base.html", "500.html"))) } @@ -973,12 +975,12 @@ func (web *Web) createBackup(backupPath string) error { // backupTest (/backup/test) tests the selected backup file func (web *Web) backupTest(c *gin.Context) { - importID, err := strconv.Atoi(c.Param("id")) + importID, err := strconv.Atoi(c.PostForm("id")) if err != nil { c.AbortWithError(http.StatusBadRequest, err) return } - if importID < 0 { + if importID < -1 { c.Redirect(http.StatusSeeOther, "/backup/view") return } @@ -995,45 +997,160 @@ func (web *Web) backupTest(c *gin.Context) { return } + backupFullPath := "" + var backup Backup + if importID >= 0 { + backupFullPath, err = web.backupFromFile(importID, &backup) + } else { // uploaded backup file + backupFullPath, err = web.backupFromUpload(c, &backup) + } + + if err != nil { + c.HTML(http.StatusOK, "backupTest", gin.H{"Error": err}) + return + } + + c.HTML(http.StatusOK, "backupTest", gin.H{ + "Backup": backup, + "BackupPath": backupFullPath, + }) +} + +func (web *Web) backupFromFile(importID int, backup *Backup) (string, error) { backupPath := viper.GetString("database.backup.path") backupDir, err := filepath.Abs(filepath.Dir(backupPath)) if err != nil { - c.HTML(http.StatusOK, "backupTest", gin.H{"Error": err}) - return + return "", err } filesInBackupDir, err := ioutil.ReadDir(backupDir) if err != nil { - c.HTML(http.StatusOK, "backupTest", gin.H{"Error": err}) - return + return "", err } if importID >= len(filesInBackupDir) { - c.Redirect(http.StatusSeeOther, "/backup/view") - return + return "", err } backupFileName := filesInBackupDir[importID] backupFullPath := filepath.Join(backupDir, backupFileName.Name()) backupFile, err := os.Open(backupFullPath) if err != nil { - c.HTML(http.StatusOK, "backupTest", gin.H{"Error": err}) - return + return "", err } defer backupFile.Close() backupReader, err := gzip.NewReader(backupFile) if err != nil { - c.HTML(http.StatusOK, "backupTest", gin.H{"Error": err}) - return + return "", err + } + defer backupReader.Close() + rawBytes, err := io.ReadAll(backupReader) + err = json.Unmarshal(rawBytes, backup) + if err != nil { + return "", err + } + return backupFullPath, nil +} + +func (web *Web) backupFromUpload(c *gin.Context, backup *Backup) (string, error) { + upload, err := c.FormFile("upload") + if err != nil { + return "", err + } + backupFullPath := upload.Filename + " (Uploaded)" + + uploadFile, err := upload.Open() + if err != nil { + return "", err + } + defer uploadFile.Close() + + backupReader, err := gzip.NewReader(uploadFile) + if err != nil { + return "", err } defer backupReader.Close() rawBytes, err := io.ReadAll(backupReader) - var backup Backup - json.Unmarshal(rawBytes, &backup) + err = json.Unmarshal(rawBytes, &backup) + if err != nil { + return "", err + } + return backupFullPath, nil +} - c.HTML(http.StatusOK, "backupTest", gin.H{ +// backupRestore (/backup/restore/:id) either restores the filesInBackupDir[id] file or from an uploaded file +func (web *Web) backupRestore(c *gin.Context) { + importID, err := strconv.Atoi(c.PostForm("id")) + if err != nil { + c.AbortWithError(http.StatusBadRequest, err) + return + } + if importID < -1 { + c.Redirect(http.StatusSeeOther, "/backup/view") + return + } + + if !viper.IsSet("database.backup") { + c.HTML(http.StatusOK, "backupRestore", gin.H{"Error": "database.backup not set"}) + return + } + if !viper.IsSet("database.backup.schedule") { + c.HTML(http.StatusOK, "backupRestore", gin.H{"Error": "database.backup.schedule not set"}) + return + } + if !viper.IsSet("database.backup.path") { + c.HTML(http.StatusOK, "backupRestore", gin.H{"Error": "database.backup.path not set"}) + return + } + + backupFullPath := "" + var backup Backup + if importID >= 0 { + backupFullPath, err = web.backupFromFile(importID, &backup) + } else { // uploaded backup file + backupFullPath, err = web.backupFromUpload(c, &backup) + } + + if err != nil { + c.HTML(http.StatusOK, "backupRestore", gin.H{"Error": err}) + return + } + + err = web.db.Transaction(func(tx *gorm.DB) error { + delete := tx.Where("1 = 1").Delete(&Watch{}) + if delete.Error != nil { + return err + } + + watches := tx.Create(&backup.Watches) + if watches.Error != nil { + return err + } + + filters := tx.Create(&backup.Filters) + if filters.Error != nil { + return err + } + + connections := tx.Create(&backup.Connections) + if connections.Error != nil { + return err + } + + values := tx.Create(&backup.Values) + if values.Error != nil { + return err + } + return nil + }) + if err != nil { + c.HTML(http.StatusOK, "backupRestore", gin.H{"Error": err}) + return + } + + c.HTML(http.StatusOK, "backupRestore", gin.H{ "Backup": backup, "BackupPath": backupFullPath, }) diff --git a/static/backup.js b/static/backup.js new file mode 100644 index 0000000..b623741 --- /dev/null +++ b/static/backup.js @@ -0,0 +1,17 @@ +function testSubmit() { + var form = document.getElementById("uploadForm"); + form.action = "/backup/test"; + form.submit(); +} +function restoreSubmit() { + var form = document.getElementById("uploadForm"); + form.action = "/backup/restore"; + form.submit(); +} +function initUploadSubmit() { + var testSubmitInput = document.getElementById("testSubmit"); + testSubmitInput.onclick = testSubmit; + var restoreSubmitInput = document.getElementById("restoreSubmit"); + restoreSubmitInput.onclick = restoreSubmit; +} +document.addEventListener('DOMContentLoaded', initUploadSubmit, false); diff --git a/static/backup.ts b/static/backup.ts new file mode 100644 index 0000000..c171f2d --- /dev/null +++ b/static/backup.ts @@ -0,0 +1,21 @@ +function testSubmit() { + let form = document.getElementById("uploadForm") as HTMLFormElement; + form.action = "/backup/test"; + form.submit(); +} + +function restoreSubmit() { + let form = document.getElementById("uploadForm") as HTMLFormElement; + form.action = "/backup/restore"; + form.submit(); +} + +function initUploadSubmit(){ + let testSubmitInput = document.getElementById("testSubmit") as HTMLInputElement; + testSubmitInput.onclick = testSubmit; + + let restoreSubmitInput = document.getElementById("restoreSubmit") as HTMLInputElement; + restoreSubmitInput.onclick = restoreSubmit; +} + +document.addEventListener('DOMContentLoaded', initUploadSubmit, false); \ No newline at end of file diff --git a/templates/backup/restore.html b/templates/backup/restore.html new file mode 100644 index 0000000..e3133dd --- /dev/null +++ b/templates/backup/restore.html @@ -0,0 +1,69 @@ +{{define "title"}} +GoWatch Backup Restore +{{end}} +{{define "content"}} + +
+
+ Restore: {{ .BackupPath }} +
+ {{ if .Error }} +
+ {{ .Error }} +
+ {{ end }} + + + + + + + + + + {{ range $watch := .Backup.Watches }} + + + + + {{ end }} + +
Watches
IDName
{{ $watch.ID }}{{ $watch.Name }}
+
+ Stored Values: {{ len .Backup.Values }} +
+ + + + + + + + + + + + + + + + {{ range $filter := .Backup.Filters }} + + + + + + + + + + + {{ end }} + +
Filters
IDWatchIDNameTypeVar1Var2Var3X/Y
{{ $filter.ID }}{{ $filter.WatchID }}{{ $filter.Name }}{{ $filter.Type }}{{ $filter.Var1 }}{{ $filter.Var2 }}{{ $filter.Var3 }}{{ $filter.X }}/{{ $filter.Y }}
+
+ FilterConnections: {{ len .Backup.Connections }} +
+
+ +{{end}} \ No newline at end of file diff --git a/templates/backup/view.html b/templates/backup/view.html index 00d4ac3..70fd11b 100644 --- a/templates/backup/view.html +++ b/templates/backup/view.html @@ -1,6 +1,11 @@ {{define "title"}} GoWatch Backups {{end}} + +{{define "head"}} + +{{ end }} + {{define "content"}}
@@ -22,21 +27,38 @@ GoWatch Backups + + +
+ + +
+ + + + + + + + + {{ range $i, $backup := .Backups }} {{ $backup }} - - Test - +
+ + +
- - Restore - +
+ + +
- + Download diff --git a/todo.md b/todo.md index 231b603..caae125 100644 --- a/todo.md +++ b/todo.md @@ -7,7 +7,7 @@ - diagram.ts - ~~scheduled backup~~ - ~~test~~ - - restore + - ~~restore~~ - delete - download - - restore from upload \ No newline at end of file + - ~~restore from upload~~