From bfeddd25d108b40bc63bff6f53a64a434ed80102 Mon Sep 17 00:00:00 2001 From: Jamie Holdstock Date: Fri, 12 Jun 2020 14:32:29 +0100 Subject: [PATCH] Download db backup from admin page. --- database/database.go | 16 ++++++++++++++++ webapi/admin.go | 14 ++++++++++++-- webapi/templates/admin.html | 7 +++++-- webapi/templates/login.html | 2 +- webapi/webapi.go | 1 + 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/database/database.go b/database/database.go index 0f4c4ba..146a09c 100644 --- a/database/database.go +++ b/database/database.go @@ -6,7 +6,9 @@ import ( "crypto/rand" "encoding/binary" "fmt" + "net/http" "os" + "strconv" "sync" "time" @@ -247,3 +249,17 @@ func (vdb *VspDatabase) GetCookieSecret() ([]byte, error) { return cookieSecret, err } + +// BackupDB streams a backup of the database over an http response writer. +func (vdb *VspDatabase) BackupDB(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/octet-stream") + w.Header().Set("Content-Disposition", `attachment; filename="vspd.db"`) + + err := vdb.db.View(func(tx *bolt.Tx) error { + w.Header().Set("Content-Length", strconv.Itoa(int(tx.Size()))) + _, err := tx.WriteTo(w) + return err + }) + + return err +} diff --git a/webapi/admin.go b/webapi/admin.go index 7a99bdf..32d7b4d 100644 --- a/webapi/admin.go +++ b/webapi/admin.go @@ -14,8 +14,8 @@ func adminPage(c *gin.Context) { }) } -// ticketSearch is the handler for "POST /admin/ticket". The "hash" param will -// be used to retrieve a ticket from the database. +// ticketSearch is the handler for "POST /admin/ticket". The hash param will be +// used to retrieve a ticket from the database. func ticketSearch(c *gin.Context) { hash := c.PostForm("hash") @@ -59,6 +59,16 @@ func adminLogout(c *gin.Context) { setAdminStatus(nil, c) } +// downloadDatabaseBackup is the handler for "GET /backup". A binary +// representation of the whole database is generated and returned to the client. +func downloadDatabaseBackup(c *gin.Context) { + err := db.BackupDB(c.Writer) + if err != nil { + log.Errorf("Error backing up database: %v", err) + c.String(http.StatusInternalServerError, "Error backing up database") + } +} + // setAdminStatus stores the authentication status of the current session and // redirects the client to GET /admin. func setAdminStatus(admin interface{}, c *gin.Context) { diff --git a/webapi/templates/admin.html b/webapi/templates/admin.html index 6c126e1..bfc10f1 100644 --- a/webapi/templates/admin.html +++ b/webapi/templates/admin.html @@ -4,13 +4,16 @@

Admin

+ + Backup Database +
- +
- +
{{ with .SearchResult }} diff --git a/webapi/templates/login.html b/webapi/templates/login.html index f9263ab..b86f080 100644 --- a/webapi/templates/login.html +++ b/webapi/templates/login.html @@ -4,7 +4,7 @@

Login

- +
{{ if .IncorrectPassword }} diff --git a/webapi/webapi.go b/webapi/webapi.go index a352b29..9aef5ee 100644 --- a/webapi/webapi.go +++ b/webapi/webapi.go @@ -207,6 +207,7 @@ func router(debugMode bool, cookieSecret []byte, dcrd rpc.DcrdConnect, wallets r ) admin.GET("", adminPage) admin.POST("/ticket", ticketSearch) + admin.GET("/backup", downloadDatabaseBackup) admin.POST("/logout", adminLogout) // These API routes access dcrd and the voting wallets, and they need