|
@@ -0,0 +1,78 @@
|
|
|
|
+package main
|
|
|
|
+
|
|
|
|
+import (
|
|
|
|
+ "encoding/json"
|
|
|
|
+ "io/ioutil"
|
|
|
|
+ "net/http"
|
|
|
|
+ "regexp"
|
|
|
|
+ "time"
|
|
|
|
+
|
|
|
|
+ "golang.org/x/mod/semver"
|
|
|
|
+)
|
|
|
|
+
|
|
|
|
+const updateCheckURL = "https://api.github.com/repos/knadh/listmonk/releases/latest"
|
|
|
|
+
|
|
|
|
+type remoteUpdateResp struct {
|
|
|
|
+ Version string `json:"tag_name"`
|
|
|
|
+ URL string `json:"html_url"`
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// AppUpdate contains information of a new update available to the app that
|
|
|
|
+// is sent to the frontend.
|
|
|
|
+type AppUpdate struct {
|
|
|
|
+ Version string `json:"version"`
|
|
|
|
+ URL string `json:"url"`
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+var reSemver = regexp.MustCompile(`-(.*)`)
|
|
|
|
+
|
|
|
|
+// checkUpdates is a blocking function that checks for updates to the app
|
|
|
|
+// at the given intervals. On detecting a new update (new semver), it
|
|
|
|
+// sets the global update status that renders a prompt on the UI.
|
|
|
|
+func checkUpdates(curVersion string, interval time.Duration, app *App) {
|
|
|
|
+ // Strip -* suffix.
|
|
|
|
+ curVersion = reSemver.ReplaceAllString(curVersion, "")
|
|
|
|
+
|
|
|
|
+ time.Sleep(time.Second * 1)
|
|
|
|
+ ticker := time.NewTicker(interval)
|
|
|
|
+ for ; true; <-ticker.C {
|
|
|
|
+ resp, err := http.Get(updateCheckURL)
|
|
|
|
+ if err != nil {
|
|
|
|
+ app.log.Printf("error checking for remote update: %v", err)
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if resp.StatusCode != 200 {
|
|
|
|
+ app.log.Printf("non 200 response on remote update check: %d", resp.StatusCode)
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ b, err := ioutil.ReadAll(resp.Body)
|
|
|
|
+ if err != nil {
|
|
|
|
+ app.log.Printf("error reading remote update payload: %v", err)
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+ resp.Body.Close()
|
|
|
|
+
|
|
|
|
+ var up remoteUpdateResp
|
|
|
|
+ if err := json.Unmarshal(b, &up); err != nil {
|
|
|
|
+ app.log.Printf("error unmarshalling remote update payload: %v", err)
|
|
|
|
+ continue
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // There is an update. Set it on the global app state.
|
|
|
|
+ if semver.IsValid(up.Version) {
|
|
|
|
+ v := reSemver.ReplaceAllString(up.Version, "")
|
|
|
|
+ if semver.Compare(v, curVersion) > 0 {
|
|
|
|
+ app.Lock()
|
|
|
|
+ app.update = &AppUpdate{
|
|
|
|
+ Version: up.Version,
|
|
|
|
+ URL: up.URL,
|
|
|
|
+ }
|
|
|
|
+ app.Unlock()
|
|
|
|
+
|
|
|
|
+ app.log.Printf("new update %s found", up.Version)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|