Restore database file

This commit is contained in:
kolaente 2020-06-21 14:44:43 +02:00
parent 29efc3df4c
commit 04e744b8e5
Signed by: konrad
GPG Key ID: F40E70337AB24C9B
4 changed files with 147 additions and 15 deletions

View File

@ -168,3 +168,20 @@ func initSqliteEngine() (engine *xorm.Engine, err error) {
return xorm.NewEngine("sqlite3", path)
}
// WipeEverything wipes all tables and their data. Use with caution...
func WipeEverything() error {
tables, err := x.DBMetas()
if err != nil {
return err
}
for _, t := range tables {
if err := x.DropTables(t.Name); err != nil {
return err
}
}
return nil
}

View File

@ -40,3 +40,14 @@ func Dump() (data map[string][]byte, err error) {
return
}
func Restore(table string, contents []map[string]interface{}) (err error) {
for _, content := range contents {
if _, err := x.Table(table).Insert(content); err != nil {
return err
}
}
return
}

View File

@ -40,15 +40,8 @@ func LightInit() {
log.InitLogger()
}
// FullInit initializes all kinds of things in the right order
func FullInit() {
LightInit()
// Run the migrations
migration.Migrate(nil)
// Set Engine
// InitEngines intializes all db connections
func InitEngines() {
err := models.SetEngine()
if err != nil {
log.Fatal(err.Error())
@ -65,6 +58,18 @@ func FullInit() {
if err != nil {
log.Fatal(err.Error())
}
}
// FullInit initializes all kinds of things in the right order
func FullInit() {
LightInit()
// Run the migrations
migration.Migrate(nil)
// Set Engine
InitEngines()
// Initialize the files handler
files.InitFileHandler()

View File

@ -19,10 +19,18 @@ package dump
import (
"archive/zip"
"bufio"
"bytes"
"code.vikunja.io/api/pkg/db"
"code.vikunja.io/api/pkg/files"
"code.vikunja.io/api/pkg/initialize"
"code.vikunja.io/api/pkg/log"
"code.vikunja.io/api/pkg/migration"
"encoding/json"
"fmt"
"io"
"os"
"sort"
"src.techknowlogick.com/xormigrate"
"strings"
)
@ -45,21 +53,93 @@ func Restore(filename string) error {
return fmt.Errorf("invalid confirmation message")
}
// Find the config file
var config *zip.File
// Find the configFile, database and files files
var configFile *zip.File
dbfiles := make(map[string]*zip.File)
filesFiles := make(map[string]*zip.File)
for _, file := range r.File {
if strings.HasPrefix(file.Name, "config") {
config = file
break
configFile = file
continue
}
if strings.HasPrefix(file.Name, "database/") {
fname := strings.ReplaceAll(file.Name, "database/", "")
dbfiles[fname[:len(fname)-5]] = file
continue
}
if strings.HasPrefix(file.Name, "files/") {
filesFiles[strings.ReplaceAll(file.Name, "files/", "")] = file
}
}
if config == nil {
if configFile == nil {
return fmt.Errorf("dump does not contain a config file")
}
if err := writeFile(config, config.Name); err != nil {
///////
// Restore the config file
if err := writeFile(configFile, configFile.Name); err != nil {
return fmt.Errorf("could not create config file: %s", err)
}
log.Info("Restored configFile file.")
// Init the configFile again since the restored configuration is most likely different from the one before
initialize.LightInit()
initialize.InitEngines()
files.InitFileHandler()
///////
// Restore the db
// Start by wiping everything
if err := db.WipeEverything(); err != nil {
return fmt.Errorf("could not wipe database: %s", err)
}
log.Info("Wiped database.")
// Because we don't explicitly saved the table definitions, we take the last ran db migration from the dump
// and execute everything until that point.
migrations := dbfiles["migration"]
rc, err := migrations.Open()
if err != nil {
return fmt.Errorf("could not open migrations: %s", err)
}
defer rc.Close()
var buf bytes.Buffer
if _, err := buf.ReadFrom(rc); err != nil {
return fmt.Errorf("could not read migrations: %s", err)
}
ms := []*xormigrate.Migration{}
if err := json.Unmarshal(buf.Bytes(), &ms); err != nil {
return fmt.Errorf("could not read migrations: %s", err)
}
sort.Slice(ms, func(i, j int) bool {
return ms[i].ID > ms[j].ID
})
lastMigration := ms[len(ms)-1]
if err := migration.MigrateTo(lastMigration.ID, nil); err != nil {
return fmt.Errorf("could not create db structure: %s", err)
}
// Restore all db data
for table, d := range dbfiles {
content, err := unmarshalFileToJson(d)
if err != nil {
return fmt.Errorf("could not read table %s: %s", table, err)
}
if err := db.Restore(table, content); err != nil {
return fmt.Errorf("could not restore table data for table %s: %s", table, err)
}
log.Infof("Restored table %s", table)
}
log.Infof("Restored %d tables", len(dbfiles))
// Run migrations again to migrate a potentially outdated dump
migration.Migrate(nil)
///////
// Restore Files
return nil
}
@ -81,3 +161,22 @@ func writeFile(file *zip.File, path string) error {
_, err = io.Copy(outFile, rc)
return err
}
func unmarshalFileToJson(file *zip.File) (contents []map[string]interface{}, err error) {
rc, err := file.Open()
if err != nil {
return
}
defer rc.Close()
var buf bytes.Buffer
if _, err := buf.ReadFrom(rc); err != nil {
return nil, err
}
contents = []map[string]interface{}{}
if err := json.Unmarshal(buf.Bytes(), &contents); err != nil {
return nil, err
}
return
}