diff --git a/.gitignore b/.gitignore index 555b8e6..65edebb 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,6 @@ atlassian-ide-plugin.xml ########## Custom ########## -_out/* \ No newline at end of file +_out/* + +script_old.sh \ No newline at end of file diff --git a/app/application.go b/app/application.go index b7680cf..40e279e 100644 --- a/app/application.go +++ b/app/application.go @@ -3,18 +3,29 @@ package app import ( "os" "os/signal" + "sync" "syscall" "fyne.io/systray" + "git.blackforestbytes.com/BlackForestBytes/goext/syncext" "mikescher.com/kpsync" + "mikescher.com/kpsync/assets" + "mikescher.com/kpsync/log" ) type Application struct { + masterLock sync.Mutex + config kpsync.Config - trayReady bool - sigStopChan chan bool - sigErrChan chan error + trayReady bool + uploadRunning *syncext.AtomicBool + + sigStopChan chan bool // keepass exited + sigErrChan chan error // fatal error + + sigSyncLoopStopChan chan bool // stop sync loop + sigTermKeepassChan chan bool // stop keepass dbFile string stateFile string @@ -25,10 +36,14 @@ func NewApplication() *Application { cfg := kpsync.LoadConfig() return &Application{ - config: cfg, - trayReady: false, - sigStopChan: make(chan bool, 128), - sigErrChan: make(chan error, 128), + masterLock: sync.Mutex{}, + config: cfg, + uploadRunning: syncext.NewAtomicBool(false), + trayReady: false, + sigStopChan: make(chan bool, 128), + sigErrChan: make(chan error, 128), + sigSyncLoopStopChan: make(chan bool, 128), + sigTermKeepassChan: make(chan bool, 128), } } @@ -42,6 +57,9 @@ func (app *Application) Run() { app.sigErrChan <- err return } + + app.setTrayStateDirect("Sleeping...", assets.IconDefault) + err = app.runSyncLoop() if err != nil { app.sigErrChan <- err @@ -55,14 +73,27 @@ func (app *Application) Run() { select { case <-sigTerm: + app.sigSyncLoopStopChan <- true + app.sigTermKeepassChan <- true + log.LogInfo("Stopping application (received SIGTERM signal)") + // TODO term - case _ = <-app.sigErrChan: + case err := <-app.sigErrChan: + + app.sigSyncLoopStopChan <- true + app.sigTermKeepassChan <- true + log.LogInfo("Stopping application (received ERROR)") + log.LogError(err.Error(), err) // TODO stop case _ = <-app.sigStopChan: + app.sigSyncLoopStopChan <- true + app.sigTermKeepassChan <- true + log.LogInfo("Stopping application (received STOP)") + // TODO stop } diff --git a/app/sync.go b/app/sync.go index 6638678..6d13526 100644 --- a/app/sync.go +++ b/app/sync.go @@ -1,11 +1,18 @@ package app import ( + "errors" "fmt" "os" + "os/exec" "path" + "syscall" + "time" "git.blackforestbytes.com/BlackForestBytes/goext/exerr" + "git.blackforestbytes.com/BlackForestBytes/goext/langext" + "git.blackforestbytes.com/BlackForestBytes/goext/timeext" + "github.com/fsnotify/fsnotify" "mikescher.com/kpsync/assets" "mikescher.com/kpsync/log" ) @@ -20,6 +27,11 @@ func (app *Application) initSync() error { app.dbFile = path.Join(app.config.WorkDir, path.Base(app.config.LocalFallback)) app.stateFile = path.Join(app.config.WorkDir, "kpsync.state") + if isKeepassRunning() { + log.LogError("keepassxc is already running!", nil) + return exerr.New(exerr.TypeInternal, "keepassxc is already running").Build() + } + state := app.readState() needsDownload := true @@ -29,16 +41,18 @@ func (app *Application) initSync() error { if err != nil { log.LogError("Failed to calculate local database checksum", err) } else if localCS == state.Checksum { - remoteETag, err := app.getRemoteETag() + remoteETag, remoteLM, err := app.getRemoteETag() if err != nil { log.LogError("Failed to get remote ETag", err) } else if remoteETag == state.ETag { log.LogInfo(fmt.Sprintf("Found local database matching remote database - skip initial download")) - log.LogInfo(fmt.Sprintf("Checksum (cached) := %s", state.Checksum)) - log.LogInfo(fmt.Sprintf("Checksum (local) := %s", localCS)) - log.LogInfo(fmt.Sprintf("ETag (cached) := %s", state.ETag)) - log.LogInfo(fmt.Sprintf("ETag (remote) := %s", remoteETag)) + log.LogInfo(fmt.Sprintf("Checksum (cached) := %s", state.Checksum)) + log.LogInfo(fmt.Sprintf("Checksum (local) := %s", localCS)) + log.LogInfo(fmt.Sprintf("ETag (cached) := %s", state.ETag)) + log.LogInfo(fmt.Sprintf("ETag (remote) := %s", remoteETag)) + log.LogInfo(fmt.Sprintf("LastModified (cached) := %s", state.LastModified.Format(time.RFC3339))) + log.LogInfo(fmt.Sprintf("LastModified (remote) := %s", remoteLM.Format(time.RFC3339))) needsDownload = false } @@ -46,22 +60,152 @@ func (app *Application) initSync() error { } if needsDownload { - func() { - fin := app.setTrayState("Downloading database", assets.IconDefault) + err = func() error { + fin := app.setTrayState("Downloading database", assets.IconDownload) defer fin() log.LogInfo(fmt.Sprintf("Downloading remote database to %s", app.dbFile)) - etag, err := app.downloadDatabase() + etag, lm, sha, sz, err := app.downloadDatabase() if err != nil { log.LogError("Failed to download remote database", err) - app.sigErrChan <- exerr.Wrap(err, "Failed to download remote database").Build() - return + return exerr.Wrap(err, "Failed to download remote database").Build() + } + + log.LogInfo(fmt.Sprintf("Downloaded remote database to %s", app.dbFile)) + log.LogInfo(fmt.Sprintf("Checksum := %s", sha)) + log.LogInfo(fmt.Sprintf("ETag := %s", etag)) + log.LogInfo(fmt.Sprintf("Size := %s", langext.FormatBytes(sz))) + log.LogInfo(fmt.Sprintf("LastModified := %s", lm.Format(time.RFC3339))) + + err = app.saveState(etag, lm, sha, sz) + if err != nil { + log.LogError("Failed to save state", err) + return exerr.Wrap(err, "Failed to save state").Build() + } + + return nil + }() + if err != nil { + return exerr.Wrap(err, "").Build() + } + } else { + log.LogInfo(fmt.Sprintf("Skip download - use existing local database %s", app.dbFile)) + } + + go func() { + + log.LogInfo("Starting keepassxc...") + + cmd := exec.Command("keepassxc", app.dbFile) + + go func() { + select { + case <-app.sigTermKeepassChan: + log.LogInfo("Received signal to terminate keepassxc") + if cmd != nil && cmd.Process != nil { + log.LogInfo(fmt.Sprintf("Terminating keepassxc %d", cmd.Process.Pid)) + err := cmd.Process.Signal(syscall.SIGTERM) + if err != nil { + log.LogError("Failed to terminate keepassxc", err) + } else { + log.LogInfo("keepassxc terminated successfully") + } + } else { + log.LogInfo("No keepassxc process to terminate") + } } }() - } + + err := cmd.Start() + if err != nil { + log.LogError("Failed to start keepassxc", err) + app.sigErrChan <- exerr.Wrap(err, "Failed to start keepassxc").Build() + return + } + + log.LogInfo(fmt.Sprintf("keepassxc started with PID %d", cmd.Process.Pid)) + + err = cmd.Wait() + + exitErr := &exec.ExitError{} + if errors.As(err, &exitErr) { + + log.LogInfo(fmt.Sprintf("keepass exited with code %d", exitErr.ExitCode())) + app.sigStopChan <- true + return + + } + + if err != nil { + log.LogError("Failed to run keepassxc", err) + app.sigErrChan <- exerr.Wrap(err, "Failed to run keepassxc").Build() + return + } + + log.LogInfo("keepassxc exited successfully") + app.sigStopChan <- true + return + + }() + + return nil } func (app *Application) runSyncLoop() error { + watcher, err := fsnotify.NewWatcher() + if err != nil { + return exerr.Wrap(err, "failed to init file-watcher").Build() + } + defer func() { _ = watcher.Close() }() + err = watcher.Add(app.config.WorkDir) + if err != nil { + return exerr.Wrap(err, "").Build() + } + + for { + select { + case <-app.sigSyncLoopStopChan: + log.LogInfo("Stopping sync loop (received signal)") + return nil + + case event := <-watcher.Events: + log.LogInfo(fmt.Sprintf("inotify event: [%s] %s", event.Op.String(), event.Name)) + + if event.Has(fsnotify.Write) && event.Name == app.dbFile { + func() { + app.masterLock.Lock() + app.uploadRunning.Wait(false) + app.uploadRunning.Set(true) + app.masterLock.Unlock() + + defer app.uploadRunning.Set(false) + + log.LogInfo("Database file was modified") + log.LogInfo(fmt.Sprintf("Sleeping for %d seconds", app.config.Debounce)) + + time.Sleep(timeext.FromSeconds(app.config.Debounce)) + + state := app.readState() + localCS, err := app.calcLocalChecksum() + if err != nil { + log.LogError("Failed to calculate local database checksum", err) + return + } + + if localCS == state.Checksum { + log.LogInfo("Local database still matches remote (via checksum) - no need to upload") + log.LogInfo(fmt.Sprintf("Checksum (remote/cached) := %s", state.Checksum)) + log.LogInfo(fmt.Sprintf("Checksum (local) := %s", localCS)) + return + } + + //TODO upload with IfMatch + }() + } + case err := <-watcher.Errors: + log.LogError("Filewatcher reported an error", err) + } + } } diff --git a/app/tray.go b/app/tray.go index 8c48b65..47d8ee1 100644 --- a/app/tray.go +++ b/app/tray.go @@ -18,3 +18,41 @@ func (app *Application) initTray() { systray.Run(trayOnReady, nil) } + +func (app *Application) setTrayState(txt string, icon []byte) func() { + if !app.trayReady { + return func() {} + } + + app.masterLock.Lock() + defer app.masterLock.Unlock() + + systray.SetIcon(icon) + systray.SetTooltip(txt) + + fin := func() { + app.masterLock.Lock() + defer app.masterLock.Unlock() + + if !app.trayReady { + return + } + + systray.SetIcon(assets.IconDefault) + systray.SetTooltip("Sleeping...") + } + + return fin +} + +func (app *Application) setTrayStateDirect(txt string, icon []byte) { + if !app.trayReady { + return + } + + app.masterLock.Lock() + defer app.masterLock.Unlock() + + systray.SetIcon(icon) + systray.SetTooltip(txt) +} diff --git a/app/utils.go b/app/utils.go index 16d3f0c..da7c0f4 100644 --- a/app/utils.go +++ b/app/utils.go @@ -3,9 +3,13 @@ package app import ( "encoding/json" "os" + "strings" + "time" "git.blackforestbytes.com/BlackForestBytes/goext/cryptext" "git.blackforestbytes.com/BlackForestBytes/goext/exerr" + "github.com/shirou/gopsutil/v3/process" + "mikescher.com/kpsync/log" ) func fileExists(p string) bool { @@ -17,12 +21,16 @@ func fileExists(p string) bool { } type State struct { - ETag string `json:"etag"` - Size int64 `json:"size"` - Checksum string `json:"checksum"` + ETag string `json:"etag"` + Size int64 `json:"size"` + Checksum string `json:"checksum"` + LastModified time.Time `json:"lastModified"` } func (app *Application) readState() *State { + app.masterLock.Lock() + defer app.masterLock.Unlock() + bin, err := os.ReadFile(app.stateFile) if err != nil { return nil @@ -37,6 +45,30 @@ func (app *Application) readState() *State { return &state } +func (app *Application) saveState(eTag string, lastModified time.Time, checksum string, size int64) error { + app.masterLock.Lock() + defer app.masterLock.Unlock() + + obj := State{ + ETag: eTag, + Size: size, + Checksum: checksum, + LastModified: lastModified, + } + + bin, err := json.MarshalIndent(obj, "", " ") + if err != nil { + return exerr.Wrap(err, "Failed to marshal state").Build() + } + + err = os.WriteFile(app.stateFile, bin, 0644) + if err != nil { + return exerr.Wrap(err, "Failed to write state file").Build() + } + + return nil +} + func (app *Application) calcLocalChecksum() (string, error) { bin, err := os.ReadFile(app.dbFile) if err != nil { @@ -45,3 +77,24 @@ func (app *Application) calcLocalChecksum() (string, error) { return cryptext.BytesSha256(bin), nil } + +func isKeepassRunning() bool { + proc, err := process.Processes() + if err != nil { + log.LogError("failed to query existing keepass process", err) + return false + } + + for _, p := range proc { + name, err := p.Name() + if err != nil { + continue + } + + if strings.ToLower(name) == "keepass" || strings.ToLower(name) == "keepassxc" { + return true + } + } + + return false +} diff --git a/app/webdav.go b/app/webdav.go index 1558da7..7c83495 100644 --- a/app/webdav.go +++ b/app/webdav.go @@ -1,5 +1,99 @@ package app -func (app *Application) initSync() { +import ( + "io" + "net/http" + "os" + "time" + "git.blackforestbytes.com/BlackForestBytes/goext/cryptext" + "git.blackforestbytes.com/BlackForestBytes/goext/exerr" + "git.blackforestbytes.com/BlackForestBytes/goext/timeext" +) + +func (app *Application) downloadDatabase() (string, time.Time, string, int64, error) { + + client := http.Client{Timeout: 90 * time.Second} + + req, err := http.NewRequest("GET", app.config.WebDAVURL, nil) + if err != nil { + return "", time.Time{}, "", 0, exerr.Wrap(err, "").Build() + } + + req.SetBasicAuth(app.config.WebDAVUser, app.config.WebDAVPass) + + resp, err := client.Do(req) + if err != nil { + return "", time.Time{}, "", 0, exerr.Wrap(err, "Failed to download remote database").Build() + } + + if resp.StatusCode != http.StatusOK { + return "", time.Time{}, "", 0, exerr.New(exerr.TypeInternal, "Failed to download remote database").Int("sc", resp.StatusCode).Build() + } + + bin, err := io.ReadAll(resp.Body) + if err != nil { + return "", time.Time{}, "", 0, exerr.Wrap(err, "Failed to read response body").Build() + } + + etag := resp.Header.Get("ETag") + if etag == "" { + return "", time.Time{}, "", 0, exerr.New(exerr.TypeInternal, "ETag header is missing").Build() + } + + lmStr := resp.Header.Get("Last-Modified") + + lm, err := time.Parse("Mon, 02 Jan 2006 15:04:05 MST", lmStr) + if err != nil { + return "", time.Time{}, "", 0, exerr.Wrap(err, "Failed to parse Last-Modified header").Build() + } + + lm = lm.In(timeext.TimezoneBerlin) + + sha := cryptext.BytesSha256(bin) + + sz := int64(len(bin)) + + err = os.WriteFile(app.dbFile, bin, 0644) + if err != nil { + return "", time.Time{}, "", 0, exerr.Wrap(err, "Failed to write database file").Build() + } + + return etag, lm, sha, sz, nil +} + +func (app *Application) getRemoteETag() (string, time.Time, error) { + client := http.Client{Timeout: 90 * time.Second} + + req, err := http.NewRequest("HEAD", app.config.WebDAVURL, nil) + if err != nil { + return "", time.Time{}, exerr.Wrap(err, "").Build() + } + + req.SetBasicAuth(app.config.WebDAVUser, app.config.WebDAVPass) + + resp, err := client.Do(req) + if err != nil { + return "", time.Time{}, exerr.Wrap(err, "Failed to download remote database").Build() + } + + if resp.StatusCode != http.StatusOK { + return "", time.Time{}, exerr.New(exerr.TypeInternal, "Failed to download remote database").Int("sc", resp.StatusCode).Build() + } + + etag := resp.Header.Get("ETag") + if etag == "" { + return "", time.Time{}, exerr.New(exerr.TypeInternal, "ETag header is missing").Build() + } + + lmStr := resp.Header.Get("Last-Modified") + + lm, err := time.Parse("Mon, 02 Jan 2006 15:04:05 MST", lmStr) + if err != nil { + return "", time.Time{}, exerr.Wrap(err, "Failed to parse Last-Modified header").Build() + } + + lm = lm.In(timeext.TimezoneBerlin) + + return etag, lm, nil } diff --git a/assets/assets.go b/assets/assets.go index 292c66f..05ce50a 100644 --- a/assets/assets.go +++ b/assets/assets.go @@ -4,7 +4,7 @@ import ( _ "embed" ) -//go:embed IconInit.png +//go:embed iconInit.png var IconInit []byte //go:embed iconDefault.png diff --git a/assets/iconDefault.png b/assets/iconDefault.png index 420c618..d2c7c4b 100644 Binary files a/assets/iconDefault.png and b/assets/iconDefault.png differ diff --git a/assets/iconDownload.png b/assets/iconDownload.png new file mode 100644 index 0000000..37f8840 Binary files /dev/null and b/assets/iconDownload.png differ diff --git a/assets/iconInit.png b/assets/iconInit.png new file mode 100644 index 0000000..2b77ac9 Binary files /dev/null and b/assets/iconInit.png differ diff --git a/config.go b/config.go index b541e09..658bd67 100644 --- a/config.go +++ b/config.go @@ -48,26 +48,26 @@ func LoadConfig() Config { flag.Parse() - if strings.HasSuffix(configPath, "~") { + if strings.HasPrefix(configPath, "~") { usr, err := user.Current() if err != nil { log.FatalErr("Failed to query users home directory", err) } fmt.Println(usr.HomeDir) - configPath = strings.TrimSuffix(configPath, "~") + configPath = strings.TrimPrefix(configPath, "~") configPath = fmt.Sprintf("%s/%s", usr.HomeDir, configPath) } if _, err := os.Stat(configPath); os.IsNotExist(err) && configPath != "" { - _ = os.WriteFile(configPath, langext.Must(json.Marshal(Config{ + _ = os.WriteFile(configPath, langext.Must(json.MarshalIndent(Config{ WebDAVURL: "https://your-nextcloud-domain.example/remote.php/dav/files/keepass.kdbx", WebDAVUser: "", WebDAVPass: "", LocalFallback: "", WorkDir: "/tmp/kpsync", Debounce: 3500, - })), 0644) + }, "", " ")), 0644) } cfgBin, err := os.ReadFile(configPath) diff --git a/go.mod b/go.mod index 11349a8..0ae1e96 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,50 @@ module mikescher.com/kpsync go 1.24.6 require ( - fyne.io/systray v1.11.0 // indirect - git.blackforestbytes.com/BlackForestBytes/goext v0.0.594 // indirect - github.com/godbus/dbus/v5 v5.1.0 // indirect - golang.org/x/sys v0.34.0 // indirect + fyne.io/systray v1.11.0 + git.blackforestbytes.com/BlackForestBytes/goext v0.0.594 + github.com/fsnotify/fsnotify v1.9.0 + github.com/shirou/gopsutil/v3 v3.24.5 +) + +require ( + github.com/bytedance/sonic v1.13.3 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect + github.com/gabriel-vasile/mimetype v1.4.9 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/gin-gonic/gin v1.10.1 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.27.0 // indirect + github.com/goccy/go-json v0.10.5 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/rs/xid v1.6.0 // indirect + github.com/rs/zerolog v1.34.0 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.mongodb.org/mongo-driver v1.17.4 // indirect + golang.org/x/arch v0.19.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.34.0 // indirect + golang.org/x/text v0.27.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d9880a9..a1ef4ed 100644 --- a/go.sum +++ b/go.sum @@ -2,7 +2,145 @@ fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg= fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= git.blackforestbytes.com/BlackForestBytes/goext v0.0.594 h1:tFbvEAe7FMvLuW9/RuXXlxsi8/Ve7xVrFxZnk8e/0yU= git.blackforestbytes.com/BlackForestBytes/goext v0.0.594/go.mod h1:vczjjViG013HjA5Ka3VTE7axDgqMChn1EsvEVg9LZnU= +github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0= +github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= +github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= +github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= +github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= +github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= +github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= +github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= +github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw= +go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= +golang.org/x/arch v0.19.0 h1:LmbDQUodHThXE+htjrnmVD73M//D9GTH6wFZjyDkjyU= +golang.org/x/arch v0.19.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/log/logger.go b/log/logger.go index 5333aa6..0affaad 100644 --- a/log/logger.go +++ b/log/logger.go @@ -1,5 +1,9 @@ package log +import ( + "git.blackforestbytes.com/BlackForestBytes/goext/exerr" +) + //TODO func Fatal(msg string) { @@ -8,15 +12,20 @@ func Fatal(msg string) { func FatalErr(msg string, err error) { if err != nil { - panic("ERROR: " + msg + "\n" + err.Error()) + println("FATAL: " + msg) + println(" " + err.Error()) + println(exerr.FromError(err).FormatLog(exerr.LogPrintOverview)) + panic(0) } else { - panic("ERROR: " + msg) + panic("FATAL: " + msg) } } func LogError(msg string, err error) { if err != nil { - println("ERROR: " + msg + "\n" + err.Error()) + println("ERROR: " + msg) + println(" " + err.Error()) + println(exerr.FromError(err).FormatLog(exerr.LogPrintOverview)) } else { println("ERROR: " + msg) }