-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement app update checker and UI notification
- Loading branch information
Showing
10 changed files
with
167 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// Copyright Kailash Nadh (https://github.com/knadh/listmonk) | ||
// SPDX-License-Identifier: AGPL-3.0 | ||
// Adapted from listmonk for Libredesk. | ||
|
||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"io" | ||
"net/http" | ||
"regexp" | ||
"time" | ||
|
||
"golang.org/x/mod/semver" | ||
) | ||
|
||
const updateCheckURL = "https://updates.libredesk.io/updates.json" | ||
|
||
type AppUpdate struct { | ||
Update struct { | ||
ReleaseVersion string `json:"release_version"` | ||
ReleaseDate string `json:"release_date"` | ||
URL string `json:"url"` | ||
Description string `json:"description"` | ||
|
||
// This is computed and set locally based on the local version. | ||
IsNew bool `json:"is_new"` | ||
} `json:"update"` | ||
Messages []struct { | ||
Date string `json:"date"` | ||
Title string `json:"title"` | ||
Description string `json:"description"` | ||
URL string `json:"url"` | ||
Priority string `json:"priority"` | ||
} `json:"messages"` | ||
} | ||
|
||
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, "") | ||
|
||
fnCheck := func() { | ||
resp, err := http.Get(updateCheckURL) | ||
if err != nil { | ||
app.lo.Error("error checking for app updates", "err", err) | ||
return | ||
} | ||
|
||
if resp.StatusCode != 200 { | ||
app.lo.Error("non-ok status code checking for app updates", "status", resp.StatusCode) | ||
return | ||
} | ||
|
||
b, err := io.ReadAll(resp.Body) | ||
if err != nil { | ||
app.lo.Error("error reading response body", "err", err) | ||
return | ||
} | ||
resp.Body.Close() | ||
|
||
var out AppUpdate | ||
if err := json.Unmarshal(b, &out); err != nil { | ||
app.lo.Error("error unmarshalling response body", "err", err) | ||
return | ||
} | ||
|
||
// There is an update. Set it on the global app state. | ||
if semver.IsValid(out.Update.ReleaseVersion) { | ||
v := reSemver.ReplaceAllString(out.Update.ReleaseVersion, "") | ||
if semver.Compare(v, curVersion) > 0 { | ||
out.Update.IsNew = true | ||
app.lo.Info("new update available", "version", out.Update.ReleaseVersion) | ||
} | ||
} | ||
|
||
app.Lock() | ||
app.update = &out | ||
app.Unlock() | ||
} | ||
|
||
// Give a 15 minute buffer after app start in case the admin wants to disable | ||
// update checks entirely and not make a request to upstream. | ||
time.Sleep(time.Minute * 15) | ||
fnCheck() | ||
|
||
// Thereafter, check every $interval. | ||
ticker := time.NewTicker(interval) | ||
defer ticker.Stop() | ||
|
||
for range ticker.C { | ||
fnCheck() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<template> | ||
<div | ||
v-if="appSettingsStore.settings['app.update']?.update?.is_new" | ||
class="p-2 mb-2 border-b bg-secondary text-secondary-foreground" | ||
> | ||
A new update is available: | ||
{{ appSettingsStore.settings['app.update'].update.release_version }} ({{ | ||
appSettingsStore.settings['app.update'].update.release_date | ||
}}) | ||
<a | ||
:href="appSettingsStore.settings['app.update'].update.url" | ||
target="_blank" | ||
nofollow | ||
noreferrer | ||
class="underline ml-2" | ||
> | ||
View details | ||
</a> | ||
</div> | ||
</template> | ||
|
||
<script setup> | ||
import { useAppSettingsStore } from '@/stores/appSettings' | ||
const appSettingsStore = useAppSettingsStore() | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { defineStore } from 'pinia' | ||
|
||
export const useAppSettingsStore = defineStore('settings', { | ||
state: () => ({ | ||
settings: {} | ||
}), | ||
actions: { | ||
setSettings (newSettings) { | ||
this.settings = newSettings | ||
} | ||
} | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters