From cfc37e575f98ab6e411432b233f8d561ce39773c Mon Sep 17 00:00:00 2001 From: Marcel Otte Date: Tue, 7 Jun 2022 21:20:16 +0200 Subject: [PATCH] Reworking UI concept... --- backive-example.yml | 1 + cmd/backive/main.go | 10 ++++-- cmd/backive_ui/main.go | 7 +++- config.go | 11 ++++--- ui/ui_main.go | 75 ++++++++++++++++++++++++++---------------- ui_handler.go | 63 +++++++++++++++++++++++++++++++++++ 6 files changed, 131 insertions(+), 36 deletions(-) create mode 100644 ui_handler.go diff --git a/backive-example.yml b/backive-example.yml index 864a7cf..444f551 100644 --- a/backive-example.yml +++ b/backive-example.yml @@ -40,6 +40,7 @@ backups: settings: systemMountPoint: /mnt/backups unixSocketLocation: /var/local/backive/backive.sock + uiUnixSocketLocation: /var/local/backive/ui.sock logLocation: /var/log/backive dbLocation: /var/lib/backive diff --git a/cmd/backive/main.go b/cmd/backive/main.go index 0bbbe59..4865567 100644 --- a/cmd/backive/main.go +++ b/cmd/backive/main.go @@ -29,6 +29,7 @@ var ( config backive.Configuration database backive.Database events backive.EventHandler + uihdl backive.UIHandler ) func defaultCallback(envMap map[string]string) { @@ -127,17 +128,22 @@ func main() { if err != nil { log.Printf("Removal of %s failed.", config.Settings.UnixSocketLocation) } + err = os.Remove(config.Settings.UIUnixSocketLocation) + if err != nil { + log.Printf("Removal of %s failed.", config.Settings.UIUnixSocketLocation) + } os.Exit(code) }() - // TODO: do proper signal handling! log.Println("backive starting up...") // find and load config database.Load() config.Load() setupLogging() backive.Init(config, database) - + uihdl.Init(config.Settings.UIUnixSocketLocation) + // Start UIHandler to be able to inform users through notifications + go uihdl.Listen() // init scheduler and check for next needed runs? // start event loop events.Init(config.Settings.UnixSocketLocation) diff --git a/cmd/backive_ui/main.go b/cmd/backive_ui/main.go index 8c28730..d4188c4 100644 --- a/cmd/backive_ui/main.go +++ b/cmd/backive_ui/main.go @@ -1,6 +1,8 @@ package main import ( + "time" + "fyne.io/fyne/v2/app" "github.com/qwc/backive" @@ -20,7 +22,10 @@ func main() { app := app.NewWithID("Backive UI") backiveui.Init(app, nil, config, database) go func() { - backiveui.NotificationRun() + for { + backiveui.NotificationRun() + time.Sleep(time.Second) + } }() app.Run() diff --git a/config.go b/config.go index 36c145a..3da1755 100644 --- a/config.go +++ b/config.go @@ -18,11 +18,12 @@ type Configuration struct { // Settings struct holds the global configuration items type Settings struct { - SystemMountPoint string `mapstructure:"systemMountPoint"` - UserMountPoint string `mapstructure:"userMountPoint"` - UnixSocketLocation string `mapstructure:"unixSocketLocation"` - LogLocation string `mapstructure:"logLocation"` - DbLocation string `mapstructure:"dbLocation"` + SystemMountPoint string `mapstructure:"systemMountPoint"` + UserMountPoint string `mapstructure:"userMountPoint"` + UnixSocketLocation string `mapstructure:"unixSocketLocation"` + UIUnixSocketLocation string `mapstructure:"uiUnixSocketLocation"` + LogLocation string `mapstructure:"logLocation"` + DbLocation string `mapstructure:"dbLocation"` } // CreateViper creates a viper instance for usage later diff --git a/ui/ui_main.go b/ui/ui_main.go index eec5c20..90ba090 100644 --- a/ui/ui_main.go +++ b/ui/ui_main.go @@ -1,7 +1,9 @@ package ui import ( + "encoding/json" "fmt" + "net" "time" "fyne.io/fyne/v2" @@ -16,16 +18,52 @@ var ( config backive.Configuration db backive.Database doNotShowUntil time.Time = time.Unix(0, 0) + c net.Conn ) -func Init(a fyne.App, w fyne.Window, c backive.Configuration, d backive.Database) { +func Init(a fyne.App, w fyne.Window, conf backive.Configuration, d backive.Database) { app = a a.SetIcon(theme.FyneLogo()) makeTray(app) - config = c + config = conf db = d + go PollConnection() } + +func PollConnection() { + var err error + for { + if c == nil { + c, err = net.Dial("unix", config.Settings.UIUnixSocketLocation) + } else { + err = fmt.Errorf("Connection already established") + } + if err != nil { + // ignore + err = nil + // sleep a while and then retry + time.Sleep(10 * time.Second) + } + } +} + func NotificationRun() { + if c != nil { + b := make([]byte, 2048) + i, err := c.Read(b) + if err == nil && i > 0 { + var data map[string]string + err = json.Unmarshal(b, &data) + if err == nil { + ShowNotification(data) + } + // else ignore and try to read again + err = nil + } + // we just try again and discard the error + err = nil + } + /* if doNotShowUntil == time.Unix(0, 0) || time.Now().After(doNotShowUntil) { ShowNotification() if doNotShowUntil != time.Unix(0, 0) { @@ -34,40 +72,22 @@ func NotificationRun() { } h, _ := time.ParseDuration("15m") time.Sleep(h) + //*/ } -func ShowNotification() { - displayStr, err := MakeNotificationString() - if err == nil { +func ShowNotification(data map[string]string) { + if ShallShow(data) { app.SendNotification( fyne.NewNotification( - "Backups are overdue...", - fmt.Sprintf("Name\t(device)\t[overdue]\n%s", displayStr), + data["header"], + data["message"], ), ) } } -func MakeNotificationString() (string, error) { - db.Load() - var displayStr string = "" - var runs backive.Runs - runs.Load(db) - fmt.Printf("Notification run\n") - for _, v := range config.Backups { - fmt.Printf("Notification run %s\n", v.Name) - if v.ShouldRun() && v.Frequency > 0 { - fmt.Printf("Notification for %s\n", v.Name) - lastBackup, err := runs.LastRun(v) - if err != nil { - return "", err - } - freq, _ := time.ParseDuration(fmt.Sprintf("%dd", v.Frequency)) - days := time.Now().Sub(lastBackup.Add(freq)) - displayStr += fmt.Sprintf("%s\t(%s)\t[%f days]\n", v.Name, v.TargetDevice, days.Hours()/24) - } - } - return displayStr, nil +func ShallShow(data map[string]string) bool { + return true } func makeTray(app fyne.App) { @@ -75,7 +95,6 @@ func makeTray(app fyne.App) { menu := fyne.NewMenu( "backive", fyne.NewMenuItem("Show notifications again", func() { - ShowNotification() }), fyne.NewMenuItem("Hide notifications for today", func() { doNotShowUntil = time.Now().AddDate(0, 0, 1) diff --git a/ui_handler.go b/ui_handler.go new file mode 100644 index 0000000..48262e7 --- /dev/null +++ b/ui_handler.go @@ -0,0 +1,63 @@ +package backive + +import ( + "encoding/json" + "fmt" + "log" + "net" + "path" +) + +type UIHandler struct { + ls net.Listener + client net.Conn +} + +var mockUIAccept = func(uh *UIHandler) (net.Conn, error) { + log.Println("Calling eh.ls.Accept()") + return uh.ls.Accept() +} + +func (uh *UIHandler) Init(socketPath string) error { + log.Println("Initializing UIHandler") + var err error + dir, _ := path.Split(socketPath) + CreateDirectoryIfNotExists(dir) + uh.ls, err = net.Listen("unix", socketPath) + if err != nil { + return err + } + return nil +} + +func (uh *UIHandler) Listen() { + log.Println("Running UIHandler loop") + func() { + for { + var err error + uh.client, err = uh.ls.Accept() + if err != nil { + log.Printf("Accept failed %e\n", err) + } + } + }() +} + +func (uh *UIHandler) DisplayMessage(header string, message string, level int) error { + if uh.client != nil { + var data map[string]interface{} + data["level"] = level + data["header"] = header + data["message"] = message + b, err := json.Marshal(data) + if err != nil { + log.Printf("Problem in sending message to UI: %e", err) + return err + } + uh.client.Write(b) + return nil + } else { + log.Println("No UI client available, msg did not get delivered.") + return fmt.Errorf("No UI client available, msg not delivered.") + } +}