mirror of
https://github.com/Walter-Sparrow/lunar-tear.git
synced 2026-07-02 13:53:41 +03:00
136 lines
3.2 KiB
Go
136 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
|
|
"charm.land/huh/v2"
|
|
"charm.land/lipgloss/v2"
|
|
)
|
|
|
|
const (
|
|
gameDBPath = "db/game.db"
|
|
backupDir = "db/backups"
|
|
backupSuffix = ".bak"
|
|
)
|
|
|
|
const banner = `
|
|
_ _____
|
|
| | _ _ _ _ __ _ _ _ |_ _|___ __ _ _ _
|
|
| |_| || | ' \/ _` + "`" + ` | '_| | |/ -_)/ _` + "`" + ` | '_|
|
|
|____\_,_|_||_\__,_|_| |_|\___|\__,_|_|
|
|
|
|
╭──────────────────────────────╮
|
|
│ RESTORE │
|
|
╰──────────────────────────────╯
|
|
`
|
|
|
|
func main() {
|
|
lipgloss.EnableLegacyWindowsANSI(os.Stdout)
|
|
lipgloss.EnableLegacyWindowsANSI(os.Stderr)
|
|
fmt.Print(banner)
|
|
|
|
chosen, ok := pickBackup()
|
|
if !ok {
|
|
return
|
|
}
|
|
if !confirmOverwrite(chosen) {
|
|
fmt.Println(" cancelled — nothing changed")
|
|
return
|
|
}
|
|
if err := doRestore(chosen); err != nil {
|
|
fmt.Fprintf(os.Stderr, " restore failed: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
fmt.Printf(" restored %s from %s\n", gameDBPath, chosen)
|
|
}
|
|
|
|
func pickBackup() (string, bool) {
|
|
entries, err := os.ReadDir(backupDir)
|
|
if err != nil {
|
|
fmt.Fprintln(os.Stderr, " no backups found in", backupDir)
|
|
return "", false
|
|
}
|
|
var backups []string
|
|
for _, e := range entries {
|
|
if !e.IsDir() && strings.HasPrefix(e.Name(), "game.db.") && strings.HasSuffix(e.Name(), backupSuffix) {
|
|
backups = append(backups, e.Name())
|
|
}
|
|
}
|
|
if len(backups) == 0 {
|
|
fmt.Fprintln(os.Stderr, " no backups found in", backupDir)
|
|
return "", false
|
|
}
|
|
sort.Slice(backups, func(i, j int) bool { return backups[i] > backups[j] })
|
|
|
|
options := make([]huh.Option[string], 0, len(backups)+1)
|
|
for _, name := range backups {
|
|
options = append(options, huh.NewOption(name, name))
|
|
}
|
|
options = append(options, huh.NewOption("Cancel", ""))
|
|
|
|
var chosen string
|
|
if err := huh.NewSelect[string]().
|
|
Title("Pick a backup to restore").
|
|
Description("db/game.db will be replaced by the chosen file.").
|
|
Options(options...).
|
|
Value(&chosen).
|
|
Run(); err != nil || chosen == "" {
|
|
return "", false
|
|
}
|
|
return chosen, true
|
|
}
|
|
|
|
func confirmOverwrite(chosen string) bool {
|
|
confirm := false
|
|
if err := huh.NewConfirm().
|
|
Title("Overwrite db/game.db?").
|
|
Description(fmt.Sprintf(
|
|
"This will REPLACE db/game.db with %s.\n"+
|
|
"Any progress since that backup will be lost.\n"+
|
|
"(A fresh backup will be taken on the next ./wizard launch.)",
|
|
chosen)).
|
|
Affirmative("Yes, restore").
|
|
Negative("Cancel").
|
|
Value(&confirm).
|
|
Run(); err != nil {
|
|
return false
|
|
}
|
|
return confirm
|
|
}
|
|
|
|
func doRestore(chosen string) error {
|
|
src := filepath.Join(backupDir, chosen)
|
|
if _, err := os.Stat(src); errors.Is(err, os.ErrNotExist) {
|
|
return fmt.Errorf("%s no longer exists", src)
|
|
}
|
|
if err := copyFile(src, gameDBPath); err != nil {
|
|
return err
|
|
}
|
|
_ = os.Remove(gameDBPath + "-wal")
|
|
_ = os.Remove(gameDBPath + "-shm")
|
|
return nil
|
|
}
|
|
|
|
func copyFile(src, dst string) error {
|
|
in, err := os.Open(src)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer in.Close()
|
|
out, err := os.Create(dst)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer out.Close()
|
|
if _, err := io.Copy(out, in); err != nil {
|
|
return err
|
|
}
|
|
return out.Sync()
|
|
}
|