From 02a9f9683b00a92287e751860f897a59b0f5ecca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denny=20K=C3=B6nigsman?= Date: Fri, 21 May 2021 11:15:50 +0200 Subject: [PATCH 01/13] Fix bug preventing message from appearing --- internal/pkg/communication/logger.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/communication/logger.go b/internal/pkg/communication/logger.go index fa52893..cd36035 100644 --- a/internal/pkg/communication/logger.go +++ b/internal/pkg/communication/logger.go @@ -207,7 +207,7 @@ func (l *stdoutLogger) LoadingFailure(tunnelID string, err error) { func (l *stdoutLogger) NewVersionAvailable(availableVersion string) { l.messageMutex.Lock() defer l.messageMutex.Unlock() - fmt.Fprint(l.colorableOutput, aurora.Cyan(fmt.Sprintf("There is new version available, to get it please visit %s", + fmt.Fprintln(l.colorableOutput, aurora.Cyan(fmt.Sprintf("There is new version available, to get it please visit %s", fmt.Sprintf("https://github.com/loophole/cli/releases/tag/%s", availableVersion)))) } From 5f604ce66f2b662b53912f04a54b0155e7d8345b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denny=20K=C3=B6nigsmann?= Date: Tue, 25 May 2021 13:04:17 +0200 Subject: [PATCH 02/13] Add popup for desktop, move CheckVersion function --- cmd/http.go | 2 +- cmd/path.go | 2 +- cmd/virtual-serve.go | 23 ----------------------- cmd/webdav.go | 2 +- go.mod | 1 + go.sum | 2 ++ internal/app/loophole/loophole.go | 31 +++++++++++++++++++++++++++++++ ui/ui.go | 1 + 8 files changed, 38 insertions(+), 26 deletions(-) diff --git a/cmd/http.go b/cmd/http.go index d613ab5..81439f5 100644 --- a/cmd/http.go +++ b/cmd/http.go @@ -29,7 +29,7 @@ To expose port running on some local host e.g. 192.168.1.20 use 'loophole http < idToken := token.GetIdToken() communication.ApplicationStart(loggedIn, idToken) - checkVersion() + loophole.CheckVersion() localEndpointSpecs.Host = "127.0.0.1" if len(args) > 1 { diff --git a/cmd/path.go b/cmd/path.go index 205d8f5..cac9193 100644 --- a/cmd/path.go +++ b/cmd/path.go @@ -26,7 +26,7 @@ To expose local directory (e.g. /data/my-data) simply use 'loophole path /data/m idToken := token.GetIdToken() communication.ApplicationStart(loggedIn, idToken) - checkVersion() + loophole.CheckVersion() dirEndpointSpecs.Path = args[0] quitChannel := make(chan bool) diff --git a/cmd/virtual-serve.go b/cmd/virtual-serve.go index fdb6004..84da4c0 100644 --- a/cmd/virtual-serve.go +++ b/cmd/virtual-serve.go @@ -10,10 +10,8 @@ import ( "strings" "github.com/beevik/guid" - "github.com/blang/semver/v4" "github.com/loophole/cli/config" lm "github.com/loophole/cli/internal/app/loophole/models" - "github.com/loophole/cli/internal/pkg/apiclient" "github.com/loophole/cli/internal/pkg/cache" "github.com/loophole/cli/internal/pkg/communication" "github.com/loophole/cli/internal/pkg/inpututil" @@ -90,24 +88,3 @@ func parseBasicAuthFlags(flagset *pflag.FlagSet) error { return nil } - -func checkVersion() { - availableVersion, err := apiclient.GetLatestAvailableVersion() - if err != nil { - communication.Debug("There was a problem obtaining info response, skipping further checking") - return - } - currentVersionParsed, err := semver.Make(config.Config.Version) - if err != nil { - communication.Debug(fmt.Sprintf("Cannot parse current version '%s' as semver version, skipping further checking", config.Config.Version)) - return - } - availableVersionParsed, err := semver.Make(availableVersion.Version) - if err != nil { - communication.Debug(fmt.Sprintf("Cannot parse available version '%s' as semver version, skipping further checking", availableVersion)) - return - } - if currentVersionParsed.LT(availableVersionParsed) { - communication.NewVersionAvailable(availableVersion.Version) - } -} diff --git a/cmd/webdav.go b/cmd/webdav.go index 75cea17..7fc3e33 100644 --- a/cmd/webdav.go +++ b/cmd/webdav.go @@ -28,7 +28,7 @@ To expose local directory via webdav (e.g. /data/my-data) simply use 'loophole w idToken := token.GetIdToken() communication.ApplicationStart(loggedIn, idToken) - checkVersion() + loophole.CheckVersion() webdavEndpointSpecs.Path = args[0] quitChannel := make(chan bool) diff --git a/go.mod b/go.mod index 45c25ca..76dcc63 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/mdp/qrterminal v1.0.1 github.com/mitchellh/go-homedir v1.1.0 github.com/ncruces/zenity v0.5.2 + github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rs/zerolog v1.19.0 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 diff --git a/go.sum b/go.sum index faa032b..72f13d4 100644 --- a/go.sum +++ b/go.sum @@ -96,6 +96,8 @@ github.com/ncruces/zenity v0.5.2/go.mod h1:FPwYbb/qb/eMG2psReJl+L1+0LtXeDkj4R+pi github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI= +github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= diff --git a/internal/app/loophole/loophole.go b/internal/app/loophole/loophole.go index 6f63d1c..2d691c9 100644 --- a/internal/app/loophole/loophole.go +++ b/internal/app/loophole/loophole.go @@ -7,6 +7,7 @@ import ( "net/http" "time" + "github.com/blang/semver/v4" "github.com/loophole/cli/config" lm "github.com/loophole/cli/internal/app/loophole/models" "github.com/loophole/cli/internal/pkg/apiclient" @@ -14,6 +15,8 @@ import ( "github.com/loophole/cli/internal/pkg/httpserver" "github.com/loophole/cli/internal/pkg/keys" "github.com/loophole/cli/internal/pkg/urlmaker" + "github.com/ncruces/zenity" + "github.com/pkg/browser" "golang.org/x/crypto/ssh" ) @@ -35,6 +38,34 @@ var remoteEndpoint = lm.Endpoint{ Port: 80, } +func CheckVersion() { + availableVersion, err := apiclient.GetLatestAvailableVersion() + if err != nil { + communication.Debug("There was a problem obtaining info response, skipping further checking") + return + } + currentVersionParsed, err := semver.Make(config.Config.Version) + if err != nil { + communication.Debug(fmt.Sprintf("Cannot parse current version '%s' as semver version, skipping further checking", config.Config.Version)) + return + } + availableVersionParsed, err := semver.Make(availableVersion.Version) + if err != nil { + communication.Debug(fmt.Sprintf("Cannot parse available version '%s' as semver version, skipping further checking", availableVersion)) + return + } + if currentVersionParsed.LT(availableVersionParsed) { + if config.Config.ClientMode == "cli" { + communication.NewVersionAvailable(availableVersion.Version) + } else { + response, _ := zenity.Question(fmt.Sprintf("A new version is available at https://github.com/loophole/cli/releases/tag/%s \nDo you want to open the link in your browser now?", availableVersion.Version), zenity.NoWrap(), zenity.Title("New version available!")) + if response { + browser.OpenURL(fmt.Sprintf("https://github.com/loophole/cli/releases/tag/%s", availableVersion.Version)) + } + } + } +} + func handleClient(tunnelID string, client net.Conn, local net.Conn) { defer client.Close() chDone := make(chan bool) diff --git a/ui/ui.go b/ui/ui.go index 653a479..7bb9006 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -259,6 +259,7 @@ func Display() { } defer ui.Close() + loophole.CheckVersion() <-ui.Done() } From b0dc43dcc9b1a8b9d00f2ad8ebdc285cc3d73ab2 Mon Sep 17 00:00:00 2001 From: Denny Date: Tue, 1 Jun 2021 12:45:25 +0200 Subject: [PATCH 03/13] Limit desktop update reminder to once a day --- go.mod | 5 ++-- go.sum | 10 +------- internal/app/loophole/loophole.go | 38 +++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 76dcc63..8bba1a1 100644 --- a/go.mod +++ b/go.mod @@ -15,12 +15,13 @@ require ( github.com/mdp/qrterminal v1.0.1 github.com/mitchellh/go-homedir v1.1.0 github.com/ncruces/zenity v0.5.2 - github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 + github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.19.0 github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 github.com/spf13/cobra v1.0.0 github.com/spf13/pflag v1.0.5 + github.com/spf13/viper v1.4.0 github.com/zserge/lorca v0.1.9 golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 golang.org/x/net v0.0.0-20200301022130-244492dfa37a diff --git a/go.sum b/go.sum index 72f13d4..07aeb21 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/abbot/go-http-auth v0.4.0 h1:QjmvZ5gSC7jm3Zg54DqWE/T5m1t2AfDu6QlXJT0EVT0= @@ -17,7 +18,6 @@ github.com/briandowns/spinner v1.11.1/go.mod h1:QOuQk7x+EaDASo80FEXwlwiA+j/PPIcX github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible h1:jFneRYjIvLMLhDLCzuTuU4rSJUjRplcJQ7pD7MnhC04= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= @@ -38,7 +38,6 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/here v0.6.0 h1:hYrd0a6gDmWxBM4TnrGw8mQg24iSVoIkHEk7FodQcBI= github.com/gobuffalo/here v0.6.0/go.mod h1:wAG085dHOYqUpf+Ap+WOdrPTp5IYcDAs/x7PLa8Y5fM= github.com/gobuffalo/here v0.6.2 h1:ZtCqC7F9ou3moLbYfHM1Tj+gwHGgWhjyRjVjsir9BE0= github.com/gobuffalo/here v0.6.2/go.mod h1:D75Sq0p2BVHdgQu3vCRsXbg85rx943V19urJpqAVWjI= @@ -66,7 +65,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -134,7 +132,6 @@ github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -192,15 +189,12 @@ golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -222,14 +216,12 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= diff --git a/internal/app/loophole/loophole.go b/internal/app/loophole/loophole.go index 2d691c9..d64872e 100644 --- a/internal/app/loophole/loophole.go +++ b/internal/app/loophole/loophole.go @@ -15,8 +15,10 @@ import ( "github.com/loophole/cli/internal/pkg/httpserver" "github.com/loophole/cli/internal/pkg/keys" "github.com/loophole/cli/internal/pkg/urlmaker" + "github.com/mitchellh/go-homedir" "github.com/ncruces/zenity" "github.com/pkg/browser" + "github.com/spf13/viper" "golang.org/x/crypto/ssh" ) @@ -58,6 +60,10 @@ func CheckVersion() { if config.Config.ClientMode == "cli" { communication.NewVersionAvailable(availableVersion.Version) } else { + remind, _ := remindAgainCheck() + if !remind { + return + } response, _ := zenity.Question(fmt.Sprintf("A new version is available at https://github.com/loophole/cli/releases/tag/%s \nDo you want to open the link in your browser now?", availableVersion.Version), zenity.NoWrap(), zenity.Title("New version available!")) if response { browser.OpenURL(fmt.Sprintf("https://github.com/loophole/cli/releases/tag/%s", availableVersion.Version)) @@ -66,6 +72,38 @@ func CheckVersion() { } } +func remindAgainCheck() (bool, error) { //TODO: Error handling, moving this function to a more appropriate file (probably to config pkg) + home, err := homedir.Dir() + if err != nil { + return true, err + } + + layout := "2006-02-01" //golangs arcane time format string + viper.SetDefault("last-reminder", time.Time{}.Format(layout)) //zero value for time + viper.SetConfigName("config") // name of config file (without extension) + viper.SetConfigType("json") // REQUIRED if the config file does not have the extension in the name + viper.AddConfigPath(fmt.Sprintf("%s/.loophole/", home)) + if err := viper.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { //create a config if none exist yet + viper.WriteConfigAs(fmt.Sprintf("%s/.loophole/config.json", home)) + } else { + return true, err + } + } + + t, err := time.Parse(layout, fmt.Sprintf("%v", viper.Get("last-reminder"))) + if err != nil { + return true, err + } + if (t.Year() < time.Now().Year()) || (t.YearDay() < time.Now().YearDay()) { //check if reminder has been done today + viper.Set("last-reminder", time.Now().Format(layout)) + viper.WriteConfigAs("/home/work/.loophole/config.json") + return true, nil + } + + return false, nil +} + func handleClient(tunnelID string, client net.Conn, local net.Conn) { defer client.Close() chDone := make(chan bool) From 7e93ac3bebd569b78038c486a4f2948cb2efce80 Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 4 Jun 2021 10:15:49 +0200 Subject: [PATCH 04/13] Replace link to release page with one to specific binary --- internal/app/loophole/loophole.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/internal/app/loophole/loophole.go b/internal/app/loophole/loophole.go index d64872e..d43c0ca 100644 --- a/internal/app/loophole/loophole.go +++ b/internal/app/loophole/loophole.go @@ -5,6 +5,7 @@ import ( "io" "net" "net/http" + "runtime" "time" "github.com/blang/semver/v4" @@ -64,14 +65,36 @@ func CheckVersion() { if !remind { return } - response, _ := zenity.Question(fmt.Sprintf("A new version is available at https://github.com/loophole/cli/releases/tag/%s \nDo you want to open the link in your browser now?", availableVersion.Version), zenity.NoWrap(), zenity.Title("New version available!")) + dlLink := getDownloadLink(availableVersion.Version) + response, _ := zenity.Question(fmt.Sprintf("A new version is available for you at \n%s \nDo you want to open the link in your browser now?", dlLink), zenity.NoWrap(), zenity.Title("New version available!")) if response { - browser.OpenURL(fmt.Sprintf("https://github.com/loophole/cli/releases/tag/%s", availableVersion.Version)) + browser.OpenURL(dlLink) } } } } +func getDownloadLink(availableVersion string) string { + archiveExt := ".tar.gz" + arch := runtime.GOARCH + if arch == "windows" { + archiveExt = ".zip" + } else if arch == "darwin" { + arch = "macos" + } + if arch == "amd64" { + arch = "64bit" + } else if arch == "386" { + arch = "32bit" + } else { + communication.Error("There was an error detecting your system architecture.") //if arch is unexpected, only link to the release page + return fmt.Sprintf("https://github.com/loophole/cli/releases/tag/%s", availableVersion) + } + res := fmt.Sprintf("https://github.com/loophole/cli/releases/download/%s/loophole-desktop_%s_%s_%s%s", availableVersion, availableVersion, runtime.GOOS, arch, archiveExt) + fmt.Println(res) + return res +} + func remindAgainCheck() (bool, error) { //TODO: Error handling, moving this function to a more appropriate file (probably to config pkg) home, err := homedir.Dir() if err != nil { From e8026c90b6900941a54a164515953f69c878dc6d Mon Sep 17 00:00:00 2001 From: Denny Date: Mon, 7 Jun 2021 10:16:13 +0200 Subject: [PATCH 05/13] Move updatecheck to pkg --- cmd/http.go | 3 +- cmd/path.go | 3 +- cmd/webdav.go | 3 +- internal/app/loophole/loophole.go | 92 --------------------- internal/pkg/updatecheck/updatecheck.go | 105 ++++++++++++++++++++++++ ui/ui.go | 3 +- 6 files changed, 113 insertions(+), 96 deletions(-) create mode 100644 internal/pkg/updatecheck/updatecheck.go diff --git a/cmd/http.go b/cmd/http.go index 81439f5..6eddf73 100644 --- a/cmd/http.go +++ b/cmd/http.go @@ -11,6 +11,7 @@ import ( lm "github.com/loophole/cli/internal/app/loophole/models" "github.com/loophole/cli/internal/pkg/communication" "github.com/loophole/cli/internal/pkg/token" + "github.com/loophole/cli/internal/pkg/updatecheck" "github.com/spf13/cobra" ) @@ -29,7 +30,7 @@ To expose port running on some local host e.g. 192.168.1.20 use 'loophole http < idToken := token.GetIdToken() communication.ApplicationStart(loggedIn, idToken) - loophole.CheckVersion() + updatecheck.CheckVersion() localEndpointSpecs.Host = "127.0.0.1" if len(args) > 1 { diff --git a/cmd/path.go b/cmd/path.go index cac9193..bb509c2 100644 --- a/cmd/path.go +++ b/cmd/path.go @@ -9,6 +9,7 @@ import ( lm "github.com/loophole/cli/internal/app/loophole/models" "github.com/loophole/cli/internal/pkg/communication" "github.com/loophole/cli/internal/pkg/token" + "github.com/loophole/cli/internal/pkg/updatecheck" "github.com/spf13/cobra" ) @@ -26,7 +27,7 @@ To expose local directory (e.g. /data/my-data) simply use 'loophole path /data/m idToken := token.GetIdToken() communication.ApplicationStart(loggedIn, idToken) - loophole.CheckVersion() + updatecheck.CheckVersion() dirEndpointSpecs.Path = args[0] quitChannel := make(chan bool) diff --git a/cmd/webdav.go b/cmd/webdav.go index 7fc3e33..9d1b019 100644 --- a/cmd/webdav.go +++ b/cmd/webdav.go @@ -9,6 +9,7 @@ import ( lm "github.com/loophole/cli/internal/app/loophole/models" "github.com/loophole/cli/internal/pkg/communication" "github.com/loophole/cli/internal/pkg/token" + "github.com/loophole/cli/internal/pkg/updatecheck" "github.com/spf13/cobra" ) @@ -28,7 +29,7 @@ To expose local directory via webdav (e.g. /data/my-data) simply use 'loophole w idToken := token.GetIdToken() communication.ApplicationStart(loggedIn, idToken) - loophole.CheckVersion() + updatecheck.CheckVersion() webdavEndpointSpecs.Path = args[0] quitChannel := make(chan bool) diff --git a/internal/app/loophole/loophole.go b/internal/app/loophole/loophole.go index d43c0ca..6f63d1c 100644 --- a/internal/app/loophole/loophole.go +++ b/internal/app/loophole/loophole.go @@ -5,10 +5,8 @@ import ( "io" "net" "net/http" - "runtime" "time" - "github.com/blang/semver/v4" "github.com/loophole/cli/config" lm "github.com/loophole/cli/internal/app/loophole/models" "github.com/loophole/cli/internal/pkg/apiclient" @@ -16,10 +14,6 @@ import ( "github.com/loophole/cli/internal/pkg/httpserver" "github.com/loophole/cli/internal/pkg/keys" "github.com/loophole/cli/internal/pkg/urlmaker" - "github.com/mitchellh/go-homedir" - "github.com/ncruces/zenity" - "github.com/pkg/browser" - "github.com/spf13/viper" "golang.org/x/crypto/ssh" ) @@ -41,92 +35,6 @@ var remoteEndpoint = lm.Endpoint{ Port: 80, } -func CheckVersion() { - availableVersion, err := apiclient.GetLatestAvailableVersion() - if err != nil { - communication.Debug("There was a problem obtaining info response, skipping further checking") - return - } - currentVersionParsed, err := semver.Make(config.Config.Version) - if err != nil { - communication.Debug(fmt.Sprintf("Cannot parse current version '%s' as semver version, skipping further checking", config.Config.Version)) - return - } - availableVersionParsed, err := semver.Make(availableVersion.Version) - if err != nil { - communication.Debug(fmt.Sprintf("Cannot parse available version '%s' as semver version, skipping further checking", availableVersion)) - return - } - if currentVersionParsed.LT(availableVersionParsed) { - if config.Config.ClientMode == "cli" { - communication.NewVersionAvailable(availableVersion.Version) - } else { - remind, _ := remindAgainCheck() - if !remind { - return - } - dlLink := getDownloadLink(availableVersion.Version) - response, _ := zenity.Question(fmt.Sprintf("A new version is available for you at \n%s \nDo you want to open the link in your browser now?", dlLink), zenity.NoWrap(), zenity.Title("New version available!")) - if response { - browser.OpenURL(dlLink) - } - } - } -} - -func getDownloadLink(availableVersion string) string { - archiveExt := ".tar.gz" - arch := runtime.GOARCH - if arch == "windows" { - archiveExt = ".zip" - } else if arch == "darwin" { - arch = "macos" - } - if arch == "amd64" { - arch = "64bit" - } else if arch == "386" { - arch = "32bit" - } else { - communication.Error("There was an error detecting your system architecture.") //if arch is unexpected, only link to the release page - return fmt.Sprintf("https://github.com/loophole/cli/releases/tag/%s", availableVersion) - } - res := fmt.Sprintf("https://github.com/loophole/cli/releases/download/%s/loophole-desktop_%s_%s_%s%s", availableVersion, availableVersion, runtime.GOOS, arch, archiveExt) - fmt.Println(res) - return res -} - -func remindAgainCheck() (bool, error) { //TODO: Error handling, moving this function to a more appropriate file (probably to config pkg) - home, err := homedir.Dir() - if err != nil { - return true, err - } - - layout := "2006-02-01" //golangs arcane time format string - viper.SetDefault("last-reminder", time.Time{}.Format(layout)) //zero value for time - viper.SetConfigName("config") // name of config file (without extension) - viper.SetConfigType("json") // REQUIRED if the config file does not have the extension in the name - viper.AddConfigPath(fmt.Sprintf("%s/.loophole/", home)) - if err := viper.ReadInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { //create a config if none exist yet - viper.WriteConfigAs(fmt.Sprintf("%s/.loophole/config.json", home)) - } else { - return true, err - } - } - - t, err := time.Parse(layout, fmt.Sprintf("%v", viper.Get("last-reminder"))) - if err != nil { - return true, err - } - if (t.Year() < time.Now().Year()) || (t.YearDay() < time.Now().YearDay()) { //check if reminder has been done today - viper.Set("last-reminder", time.Now().Format(layout)) - viper.WriteConfigAs("/home/work/.loophole/config.json") - return true, nil - } - - return false, nil -} - func handleClient(tunnelID string, client net.Conn, local net.Conn) { defer client.Close() chDone := make(chan bool) diff --git a/internal/pkg/updatecheck/updatecheck.go b/internal/pkg/updatecheck/updatecheck.go new file mode 100644 index 0000000..86e4ef1 --- /dev/null +++ b/internal/pkg/updatecheck/updatecheck.go @@ -0,0 +1,105 @@ +package updatecheck + +import ( + "fmt" + "runtime" + "time" + + "github.com/blang/semver/v4" + "github.com/loophole/cli/config" + "github.com/loophole/cli/internal/pkg/apiclient" + "github.com/loophole/cli/internal/pkg/communication" + "github.com/mitchellh/go-homedir" + "github.com/ncruces/zenity" + "github.com/pkg/browser" + "github.com/spf13/viper" +) + +func CheckVersion() { + availableVersion, err := apiclient.GetLatestAvailableVersion() + if err != nil { + communication.Debug("There was a problem obtaining info response, skipping further checking") + return + } + currentVersionParsed, err := semver.Make(config.Config.Version) + if err != nil { + communication.Debug(fmt.Sprintf("Cannot parse current version '%s' as semver version, skipping further checking", config.Config.Version)) + return + } + availableVersionParsed, err := semver.Make(availableVersion.Version) + if err != nil { + communication.Debug(fmt.Sprintf("Cannot parse available version '%s' as semver version, skipping further checking", availableVersion)) + return + } + if currentVersionParsed.LT(availableVersionParsed) { + if config.Config.ClientMode == "cli" { + communication.NewVersionAvailable(availableVersion.Version) + } else { + remind, err := remindAgainCheck() + if err != nil { + communication.Error(err.Error()) //errors in retrieving a download link should be noted, but not interrupt the program + } + if !remind { + return + } + dlLink := getDownloadLink(availableVersion.Version) + response, _ := zenity.Question(fmt.Sprintf("A new version is available for you at \n%s \nDo you want to open the link in your browser now?", dlLink), zenity.NoWrap(), zenity.Title("New version available!")) + if response { + browser.OpenURL(dlLink) + } + } + } +} + +func getDownloadLink(availableVersion string) string { + archiveExt := ".tar.gz" + arch := runtime.GOARCH + if arch == "windows" { + archiveExt = ".zip" + } else if arch == "darwin" { + arch = "macos" + } + if arch == "amd64" { + arch = "64bit" + } else if arch == "386" { + arch = "32bit" + } else { + communication.Error("There was an error detecting your system architecture.") //if arch is unexpected, only link to the release page + return fmt.Sprintf("https://github.com/loophole/cli/releases/tag/%s", availableVersion) + } + res := fmt.Sprintf("https://github.com/loophole/cli/releases/download/%s/loophole-desktop_%s_%s_%s%s", availableVersion, availableVersion, runtime.GOOS, arch, archiveExt) + fmt.Println(res) + return res +} + +func remindAgainCheck() (bool, error) { + home, err := homedir.Dir() + if err != nil { + return true, err + } + + layout := "2006-02-01" //golangs arcane time format string + viper.SetDefault("last-reminder", time.Time{}.Format(layout)) //zero value for time + viper.SetConfigName("config") // name of config file (without extension) + viper.SetConfigType("json") // REQUIRED if the config file does not have the extension in the name + viper.AddConfigPath(fmt.Sprintf("%s/.loophole/", home)) + if err := viper.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { //create a config if none exist yet + viper.WriteConfigAs(fmt.Sprintf("%s/.loophole/config.json", home)) + } else { + return true, err + } + } + + t, err := time.Parse(layout, fmt.Sprintf("%v", viper.Get("last-reminder"))) + if err != nil { + return true, err + } + if (t.Year() < time.Now().Year()) || (t.YearDay() < time.Now().YearDay()) { //check if reminder has been done today + viper.Set("last-reminder", time.Now().Format(layout)) + viper.WriteConfigAs("/home/work/.loophole/config.json") + return true, nil + } + + return false, nil +} diff --git a/ui/ui.go b/ui/ui.go index 7bb9006..78241f0 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -21,6 +21,7 @@ import ( "github.com/loophole/cli/internal/pkg/cache" "github.com/loophole/cli/internal/pkg/communication" "github.com/loophole/cli/internal/pkg/token" + "github.com/loophole/cli/internal/pkg/updatecheck" ) var upgrader = websocket.Upgrader{} // use default options @@ -259,7 +260,7 @@ func Display() { } defer ui.Close() - loophole.CheckVersion() + updatecheck.CheckVersion() <-ui.Done() } From 52fd658b154bfe6f2bcdae174b666cfcacae6c8e Mon Sep 17 00:00:00 2001 From: Denny Date: Mon, 7 Jun 2021 13:03:25 +0200 Subject: [PATCH 06/13] Rename variables, replace prompt with notify --- internal/pkg/updatecheck/updatecheck.go | 38 ++++++++++++------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/internal/pkg/updatecheck/updatecheck.go b/internal/pkg/updatecheck/updatecheck.go index 86e4ef1..a6b2bd5 100644 --- a/internal/pkg/updatecheck/updatecheck.go +++ b/internal/pkg/updatecheck/updatecheck.go @@ -11,7 +11,6 @@ import ( "github.com/loophole/cli/internal/pkg/communication" "github.com/mitchellh/go-homedir" "github.com/ncruces/zenity" - "github.com/pkg/browser" "github.com/spf13/viper" ) @@ -42,34 +41,35 @@ func CheckVersion() { if !remind { return } - dlLink := getDownloadLink(availableVersion.Version) - response, _ := zenity.Question(fmt.Sprintf("A new version is available for you at \n%s \nDo you want to open the link in your browser now?", dlLink), zenity.NoWrap(), zenity.Title("New version available!")) - if response { - browser.OpenURL(dlLink) + downloadlink := getDownloadLink(availableVersion.Version) + err = zenity.Notify(fmt.Sprintf("A new version is available for you at \n%s \n", downloadlink), zenity.Title("New version available!")) + if err != nil { + communication.Debug(err.Error()) //errors in showing a download link should be noted, but not interrupt the program } } } } func getDownloadLink(availableVersion string) string { - archiveExt := ".tar.gz" - arch := runtime.GOARCH - if arch == "windows" { - archiveExt = ".zip" - } else if arch == "darwin" { - arch = "macos" + archiveType := ".tar.gz" + operatingSystem := runtime.GOOS + architecture := runtime.GOARCH + if operatingSystem == "windows" { + archiveType = ".zip" + } else if operatingSystem == "darwin" { + operatingSystem = "macos" //rename for use in downloadlink } - if arch == "amd64" { - arch = "64bit" - } else if arch == "386" { - arch = "32bit" + if architecture == "amd64" { + architecture = "64bit" + } else if architecture == "386" { + architecture = "32bit" } else { communication.Error("There was an error detecting your system architecture.") //if arch is unexpected, only link to the release page return fmt.Sprintf("https://github.com/loophole/cli/releases/tag/%s", availableVersion) } - res := fmt.Sprintf("https://github.com/loophole/cli/releases/download/%s/loophole-desktop_%s_%s_%s%s", availableVersion, availableVersion, runtime.GOOS, arch, archiveExt) - fmt.Println(res) - return res + link := fmt.Sprintf("https://github.com/loophole/cli/releases/download/%s/loophole-desktop_%s_%s_%s%s", availableVersion, availableVersion, operatingSystem, operatingSystem, archiveType) + fmt.Println(link) + return link } func remindAgainCheck() (bool, error) { @@ -81,7 +81,7 @@ func remindAgainCheck() (bool, error) { layout := "2006-02-01" //golangs arcane time format string viper.SetDefault("last-reminder", time.Time{}.Format(layout)) //zero value for time viper.SetConfigName("config") // name of config file (without extension) - viper.SetConfigType("json") // REQUIRED if the config file does not have the extension in the name + viper.SetConfigType("json") viper.AddConfigPath(fmt.Sprintf("%s/.loophole/", home)) if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { //create a config if none exist yet From b416b0c707ab3e4f5831fd63871fa5ca7f934e36 Mon Sep 17 00:00:00 2001 From: Denny Date: Thu, 10 Jun 2021 12:23:14 +0200 Subject: [PATCH 07/13] Switch notification behavior, Use direct viper getters, Fix download link --- internal/pkg/updatecheck/updatecheck.go | 46 +++++++++++++++++-------- 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/internal/pkg/updatecheck/updatecheck.go b/internal/pkg/updatecheck/updatecheck.go index a6b2bd5..2d09968 100644 --- a/internal/pkg/updatecheck/updatecheck.go +++ b/internal/pkg/updatecheck/updatecheck.go @@ -11,6 +11,7 @@ import ( "github.com/loophole/cli/internal/pkg/communication" "github.com/mitchellh/go-homedir" "github.com/ncruces/zenity" + "github.com/pkg/browser" "github.com/spf13/viper" ) @@ -34,7 +35,7 @@ func CheckVersion() { if config.Config.ClientMode == "cli" { communication.NewVersionAvailable(availableVersion.Version) } else { - remind, err := remindAgainCheck() + remind, usePrompt, err := remindAgainCheck(availableVersion.Version) if err != nil { communication.Error(err.Error()) //errors in retrieving a download link should be noted, but not interrupt the program } @@ -42,7 +43,15 @@ func CheckVersion() { return } downloadlink := getDownloadLink(availableVersion.Version) - err = zenity.Notify(fmt.Sprintf("A new version is available for you at \n%s \n", downloadlink), zenity.Title("New version available!")) + if usePrompt { + openLink := false + openLink, err = zenity.Question(fmt.Sprintf("A new version is available for you at \n%s \n Do you want to open this link in your browser?", downloadlink), zenity.NoWrap(), zenity.Title("New version available!")) + if openLink { + browser.OpenURL(downloadlink) + } + } else { + err = zenity.Notify(fmt.Sprintf("A new version is available for you at \n%s \n", downloadlink), zenity.Title("New version available!")) + } if err != nil { communication.Debug(err.Error()) //errors in showing a download link should be noted, but not interrupt the program } @@ -67,39 +76,46 @@ func getDownloadLink(availableVersion string) string { communication.Error("There was an error detecting your system architecture.") //if arch is unexpected, only link to the release page return fmt.Sprintf("https://github.com/loophole/cli/releases/tag/%s", availableVersion) } - link := fmt.Sprintf("https://github.com/loophole/cli/releases/download/%s/loophole-desktop_%s_%s_%s%s", availableVersion, availableVersion, operatingSystem, operatingSystem, archiveType) + link := fmt.Sprintf("https://github.com/loophole/cli/releases/download/%s/loophole-desktop_%s_%s_%s%s", availableVersion, availableVersion, operatingSystem, architecture, archiveType) fmt.Println(link) return link } -func remindAgainCheck() (bool, error) { +func remindAgainCheck(availableVersion string) (bool, bool, error) { home, err := homedir.Dir() if err != nil { - return true, err + return true, false, err } - layout := "2006-02-01" //golangs arcane time format string - viper.SetDefault("last-reminder", time.Time{}.Format(layout)) //zero value for time - viper.SetConfigName("config") // name of config file (without extension) + viper.SetDefault("lastreminder", time.Time{}) //date of last reminder, default zero value for time + viper.SetDefault("availableversion", "1.0.0-beta.14") //TODO: last seen latest available version + viper.SetDefault("remindercount", 3) //counts to zero, then switches from prompt to notification reminder + viper.SetConfigName("config") // name of config file (without extension) viper.SetConfigType("json") viper.AddConfigPath(fmt.Sprintf("%s/.loophole/", home)) if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { //create a config if none exist yet viper.WriteConfigAs(fmt.Sprintf("%s/.loophole/config.json", home)) } else { - return true, err + return true, false, err } } - t, err := time.Parse(layout, fmt.Sprintf("%v", viper.Get("last-reminder"))) if err != nil { - return true, err + return true, false, err } - if (t.Year() < time.Now().Year()) || (t.YearDay() < time.Now().YearDay()) { //check if reminder has been done today - viper.Set("last-reminder", time.Now().Format(layout)) + lastReminder := viper.GetTime("lastreminder") + if (lastReminder.Year() < time.Now().Year()) || (lastReminder.YearDay() < time.Now().YearDay()) { //check if reminder has been done today + viper.Set("lastreminder", time.Now()) viper.WriteConfigAs("/home/work/.loophole/config.json") - return true, nil + if viper.GetInt("remindercount") < 1 { + return true, false, nil + } else { + viper.Set("remindercount", viper.GetInt("remindercount")-1) + viper.WriteConfigAs("/home/work/.loophole/config.json") + return true, true, nil + } } - return false, nil + return false, false, nil } From 4b09154933b2ab0cf27655e309558c880aed7bdd Mon Sep 17 00:00:00 2001 From: Denny Date: Thu, 10 Jun 2021 12:54:39 +0200 Subject: [PATCH 08/13] Reset reminder when newer version is out --- internal/pkg/updatecheck/updatecheck.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/internal/pkg/updatecheck/updatecheck.go b/internal/pkg/updatecheck/updatecheck.go index 2d09968..2135010 100644 --- a/internal/pkg/updatecheck/updatecheck.go +++ b/internal/pkg/updatecheck/updatecheck.go @@ -35,7 +35,7 @@ func CheckVersion() { if config.Config.ClientMode == "cli" { communication.NewVersionAvailable(availableVersion.Version) } else { - remind, usePrompt, err := remindAgainCheck(availableVersion.Version) + remind, usePrompt, err := remindAgainCheck(availableVersionParsed) if err != nil { communication.Error(err.Error()) //errors in retrieving a download link should be noted, but not interrupt the program } @@ -81,14 +81,14 @@ func getDownloadLink(availableVersion string) string { return link } -func remindAgainCheck(availableVersion string) (bool, bool, error) { +func remindAgainCheck(availableVersionParsed semver.Version) (bool, bool, error) { home, err := homedir.Dir() if err != nil { return true, false, err } viper.SetDefault("lastreminder", time.Time{}) //date of last reminder, default zero value for time - viper.SetDefault("availableversion", "1.0.0-beta.14") //TODO: last seen latest available version + viper.SetDefault("availableversion", "1.0.0-beta.14") //last seen latest version viper.SetDefault("remindercount", 3) //counts to zero, then switches from prompt to notification reminder viper.SetConfigName("config") // name of config file (without extension) viper.SetConfigType("json") @@ -101,6 +101,11 @@ func remindAgainCheck(availableVersion string) (bool, bool, error) { } } + lastSeenLatestVersion, err := semver.Make(viper.GetString("availableversion")) + if availableVersionParsed.GT(lastSeenLatestVersion) { //reset reminder count if new version is out + viper.Set("availableversion", availableVersionParsed.String()) + viper.Set("remindercount", 3) + } if err != nil { return true, false, err } From 60435ede7808b7a261e8a9222c893c29e9f3e461 Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 11 Jun 2021 12:39:55 +0200 Subject: [PATCH 09/13] Rename CheckVersion to CheckForUpdates --- cmd/http.go | 2 +- cmd/path.go | 2 +- cmd/webdav.go | 2 +- internal/pkg/updatecheck/updatecheck.go | 12 ++++++------ ui/ui.go | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/http.go b/cmd/http.go index 6eddf73..e9b49fd 100644 --- a/cmd/http.go +++ b/cmd/http.go @@ -30,7 +30,7 @@ To expose port running on some local host e.g. 192.168.1.20 use 'loophole http < idToken := token.GetIdToken() communication.ApplicationStart(loggedIn, idToken) - updatecheck.CheckVersion() + updatecheck.CheckForUpdates() localEndpointSpecs.Host = "127.0.0.1" if len(args) > 1 { diff --git a/cmd/path.go b/cmd/path.go index bb509c2..9106f46 100644 --- a/cmd/path.go +++ b/cmd/path.go @@ -27,7 +27,7 @@ To expose local directory (e.g. /data/my-data) simply use 'loophole path /data/m idToken := token.GetIdToken() communication.ApplicationStart(loggedIn, idToken) - updatecheck.CheckVersion() + updatecheck.CheckForUpdates() dirEndpointSpecs.Path = args[0] quitChannel := make(chan bool) diff --git a/cmd/webdav.go b/cmd/webdav.go index 9d1b019..e162531 100644 --- a/cmd/webdav.go +++ b/cmd/webdav.go @@ -29,7 +29,7 @@ To expose local directory via webdav (e.g. /data/my-data) simply use 'loophole w idToken := token.GetIdToken() communication.ApplicationStart(loggedIn, idToken) - updatecheck.CheckVersion() + updatecheck.CheckForUpdates() webdavEndpointSpecs.Path = args[0] quitChannel := make(chan bool) diff --git a/internal/pkg/updatecheck/updatecheck.go b/internal/pkg/updatecheck/updatecheck.go index 2135010..636f8d1 100644 --- a/internal/pkg/updatecheck/updatecheck.go +++ b/internal/pkg/updatecheck/updatecheck.go @@ -15,7 +15,7 @@ import ( "github.com/spf13/viper" ) -func CheckVersion() { +func CheckForUpdates() { availableVersion, err := apiclient.GetLatestAvailableVersion() if err != nil { communication.Debug("There was a problem obtaining info response, skipping further checking") @@ -37,14 +37,14 @@ func CheckVersion() { } else { remind, usePrompt, err := remindAgainCheck(availableVersionParsed) if err != nil { - communication.Error(err.Error()) //errors in retrieving a download link should be noted, but not interrupt the program + communication.Error(err.Error()) //errors in determining the type reminder should be noted, but not interrupt the program } if !remind { return } downloadlink := getDownloadLink(availableVersion.Version) - if usePrompt { - openLink := false + if usePrompt { //either use a notification that the user needs to click away, or use a notification they can ignore + openLink := false //needs to be declared here instead of below with := so we can still have access to err outside of this scope openLink, err = zenity.Question(fmt.Sprintf("A new version is available for you at \n%s \n Do you want to open this link in your browser?", downloadlink), zenity.NoWrap(), zenity.Title("New version available!")) if openLink { browser.OpenURL(downloadlink) @@ -66,7 +66,7 @@ func getDownloadLink(availableVersion string) string { if operatingSystem == "windows" { archiveType = ".zip" } else if operatingSystem == "darwin" { - operatingSystem = "macos" //rename for use in downloadlink + operatingSystem = "macos" //rename for use in download url } if architecture == "amd64" { architecture = "64bit" @@ -90,7 +90,7 @@ func remindAgainCheck(availableVersionParsed semver.Version) (bool, bool, error) viper.SetDefault("lastreminder", time.Time{}) //date of last reminder, default zero value for time viper.SetDefault("availableversion", "1.0.0-beta.14") //last seen latest version viper.SetDefault("remindercount", 3) //counts to zero, then switches from prompt to notification reminder - viper.SetConfigName("config") // name of config file (without extension) + viper.SetConfigName("config") //name of config file (without extension) viper.SetConfigType("json") viper.AddConfigPath(fmt.Sprintf("%s/.loophole/", home)) if err := viper.ReadInConfig(); err != nil { diff --git a/ui/ui.go b/ui/ui.go index 78241f0..6b147ed 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -260,7 +260,7 @@ func Display() { } defer ui.Close() - updatecheck.CheckVersion() + updatecheck.CheckForUpdates() <-ui.Done() } From fdd4e7d15e9dfa6d2b2b2b17a2dca688c4874583 Mon Sep 17 00:00:00 2001 From: Denny Date: Mon, 14 Jun 2021 10:49:04 +0200 Subject: [PATCH 10/13] Fix wrong config path, Adjust download link --- internal/pkg/updatecheck/updatecheck.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/internal/pkg/updatecheck/updatecheck.go b/internal/pkg/updatecheck/updatecheck.go index 636f8d1..990a96f 100644 --- a/internal/pkg/updatecheck/updatecheck.go +++ b/internal/pkg/updatecheck/updatecheck.go @@ -42,15 +42,16 @@ func CheckForUpdates() { if !remind { return } - downloadlink := getDownloadLink(availableVersion.Version) if usePrompt { //either use a notification that the user needs to click away, or use a notification they can ignore + downloadlink := getDownloadLink(availableVersion.Version) openLink := false //needs to be declared here instead of below with := so we can still have access to err outside of this scope openLink, err = zenity.Question(fmt.Sprintf("A new version is available for you at \n%s \n Do you want to open this link in your browser?", downloadlink), zenity.NoWrap(), zenity.Title("New version available!")) if openLink { browser.OpenURL(downloadlink) } } else { - err = zenity.Notify(fmt.Sprintf("A new version is available for you at \n%s \n", downloadlink), zenity.Title("New version available!")) + downloadlink := "https://loophole.cloud/download" //this notification isn't clickable, so the link should be something the user can remember + err = zenity.Notify(fmt.Sprintf("A new version is available for you, please visit \n%s \n", downloadlink), zenity.Title("New version available!")) } if err != nil { communication.Debug(err.Error()) //errors in showing a download link should be noted, but not interrupt the program @@ -112,12 +113,18 @@ func remindAgainCheck(availableVersionParsed semver.Version) (bool, bool, error) lastReminder := viper.GetTime("lastreminder") if (lastReminder.Year() < time.Now().Year()) || (lastReminder.YearDay() < time.Now().YearDay()) { //check if reminder has been done today viper.Set("lastreminder", time.Now()) - viper.WriteConfigAs("/home/work/.loophole/config.json") + err = viper.WriteConfigAs(fmt.Sprintf("%s/.loophole/config.json", home)) + if err != nil { + return true, false, err + } if viper.GetInt("remindercount") < 1 { return true, false, nil } else { viper.Set("remindercount", viper.GetInt("remindercount")-1) - viper.WriteConfigAs("/home/work/.loophole/config.json") + err = viper.WriteConfigAs(fmt.Sprintf("%s/.loophole/config.json", home)) + if err != nil { + return true, false, err + } return true, true, nil } } From ca949a35a682158c25965d1f261db4ff174b6a90 Mon Sep 17 00:00:00 2001 From: Denny Date: Fri, 18 Jun 2021 10:48:24 +0200 Subject: [PATCH 11/13] Move viper to config pkg functions --- cmd/root.go | 5 +++ config/config.go | 41 +++++++++++++++++++++++++ internal/pkg/updatecheck/updatecheck.go | 24 ++------------- ui/ui.go | 5 +++ 4 files changed, 53 insertions(+), 22 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index a99feff..c8853f8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -10,6 +10,7 @@ import ( "github.com/loophole/cli/config" "github.com/loophole/cli/internal/pkg/cache" + "github.com/loophole/cli/internal/pkg/communication" "github.com/mattn/go-colorable" "github.com/rs/zerolog" "github.com/rs/zerolog/log" @@ -56,6 +57,10 @@ func initLogger() { // Execute runs command parsing chain func Execute() { rootCmd.Version = fmt.Sprintf("%s (%s)", config.Config.Version, config.Config.CommitHash) + err := config.SetupViperConfig() + if err != nil { + communication.Error(fmt.Sprintf("Error while setting up viper: %s", err.Error())) + } if err := rootCmd.Execute(); err != nil { os.Exit(1) diff --git a/config/config.go b/config/config.go index 122efc8..9179fe8 100644 --- a/config/config.go +++ b/config/config.go @@ -1,7 +1,12 @@ package config import ( + "fmt" + "time" + "github.com/loophole/cli/internal/app/loophole/models" + "github.com/mitchellh/go-homedir" + "github.com/spf13/viper" ) // OAuthConfig defined OAuth settings shape @@ -33,3 +38,39 @@ type ApplicationConfig struct { APIEndpoint models.Endpoint `json:"apiConfig"` GatewayEndpoint models.Endpoint `json:"gatewayConfig"` } + +func SetupViperConfig() error { + home, err := homedir.Dir() + if err != nil { + return err + } + viper.SetDefault("lastreminder", time.Time{}) //date of last reminder, default is zero value for time + viper.SetDefault("availableversion", "1.0.0-beta.14") //last seen latest version + viper.SetDefault("remindercount", 3) //counts to zero, then switches from prompt to notification reminder + viper.SetConfigName("config") + viper.SetConfigType("json") + viper.AddConfigPath(fmt.Sprintf("%s/.loophole/", home)) + if err := viper.ReadInConfig(); err != nil { + if _, ok := err.(viper.ConfigFileNotFoundError); ok { //create a config if none exist yet + err = SaveViperConfig() + if err != nil { + return err + } + } else { + return err + } + } + return nil +} + +func SaveViperConfig() error { + home, err := homedir.Dir() + if err != nil { + return err + } + err = viper.WriteConfigAs(fmt.Sprintf("%s/.loophole/config.json", home)) + if err != nil { + return err + } + return nil +} diff --git a/internal/pkg/updatecheck/updatecheck.go b/internal/pkg/updatecheck/updatecheck.go index 990a96f..33e71fc 100644 --- a/internal/pkg/updatecheck/updatecheck.go +++ b/internal/pkg/updatecheck/updatecheck.go @@ -9,7 +9,6 @@ import ( "github.com/loophole/cli/config" "github.com/loophole/cli/internal/pkg/apiclient" "github.com/loophole/cli/internal/pkg/communication" - "github.com/mitchellh/go-homedir" "github.com/ncruces/zenity" "github.com/pkg/browser" "github.com/spf13/viper" @@ -83,25 +82,6 @@ func getDownloadLink(availableVersion string) string { } func remindAgainCheck(availableVersionParsed semver.Version) (bool, bool, error) { - home, err := homedir.Dir() - if err != nil { - return true, false, err - } - - viper.SetDefault("lastreminder", time.Time{}) //date of last reminder, default zero value for time - viper.SetDefault("availableversion", "1.0.0-beta.14") //last seen latest version - viper.SetDefault("remindercount", 3) //counts to zero, then switches from prompt to notification reminder - viper.SetConfigName("config") //name of config file (without extension) - viper.SetConfigType("json") - viper.AddConfigPath(fmt.Sprintf("%s/.loophole/", home)) - if err := viper.ReadInConfig(); err != nil { - if _, ok := err.(viper.ConfigFileNotFoundError); ok { //create a config if none exist yet - viper.WriteConfigAs(fmt.Sprintf("%s/.loophole/config.json", home)) - } else { - return true, false, err - } - } - lastSeenLatestVersion, err := semver.Make(viper.GetString("availableversion")) if availableVersionParsed.GT(lastSeenLatestVersion) { //reset reminder count if new version is out viper.Set("availableversion", availableVersionParsed.String()) @@ -113,7 +93,7 @@ func remindAgainCheck(availableVersionParsed semver.Version) (bool, bool, error) lastReminder := viper.GetTime("lastreminder") if (lastReminder.Year() < time.Now().Year()) || (lastReminder.YearDay() < time.Now().YearDay()) { //check if reminder has been done today viper.Set("lastreminder", time.Now()) - err = viper.WriteConfigAs(fmt.Sprintf("%s/.loophole/config.json", home)) + err = config.SaveViperConfig() if err != nil { return true, false, err } @@ -121,7 +101,7 @@ func remindAgainCheck(availableVersionParsed semver.Version) (bool, bool, error) return true, false, nil } else { viper.Set("remindercount", viper.GetInt("remindercount")-1) - err = viper.WriteConfigAs(fmt.Sprintf("%s/.loophole/config.json", home)) + err = config.SaveViperConfig() if err != nil { return true, false, err } diff --git a/ui/ui.go b/ui/ui.go index 6b147ed..6efb784 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -16,6 +16,7 @@ import ( "github.com/gorilla/websocket" + "github.com/loophole/cli/config" "github.com/loophole/cli/internal/app/loophole" lm "github.com/loophole/cli/internal/app/loophole/models" "github.com/loophole/cli/internal/pkg/cache" @@ -235,6 +236,10 @@ func websocketHandler(w http.ResponseWriter, r *http.Request) { // Display shows the main app window func Display() { + err := config.SetupViperConfig() + if err != nil { + communication.Error(fmt.Sprintf("Error while setting up viper: %s", err.Error())) + } chromeLocation := lorca.LocateChrome() if chromeLocation == "" { message := "Chrome/Chromium >= 70 is required." From 9c65ea2437af1810c8152092acc91e899550efe6 Mon Sep 17 00:00:00 2001 From: Denny Date: Tue, 6 Jul 2021 10:36:15 +0200 Subject: [PATCH 12/13] Apply review suggestions --- config/config.go | 6 +++--- internal/pkg/updatecheck/updatecheck.go | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/config/config.go b/config/config.go index 9179fe8..6eaa757 100644 --- a/config/config.go +++ b/config/config.go @@ -44,9 +44,9 @@ func SetupViperConfig() error { if err != nil { return err } - viper.SetDefault("lastreminder", time.Time{}) //date of last reminder, default is zero value for time - viper.SetDefault("availableversion", "1.0.0-beta.14") //last seen latest version - viper.SetDefault("remindercount", 3) //counts to zero, then switches from prompt to notification reminder + viper.SetDefault("lastreminder", time.Time{}) //date of last reminder, default is zero value for time + viper.SetDefault("availableversion", Config.Version) //last seen latest version + viper.SetDefault("remindercount", 3) //counts to zero, then switches from prompt to notification reminder viper.SetConfigName("config") viper.SetConfigType("json") viper.AddConfigPath(fmt.Sprintf("%s/.loophole/", home)) diff --git a/internal/pkg/updatecheck/updatecheck.go b/internal/pkg/updatecheck/updatecheck.go index 33e71fc..48182d1 100644 --- a/internal/pkg/updatecheck/updatecheck.go +++ b/internal/pkg/updatecheck/updatecheck.go @@ -77,7 +77,6 @@ func getDownloadLink(availableVersion string) string { return fmt.Sprintf("https://github.com/loophole/cli/releases/tag/%s", availableVersion) } link := fmt.Sprintf("https://github.com/loophole/cli/releases/download/%s/loophole-desktop_%s_%s_%s%s", availableVersion, availableVersion, operatingSystem, architecture, archiveType) - fmt.Println(link) return link } From a4559132772f7d3dc5049b7707136f8103c09fe2 Mon Sep 17 00:00:00 2001 From: Denny Date: Tue, 13 Jul 2021 11:50:12 +0200 Subject: [PATCH 13/13] Separate logger from communication to prevent cyclic dependency --- config/config.go | 7 +- .../loophole/models/CommunicationMechanism.go | 40 ++++++++ internal/pkg/cache/cache.go | 10 +- internal/pkg/communication/communication.go | 94 ++++++------------- internal/pkg/communication/websocket.go | 11 ++- .../pkg/{communication => logger}/logger.go | 16 ++-- 6 files changed, 90 insertions(+), 88 deletions(-) create mode 100644 internal/app/loophole/models/CommunicationMechanism.go rename internal/pkg/{communication => logger}/logger.go (94%) diff --git a/config/config.go b/config/config.go index 6eaa757..bac41ad 100644 --- a/config/config.go +++ b/config/config.go @@ -5,6 +5,7 @@ import ( "time" "github.com/loophole/cli/internal/app/loophole/models" + "github.com/loophole/cli/internal/pkg/cache" "github.com/mitchellh/go-homedir" "github.com/spf13/viper" ) @@ -40,16 +41,12 @@ type ApplicationConfig struct { } func SetupViperConfig() error { - home, err := homedir.Dir() - if err != nil { - return err - } viper.SetDefault("lastreminder", time.Time{}) //date of last reminder, default is zero value for time viper.SetDefault("availableversion", Config.Version) //last seen latest version viper.SetDefault("remindercount", 3) //counts to zero, then switches from prompt to notification reminder viper.SetConfigName("config") viper.SetConfigType("json") - viper.AddConfigPath(fmt.Sprintf("%s/.loophole/", home)) + viper.AddConfigPath(cache.GetLocalStorageDir("config")) if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); ok { //create a config if none exist yet err = SaveViperConfig() diff --git a/internal/app/loophole/models/CommunicationMechanism.go b/internal/app/loophole/models/CommunicationMechanism.go new file mode 100644 index 0000000..853c2a0 --- /dev/null +++ b/internal/app/loophole/models/CommunicationMechanism.go @@ -0,0 +1,40 @@ +package models + +import authModels "github.com/loophole/cli/internal/pkg/token/models" + +// Mechanism is a type defining interface for loophole communication +type CommunicationMechanism interface { + Debug(message string) + Info(message string) + Warn(message string) + Error(message string) + Fatal(message string) + + TunnelDebug(tunnelID string, message string) + TunnelInfo(tunnelID string, message string) + TunnelWarn(tunnelID string, message string) + TunnelError(tunnelID string, message string) + + ApplicationStart(loggedIn bool, idToken string) + ApplicationStop(feedbackFormURL ...string) + + TunnelStart(tunnelID string) + + TunnelStartSuccess(remoteConfig RemoteEndpointSpecs, localEndpoint string, qr ...bool) + TunnelStartFailure(tunnelID string, err error) + + TunnelStopSuccess(tunnelID string) + + LoginStart(authModels.DeviceCodeSpec) + LoginSuccess(idToken string) + LoginFailure(err error) + + LogoutSuccess() + LogoutFailure(err error) + + LoadingStart(tunnelID string, loaderMessage string) + LoadingSuccess(tunnelID string) + LoadingFailure(tunnelID string, err error) + + NewVersionAvailable(availableVersion string) +} diff --git a/internal/pkg/cache/cache.go b/internal/pkg/cache/cache.go index 6d29b36..7497ccd 100644 --- a/internal/pkg/cache/cache.go +++ b/internal/pkg/cache/cache.go @@ -5,7 +5,7 @@ import ( "os" "path" - "github.com/loophole/cli/internal/pkg/communication" + "github.com/loophole/cli/internal/pkg/logger" "github.com/mitchellh/go-homedir" ) @@ -13,13 +13,13 @@ import ( func GetLocalStorageDir(directoryName string) string { home, err := homedir.Dir() if err != nil { - communication.Fatal(fmt.Sprintf("Error reading user home directory: %s", err.Error())) + logger.CommunicationMechanism.Fatal(fmt.Sprintf("Error reading user home directory: %s", err.Error())) } dirName := path.Join(home, ".loophole", directoryName) err = os.MkdirAll(dirName, os.ModePerm) if err != nil { - communication.Fatal(fmt.Sprintf("Error creating local cache directory: %s", err.Error())) + logger.CommunicationMechanism.Fatal(fmt.Sprintf("Error creating local cache directory: %s", err.Error())) } return dirName } @@ -28,12 +28,12 @@ func GetLocalStorageDir(directoryName string) string { func GetLocalStorageFile(fileName string, directoryName string) string { home, err := homedir.Dir() if err != nil { - communication.Fatal(fmt.Sprintf("Error reading user home directory: %s", err.Error())) + logger.CommunicationMechanism.Fatal(fmt.Sprintf("Error reading user home directory: %s", err.Error())) } dirName := path.Join(home, ".loophole", directoryName) err = os.MkdirAll(dirName, os.ModePerm) if err != nil { - communication.Fatal(fmt.Sprintf("Error creating local cache directory: %s", err.Error())) + logger.CommunicationMechanism.Fatal(fmt.Sprintf("Error creating local cache directory: %s", err.Error())) } return path.Join(dirName, fileName) diff --git a/internal/pkg/communication/communication.go b/internal/pkg/communication/communication.go index f435bf1..0580f32 100644 --- a/internal/pkg/communication/communication.go +++ b/internal/pkg/communication/communication.go @@ -1,148 +1,110 @@ package communication import ( + "github.com/loophole/cli/config" coreModels "github.com/loophole/cli/internal/app/loophole/models" + "github.com/loophole/cli/internal/pkg/logger" authModels "github.com/loophole/cli/internal/pkg/token/models" ) -var defaultLogger = NewStdOutLogger() -var communicationMechanism Mechanism = defaultLogger - -// Mechanism is a type defining interface for loophole communication -type Mechanism interface { - Debug(message string) - Info(message string) - Warn(message string) - Error(message string) - Fatal(message string) - - TunnelDebug(tunnelID string, message string) - TunnelInfo(tunnelID string, message string) - TunnelWarn(tunnelID string, message string) - TunnelError(tunnelID string, message string) - - ApplicationStart(loggedIn bool, idToken string) - ApplicationStop() - - TunnelStart(tunnelID string) - - TunnelStartSuccess(remoteConfig coreModels.RemoteEndpointSpecs, localEndpoint string) - TunnelStartFailure(tunnelID string, err error) - - TunnelStopSuccess(tunnelID string) - - LoginStart(authModels.DeviceCodeSpec) - LoginSuccess(idToken string) - LoginFailure(err error) - - LogoutSuccess() - LogoutFailure(err error) - - LoadingStart(tunnelID string, loaderMessage string) - LoadingSuccess(tunnelID string) - LoadingFailure(tunnelID string, err error) - - NewVersionAvailable(availableVersion string) -} - // SetCommunicationMechanism is communication mechanism switcher -func SetCommunicationMechanism(mechanism Mechanism) { - communicationMechanism = mechanism +func SetCommunicationMechanism(mechanism coreModels.CommunicationMechanism) { + logger.CommunicationMechanism = mechanism } // TunnelDebug is debug level logger in context of a tunnel func TunnelDebug(tunnelID string, message string) { - communicationMechanism.TunnelDebug(tunnelID, message) + logger.CommunicationMechanism.TunnelDebug(tunnelID, message) } // TunnelInfo is info level logger in context of a tunnel func TunnelInfo(tunnelID string, message string) { - communicationMechanism.TunnelInfo(tunnelID, message) + logger.CommunicationMechanism.TunnelInfo(tunnelID, message) } // TunnelWarn is warn level logger in context of a tunnel func TunnelWarn(tunnelID string, message string) { - communicationMechanism.TunnelWarn(tunnelID, message) + logger.CommunicationMechanism.TunnelWarn(tunnelID, message) } // TunnelError is error level logger in context of a tunnel func TunnelError(tunnelID string, message string) { - communicationMechanism.TunnelError(tunnelID, message) + logger.CommunicationMechanism.TunnelError(tunnelID, message) } // Debug is debug level logger func Debug(message string) { - communicationMechanism.Debug(message) + logger.CommunicationMechanism.Debug(message) } // Info is info level logger func Info(message string) { - communicationMechanism.Info(message) + logger.CommunicationMechanism.Info(message) } // Warn is warn level logger func Warn(message string) { - communicationMechanism.Warn(message) + logger.CommunicationMechanism.Warn(message) } // Error is error level logger func Error(message string) { - communicationMechanism.Error(message) + logger.CommunicationMechanism.Error(message) } // Fatal is fatal level logger, which should cause application to stop func Fatal(message string) { - communicationMechanism.Fatal(message) + logger.CommunicationMechanism.Fatal(message) } // ApplicationStart is the application startup welcome communicate func ApplicationStart(loggedIn bool, idToken string) { - communicationMechanism.ApplicationStart(loggedIn, idToken) + logger.CommunicationMechanism.ApplicationStart(loggedIn, idToken) } // ApplicationStop is the application startup goodbye communicate func ApplicationStop() { - communicationMechanism.ApplicationStop() + logger.CommunicationMechanism.ApplicationStop(config.Config.FeedbackFormURL) } // LoginStart is the communicate to notify about login process being started func LoginStart(deviceCodeSpec authModels.DeviceCodeSpec) { - communicationMechanism.LoginStart(deviceCodeSpec) + logger.CommunicationMechanism.LoginStart(deviceCodeSpec) } // LoginSuccess is the application success login communicate func LoginSuccess(idToken string) { - communicationMechanism.LoginSuccess(idToken) + logger.CommunicationMechanism.LoginSuccess(idToken) } // LoginFailure is the application login failure communicate func LoginFailure(err error) { - communicationMechanism.LoginFailure(err) + logger.CommunicationMechanism.LoginFailure(err) } // LogoutSuccess is the notification logout success communicate func LogoutSuccess() { - communicationMechanism.LogoutSuccess() + logger.CommunicationMechanism.LogoutSuccess() } // LogoutFailure is the notification logout failure communicate func LogoutFailure(err error) { - communicationMechanism.LogoutFailure(err) + logger.CommunicationMechanism.LogoutFailure(err) } // TunnelStart is the notification about tunnel registration success func TunnelStart(tunnelID string) { - communicationMechanism.TunnelStart(tunnelID) + logger.CommunicationMechanism.TunnelStart(tunnelID) } // TunnelStartSuccess is the notification about tunnel being started succesfully func TunnelStartSuccess(remoteConfig coreModels.RemoteEndpointSpecs, localEndpoint string) { - communicationMechanism.TunnelStartSuccess(remoteConfig, localEndpoint) + logger.CommunicationMechanism.TunnelStartSuccess(remoteConfig, localEndpoint, config.Config.Display.QR) } // TunnelStartFailure is the notification about tunnel failing to start func TunnelStartFailure(tunnelID string, err error) { - communicationMechanism.TunnelStartFailure(tunnelID, err) + logger.CommunicationMechanism.TunnelStartFailure(tunnelID, err) } // TunnelRestart is the notification about tunnel being restarted @@ -150,25 +112,25 @@ func TunnelRestart(tunnelID string) {} // TunnelStopSuccess is the notification about tunnel being shut down func TunnelStopSuccess(tunnelID string) { - communicationMechanism.TunnelStopSuccess(tunnelID) + logger.CommunicationMechanism.TunnelStopSuccess(tunnelID) } // LoadingStart is the notification about some loading process being started func LoadingStart(tunnelID string, loaderMessage string) { - communicationMechanism.LoadingStart(tunnelID, loaderMessage) + logger.CommunicationMechanism.LoadingStart(tunnelID, loaderMessage) } // LoadingSuccess is the notification about started loading process being finished successfully func LoadingSuccess(tunnelID string) { - communicationMechanism.LoadingSuccess(tunnelID) + logger.CommunicationMechanism.LoadingSuccess(tunnelID) } // LoadingFailure is the notification about started loading process being finished with failure func LoadingFailure(tunnelID string, err error) { - communicationMechanism.LoadingFailure(tunnelID, err) + logger.CommunicationMechanism.LoadingFailure(tunnelID, err) } // NewVersionAvailable is a communicate being sent if new version of the application is available func NewVersionAvailable(availableVersion string) { - communicationMechanism.NewVersionAvailable(availableVersion) + logger.CommunicationMechanism.NewVersionAvailable(availableVersion) } diff --git a/internal/pkg/communication/websocket.go b/internal/pkg/communication/websocket.go index ee5e9e0..9ce467b 100644 --- a/internal/pkg/communication/websocket.go +++ b/internal/pkg/communication/websocket.go @@ -8,6 +8,7 @@ import ( "github.com/gorilla/websocket" "github.com/loophole/cli/config" coreModels "github.com/loophole/cli/internal/app/loophole/models" + "github.com/loophole/cli/internal/pkg/logger" authModels "github.com/loophole/cli/internal/pkg/token/models" "github.com/loophole/cli/internal/pkg/urlmaker" "github.com/mitchellh/go-homedir" @@ -47,7 +48,7 @@ const ( ) // NewWebsocketLogger is websocket mechanism constructor -func NewWebsocketLogger(wsClient *websocket.Conn) Mechanism { +func NewWebsocketLogger(wsClient *websocket.Conn) coreModels.CommunicationMechanism { logger := websocketLogger{ wsClient: wsClient, } @@ -164,7 +165,7 @@ func (l *websocketLogger) write(websocketMessage interface{}) { err := l.wsClient.WriteJSON(websocketMessage) if err != nil { - defaultLogger.Fatal("Communication over websocket is failing") + logger.DefaultLogger.Fatal("Communication over websocket is failing") } } @@ -243,7 +244,7 @@ func (l *websocketLogger) Fatal(message string) { }) zenity.Error(message) - defaultLogger.Fatal(message) + logger.DefaultLogger.Fatal(message) } func (l *websocketLogger) ApplicationStart(loggedIn bool, idToken string) { @@ -264,7 +265,7 @@ func (l *websocketLogger) ApplicationStart(loggedIn bool, idToken string) { l.write(websocketMessage) } -func (l *websocketLogger) ApplicationStop() { +func (l *websocketLogger) ApplicationStop(s ...string) { //s is only defined to fulfill the interface l.write(appStopMessage{ Type: MessageTypeAppStop, }) @@ -277,7 +278,7 @@ func (l *websocketLogger) TunnelStart(tunnelID string) { }) } -func (l *websocketLogger) TunnelStartSuccess(remoteConfig coreModels.RemoteEndpointSpecs, localEndpoint string) { +func (l *websocketLogger) TunnelStartSuccess(remoteConfig coreModels.RemoteEndpointSpecs, localEndpoint string, b ...bool) { //b is only defined to fulfill the interface siteAddrs := []string{} siteAddrs = append(siteAddrs, urlmaker.GetSiteURL("https", remoteConfig.SiteID, remoteConfig.Domain)) diff --git a/internal/pkg/communication/logger.go b/internal/pkg/logger/logger.go similarity index 94% rename from internal/pkg/communication/logger.go rename to internal/pkg/logger/logger.go index cd36035..433965d 100644 --- a/internal/pkg/communication/logger.go +++ b/internal/pkg/logger/logger.go @@ -1,4 +1,4 @@ -package communication +package logger import ( "fmt" @@ -8,7 +8,6 @@ import ( "github.com/briandowns/spinner" "github.com/logrusorgru/aurora" - "github.com/loophole/cli/config" coreModels "github.com/loophole/cli/internal/app/loophole/models" authModels "github.com/loophole/cli/internal/pkg/token/models" "github.com/loophole/cli/internal/pkg/urlmaker" @@ -17,6 +16,9 @@ import ( "github.com/rs/zerolog/log" ) +var DefaultLogger = NewStdOutLoggerMechanism() +var CommunicationMechanism coreModels.CommunicationMechanism = DefaultLogger + type stdoutLogger struct { colorableOutput io.Writer loader *spinner.Spinner @@ -24,7 +26,7 @@ type stdoutLogger struct { } // NewStdOutLogger is stdout mechanism constructor -func NewStdOutLogger() Mechanism { +func NewStdOutLoggerMechanism() coreModels.CommunicationMechanism { logger := stdoutLogger{ colorableOutput: colorable.NewColorableStdout(), } @@ -95,13 +97,13 @@ func (l *stdoutLogger) ApplicationStart(loggedIn bool, idToken string) { fmt.Fprintln(l.colorableOutput) fmt.Fprintln(l.colorableOutput) } -func (l *stdoutLogger) ApplicationStop() { +func (l *stdoutLogger) ApplicationStop(feedbackFormURL ...string) { l.messageMutex.Lock() defer l.messageMutex.Unlock() l.divider() fmt.Fprint(l.colorableOutput, "Goodbye") - fmt.Fprintln(l.colorableOutput, aurora.Cyan(fmt.Sprintf("Thank you for using Loophole. Please give us your feedback via %s and help us improve our services.", config.Config.FeedbackFormURL))) + fmt.Fprintln(l.colorableOutput, aurora.Cyan(fmt.Sprintf("Thank you for using Loophole. Please give us your feedback via %s and help us improve our services.", feedbackFormURL[0]))) } func (l *stdoutLogger) TunnelStart(tunnelID string) { @@ -110,7 +112,7 @@ func (l *stdoutLogger) TunnelStart(tunnelID string) { log.Debug().Msg("Tunnel starting up...") } -func (l *stdoutLogger) TunnelStartSuccess(remoteConfig coreModels.RemoteEndpointSpecs, localEndpoint string) { +func (l *stdoutLogger) TunnelStartSuccess(remoteConfig coreModels.RemoteEndpointSpecs, localEndpoint string, qr ...bool) { l.messageMutex.Lock() defer l.messageMutex.Unlock() @@ -122,7 +124,7 @@ func (l *stdoutLogger) TunnelStartSuccess(remoteConfig coreModels.RemoteEndpoint fmt.Fprint(l.colorableOutput, " -> ") fmt.Fprint(l.colorableOutput, aurora.Green(localEndpoint)) - if config.Config.Display.QR { + if qr[0] { fmt.Fprintln(l.colorableOutput, "") fmt.Fprintln(l.colorableOutput, "")