diff --git a/ROADMAP.md b/ROADMAP.md index 4a99aaa..652a636 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,7 +2,9 @@ ## Todo -- implement commands +- cron +- check & enforce config +- auto create key ## Packages diff --git a/internal/backend.go b/internal/backend.go index 962d8ce..7fe7001 100644 --- a/internal/backend.go +++ b/internal/backend.go @@ -1,7 +1,12 @@ package internal import ( + "crypto/rand" + "encoding/base64" "fmt" + "strings" + + "github.com/spf13/viper" ) type Backend struct { @@ -41,7 +46,44 @@ func (b Backend) getEnv() (map[string]string, error) { return env, err } +func generateRandomKey() string { + b := make([]byte, 64) + rand.Read(b) + key := base64.StdEncoding.EncodeToString(b) + key = strings.ReplaceAll(key, "=", "") + key = strings.ReplaceAll(key, "+", "") + key = strings.ReplaceAll(key, "/", "") + return key +} + func (b Backend) validate() error { + if b.Name == "" { + return fmt.Errorf(`Backend has no "name"`) + } + if b.Type == "" { + return fmt.Errorf(`Backend "%s" has no "type"`, b.Name) + } + if b.Path == "" { + return fmt.Errorf(`Backend "%s" has no "path"`, b.Name) + } + if b.Key == "" { + key := generateRandomKey() + b.Key = key + c := GetConfig() + for i, backend := range c.Backends { + if backend.Name == b.Name { + c.Backends[i].Key = key + break + } + } + file := viper.ConfigFileUsed() + if err := CopyFile(file, file+".old"); err != nil { + return err + } + fmt.Println("Saved a backup copy of your file next the the original.") + viper.Set("backends", c.Backends) + viper.WriteConfig() + } env, err := b.getEnv() if err != nil { return err diff --git a/internal/config.go b/internal/config.go index 99cd23d..97f5b47 100644 --- a/internal/config.go +++ b/internal/config.go @@ -2,7 +2,6 @@ package internal import ( "fmt" - "log" "path" "strings" "sync" @@ -27,7 +26,7 @@ func GetConfig() *Config { once.Do(func() { config = &Config{} if err := viper.UnmarshalExact(config); err != nil { - log.Fatal("Nope ", err) + panic(err) } }) } @@ -45,15 +44,27 @@ func GetPathRelativeToConfig(p string) (string, error) { } } -func (c Config) CheckConfig() error { +func (c *Config) CheckConfig() error { + found := map[string]bool{} for _, backend := range c.Backends { if err := backend.validate(); err != nil { - return fmt.Errorf("backend \"%s\": %s", backend.Name, err) + return err + } + if _, ok := found[backend.Name]; ok { + return fmt.Errorf(`duplicate name for backends "%s"`, backend.Name) + } else { + found[backend.Name] = true } } + found = map[string]bool{} for _, location := range c.Locations { if err := location.validate(c); err != nil { - return fmt.Errorf("location \"%s\": %s", location.Name, err) + return err + } + if _, ok := found[location.Name]; ok { + return fmt.Errorf(`duplicate name for locations "%s"`, location.Name) + } else { + found[location.Name] = true } } return nil diff --git a/internal/location.go b/internal/location.go index c0c632e..6b179ef 100644 --- a/internal/location.go +++ b/internal/location.go @@ -39,7 +39,16 @@ func GetLocation(name string) (Location, bool) { return Location{}, false } -func (l Location) validate(c Config) error { +func (l Location) validate(c *Config) error { + if l.Name == "" { + return fmt.Errorf(`Location is missing name`) + } + if l.From == "" { + return fmt.Errorf(`Location "%s" is missing "from" key`, l.Name) + } + if len(l.To) == 0 { + return fmt.Errorf(`Location "%s" has no "to" targets`, l.Name) + } // Check if backends are all valid for _, to := range l.To { _, ok := GetBackend(to) diff --git a/internal/utils.go b/internal/utils.go index 108c233..febc060 100644 --- a/internal/utils.go +++ b/internal/utils.go @@ -3,6 +3,7 @@ package internal import ( "bytes" "fmt" + "io" "os" "os/exec" ) @@ -46,3 +47,22 @@ func ExecuteResticCommand(options ExecuteOptions, args ...string) (string, error options.Command = "restic" return ExecuteCommand(options, args...) } + +func CopyFile(from, to string) error { + original, err := os.Open("original.txt") + if err != nil { + return nil + } + defer original.Close() + + new, err := os.Create("new.txt") + if err != nil { + return nil + } + defer new.Close() + + if _, err := io.Copy(new, original); err != nil { + return err + } + return nil +}