mirror of
https://github.com/knadh/listmonk.git
synced 2024-11-13 02:55:04 +08:00
Refactor update check.
- Switch away from GitHub releases API to a statically hosted custom JSON message to include richer data. - Instead of checking 24 hours post-boot, check 15 mins later post boot and then every 24 hours. - Add provision for messages to display on the admin dashboard to communicate important / urgent announcements. (Fingers crossed, this never has to be used!)
This commit is contained in:
parent
a8c17780f9
commit
4eabd967d8
4 changed files with 58 additions and 30 deletions
|
@ -10,18 +10,25 @@ import (
|
|||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
const updateCheckURL = "https://api.github.com/repos/knadh/listmonk/releases/latest"
|
||||
const updateCheckURL = "https://update.listmonk.app/update.json"
|
||||
|
||||
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"`
|
||||
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(`-(.*)`)
|
||||
|
@ -32,11 +39,12 @@ var reSemver = regexp.MustCompile(`-(.*)`)
|
|||
func checkUpdates(curVersion string, interval time.Duration, app *App) {
|
||||
// Strip -* suffix.
|
||||
curVersion = reSemver.ReplaceAllString(curVersion, "")
|
||||
time.Sleep(time.Second * 1)
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
// 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 * 1)
|
||||
|
||||
for {
|
||||
resp, err := http.Get(updateCheckURL)
|
||||
if err != nil {
|
||||
app.log.Printf("error checking for remote update: %v", err)
|
||||
|
@ -55,25 +63,25 @@ func checkUpdates(curVersion string, interval time.Duration, app *App) {
|
|||
}
|
||||
resp.Body.Close()
|
||||
|
||||
var up remoteUpdateResp
|
||||
if err := json.Unmarshal(b, &up); err != nil {
|
||||
var out AppUpdate
|
||||
if err := json.Unmarshal(b, &out); 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.IsValid(out.Update.ReleaseVersion) {
|
||||
v := reSemver.ReplaceAllString(out.Update.ReleaseVersion, "")
|
||||
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)
|
||||
out.Update.IsNew = true
|
||||
app.log.Printf("new update %s found", out.Update.ReleaseVersion)
|
||||
}
|
||||
}
|
||||
|
||||
app.Lock()
|
||||
app.update = &out
|
||||
app.Unlock()
|
||||
|
||||
time.Sleep(interval)
|
||||
}
|
||||
}
|
||||
|
|
1
frontend/.eslintrc.js
vendored
1
frontend/.eslintrc.js
vendored
|
@ -21,6 +21,7 @@ module.exports = {
|
|||
'vue/max-attributes-per-line': 'off',
|
||||
'vue/html-indent': 'off',
|
||||
'vue/html-closing-bracket-newline': 'off',
|
||||
'vue/singleline-html-element-content-newline': 'off',
|
||||
'vue/max-len': ['error', {
|
||||
code: 200,
|
||||
template: 200,
|
||||
|
|
|
@ -58,10 +58,24 @@
|
|||
{{ $t('settings.restart') }}
|
||||
</b-button>
|
||||
</div>
|
||||
<div v-if="serverConfig.update" class="notification is-success">
|
||||
{{ $t('settings.updateAvailable', { version: serverConfig.update.version }) }}
|
||||
<a :href="serverConfig.update.url" target="_blank" rel="noopener noreferer">View</a>
|
||||
|
||||
<div v-if="serverConfig.update.update.is_new" class="notification is-success">
|
||||
{{ $t('settings.updateAvailable', {
|
||||
version: `${serverConfig.update.update.release_version}
|
||||
(${$utils.getDate(serverConfig.update.update.release_date).format('DD MMM YY')})`,
|
||||
}) }}
|
||||
<a :href="serverConfig.update.update.url" target="_blank" rel="noopener noreferer">View</a>
|
||||
</div>
|
||||
|
||||
<template v-if="serverConfig.update.messages && serverConfig.update.messages.length > 0">
|
||||
<div v-for="m in serverConfig.update.messages" class="notification"
|
||||
:class="{ [m.priority === 'high' ? 'is-danger' : 'is-info']: true }" :key="m.title">
|
||||
<h3 class="is-size-5" v-if="m.title"><strong>{{ m.title }}</strong></h3>
|
||||
<p v-if="m.description">{{ m.description }}</p>
|
||||
<a v-if="m.url" :href="m.url" target="_blank" rel="noopener noreferer">View</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div v-if="serverConfig.has_legacy_user" class="notification is-danger">
|
||||
<b-icon icon="warning-empty" />
|
||||
Remove the <code>admin_username</code> and <code>admin_password</code> fields from the TOML
|
||||
|
|
|
@ -252,7 +252,7 @@ body.is-noscroll {
|
|||
}
|
||||
.notification {
|
||||
padding: 10px 15px;
|
||||
border-left: 5px solid #eee;
|
||||
border-left: 10px solid #eee;
|
||||
|
||||
&.is-danger {
|
||||
background: $white-ter;
|
||||
|
@ -264,6 +264,11 @@ body.is-noscroll {
|
|||
color: $black;
|
||||
border-left-color: $green;
|
||||
}
|
||||
&.is-info {
|
||||
background: $white-ter;
|
||||
border-left-color: $primary;
|
||||
color: $grey-dark;
|
||||
}
|
||||
}
|
||||
|
||||
/* WYSIWYG / HTML code editor */
|
||||
|
|
Loading…
Reference in a new issue