Restore database file
This commit is contained in:
parent
29efc3df4c
commit
04e744b8e5
17
pkg/db/db.go
17
pkg/db/db.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue