[add] restore command

This commit is contained in:
Max 2022-06-13 21:32:20 +08:00
parent c913a62778
commit e43bc0b612
3 changed files with 219 additions and 7 deletions

View file

@ -2,6 +2,7 @@ package cmd
import (
"archive/zip"
"errors"
"fmt"
"io/ioutil"
"os"
@ -49,6 +50,12 @@ var dumpCmd = &cobra.Command{
os.Exit(1)
}
_, err = os.Stat(output)
if !errors.Is(err, os.ErrNotExist) {
fmt.Println(color.RedString("%s exists", output))
os.Exit(1)
}
// Load model
err = engine.Load(config.Conf)
if err != nil {
@ -57,10 +64,8 @@ var dumpCmd = &cobra.Command{
}
if dumpModel != "" {
mod, has := gou.Models[dumpModel]
if has {
mod.Migrate(true)
}
fmt.Println(color.YellowString(L("Not supported yet")))
os.Exit(1)
return
}
@ -100,9 +105,9 @@ var dumpCmd = &cobra.Command{
},
}
func init() {
dumpCmd.PersistentFlags().StringVarP(&dumpModel, "name", "n", "", L("Model name"))
}
// func init() {
// // dumpCmd.PersistentFlags().StringVarP(&dumpModel, "name", "n", "", L("Model name"))
// }
// gzipfiles
func zipfiles(files []string, output string, process func(file string)) error {

206
cmd/restore.go Normal file
View file

@ -0,0 +1,206 @@
package cmd
import (
"archive/zip"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
"github.com/fatih/color"
"github.com/spf13/cobra"
"github.com/yaoapp/gou"
"github.com/yaoapp/kun/exception"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/engine"
"github.com/yaoapp/yao/share"
)
var restoreForce bool = false
var restoreCmd = &cobra.Command{
Use: "restore",
Short: L("Restore the application data"),
Long: L("Restore the application data"),
Run: func(cmd *cobra.Command, args []string) {
defer func() {
err := exception.Catch(recover())
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
}
}()
if len(args) < 1 {
fmt.Println(color.RedString(L("Not enough arguments")))
fmt.Println(color.WhiteString(share.BUILDNAME + " help"))
os.Exit(1)
}
zipfile, err := filepath.Abs(args[0])
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
Boot()
if !restoreForce && config.Conf.Mode == "production" {
fmt.Println(color.WhiteString(L("TRY:")), color.GreenString("%s restore --force", share.BUILDNAME))
exception.New(L("Retore is not allowed on production mode."), 403).Throw()
}
// Unzip files
dst := unzipFile(zipfile, func(file string) {
fmt.Printf("\r%s", strings.Repeat(" ", 80))
fmt.Printf("\r%s", color.GreenString(L("Unzip the file: %s"), file))
})
// 加载数据模型
err = engine.Load(config.Conf)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
// Restore models
restoreModels(filepath.Join(dst, "model"))
// Restore Data
restoreData(filepath.Join(dst, "data"))
// Clean
os.RemoveAll(dst)
fmt.Println(color.GreenString(L("✨DONE✨")))
},
}
func init() {
restoreCmd.PersistentFlags().BoolVarP(&restoreForce, "force", "", false, L("Force restore"))
}
func restoreData(basePath string) {
_, err := os.Stat(basePath)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
// Clean Data
dataPath := filepath.Join(config.Conf.Root, "data")
_, err = os.Stat(dataPath)
if err == nil {
os.RemoveAll(dataPath)
}
err = os.Rename(basePath, dataPath)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
}
func restoreModels(basePath string) {
files, err := ioutil.ReadDir(basePath)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
// Migrate models
for _, mod := range gou.Models {
fmt.Printf("\r%s", strings.Repeat(" ", 80))
fmt.Printf(color.GreenString(L("\rUpdate schema model: %s (%s) "), mod.Name, mod.MetaData.Table.Name))
err := mod.ForceCreateSchema()
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
}
fmt.Println("")
for _, file := range files {
namer := strings.Split(file.Name(), ".")
name := strings.Join(namer[:len(namer)-2], ".")
if mod, has := gou.Models[name]; has {
fmt.Printf("\r%s", strings.Repeat(" ", 80))
fmt.Printf(color.GreenString(L("\rRestore model: %s (%s) "), mod.Name, mod.MetaData.Table.Name))
err := mod.Import(filepath.Join(basePath, file.Name()))
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
}
}
}
func unzipFile(file string, process func(file string)) string {
_, err := os.Stat(file)
if errors.Is(err, os.ErrNotExist) {
fmt.Println(color.RedString("%s not exists", file))
os.Exit(1)
}
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
dst := filepath.Join(os.TempDir(), fmt.Sprintf("%s-%s", filepath.Base(file), time.Now().Format("20060102150405")))
os.MkdirAll(dst, 0755)
archive, err := zip.OpenReader(file)
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
defer archive.Close()
for _, f := range archive.File {
filePath := filepath.Join(dst, f.Name)
process(f.Name)
if !strings.HasPrefix(filePath, filepath.Clean(dst)+string(os.PathSeparator)) {
fmt.Println(color.RedString(L("Fatal: invalid file path")))
os.Exit(1)
}
if f.FileInfo().IsDir() {
os.MkdirAll(filePath, os.ModePerm)
continue
}
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
dstFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
fileInArchive, err := f.Open()
if err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
if _, err := io.Copy(dstFile, fileInArchive); err != nil {
fmt.Println(color.RedString(L("Fatal: %s"), err.Error()))
os.Exit(1)
}
dstFile.Close()
fileInArchive.Close()
}
return dst
}

View file

@ -93,6 +93,7 @@ func init() {
initCmd,
serviceCmd,
dumpCmd,
restoreCmd,
)
// rootCmd.SetHelpCommand(helpCmd)
rootCmd.PersistentFlags().StringVarP(&appPath, "app", "a", "", L("Application directory"))