Merge pull request #3658 from thelounge/xpaw/version-notify

Display icon when update is available, check on server start
This commit is contained in:
Pavel Djundik 2020-01-22 10:29:18 +02:00 committed by GitHub
commit e8ba4f4fb9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 83 additions and 35 deletions

View file

@ -52,12 +52,22 @@
aria-controls="settings"
:aria-selected="$route.name === 'Settings'"
/></span>
<span class="tooltipped tooltipped-n tooltipped-no-touch" aria-label="Help"
<span
class="tooltipped tooltipped-n tooltipped-no-touch"
:aria-label="
$store.state.serverConfiguration.isUpdateAvailable
? 'Help\n(update available)'
: 'Help'
"
><router-link
to="/help"
tag="button"
active-class="active"
:class="['icon', 'help']"
:class="[
'icon',
'help',
{notified: $store.state.serverConfiguration.isUpdateAvailable},
]"
aria-label="Help"
role="tab"
aria-controls="help"

View file

@ -1,7 +1,7 @@
<template>
<div id="version-checker" :class="[$store.state.versionStatus]">
<p v-if="$store.state.versionStatus === 'loading'">
Checking for updates...
Checking for updates
</p>
<p v-if="$store.state.versionStatus === 'new-version'">
The Lounge <b>{{ $store.state.versionData.latest.version }}</b>
@ -35,7 +35,7 @@
</template>
<template v-if="$store.state.versionStatus === 'error'">
<p>
Information about latest releases could not be retrieved.
Information about latest release could not be retrieved.
</p>
<button id="check-now" class="btn btn-small" @click="checkNow">Try again</button>
@ -48,11 +48,6 @@ import socket from "../js/socket";
export default {
name: "VersionChecker",
data() {
return {
status: "loading",
};
},
mounted() {
if (!this.$store.state.versionData) {
this.checkNow();

View file

@ -26,7 +26,7 @@
></div>
</template>
<template v-else>
<p>Unable to retrieve releases from GitHub.</p>
<p>Unable to retrieve changelog for current release from GitHub.</p>
<p>
<a
:href="

View file

@ -906,6 +906,18 @@ background on hover (unless active) */
border-radius: 5px;
}
#footer .help.notified::after {
content: "\f021";
position: absolute;
bottom: 10px;
right: 7px;
padding: 2px;
font-size: 10px;
border-radius: 50%;
color: var(--link-color);
background: var(--body-bg-color);
}
.window li,
.window p,
.window label,

View file

@ -24,6 +24,16 @@ socket.on("changelog", function(data) {
// When there is a button to refresh the checker available, display it when
// data is expired. Before that, server would return same information anyway.
if (data.expiresAt) {
setTimeout(() => store.commit("versionDataExpired", true), data.expiresAt - Date.now());
const expires = data.expiresAt - Date.now();
if (expires > 0) {
setTimeout(() => store.commit("versionDataExpired", true), expires);
} else {
store.commit("versionDataExpired", true);
}
}
});
socket.on("changelog:newversion", () => {
store.state.serverConfiguration.isUpdateAvailable = true;
});

View file

@ -1,13 +1,16 @@
"use strict";
const got = require("got");
const colors = require("chalk");
const log = require("../log");
const pkg = require("../../package.json");
const TIME_TO_LIVE = 15 * 60 * 1000; // 15 minutes, in milliseconds
module.exports = {
isUpdateAvailable: false,
fetch,
checkForUpdates,
};
const versions = {
@ -17,8 +20,10 @@ const versions = {
};
async function fetch() {
const time = Date.now();
// Serving information from cache
if (versions.current.changelog) {
if (versions.expiresAt > time) {
return versions;
}
@ -36,11 +41,8 @@ async function fetch() {
updateVersions(response);
// Emptying cached information after reaching said expiration date
setTimeout(() => {
delete versions.current.changelog;
delete versions.latest;
}, TIME_TO_LIVE);
// Add expiration date to the data to send to the client for later refresh
versions.expiresAt = time + TIME_TO_LIVE;
} catch (error) {
log.error(`Failed to fetch changelog: ${error}`);
}
@ -74,6 +76,8 @@ function updateVersions(response) {
// Find latest release or pre-release if current version is also a pre-release
if (!release.prerelease || release.prerelease === prerelease) {
module.exports.isUpdateAvailable = true;
versions.latest = {
prerelease: release.prerelease,
version: release.tag_name,
@ -84,7 +88,26 @@ function updateVersions(response) {
}
}
}
// Add expiration date to the data to send to the client for later refresh
versions.expiresAt = Date.now() + TIME_TO_LIVE;
}
function checkForUpdates(manager) {
fetch().then((versionData) => {
if (!module.exports.isUpdateAvailable) {
// Check for updates every 24 hours + random jitter of <3 hours
setTimeout(checkForUpdates, 24 * 3600 * 1000 + Math.floor(Math.random() * 10000000));
}
if (!versionData.latest) {
return;
}
log.info(
`The Lounge ${colors.green(
versionData.latest.version
)} is available. Read more on GitHub: ${versionData.latest.url}`
);
// Notify all connected clients about the new version
manager.clients.forEach((client) => client.emit("changelog:newversion"));
});
}

View file

@ -239,6 +239,8 @@ module.exports = function(options = {}) {
if (Helper.config.prefetchStorage) {
require("./plugins/storage").emptyDir();
}
changelog.checkForUpdates(manager);
});
return server;
@ -703,6 +705,7 @@ function getClientConfiguration() {
]);
}
config.isUpdateAvailable = changelog.isUpdateAvailable;
config.applicationServerKey = manager.webPush.vapidKeys.publicKey;
config.version = pkg.version;
config.gitCommit = Helper.getGitCommit();

View file

@ -129,12 +129,9 @@ describe("LDAP authentication plugin", function() {
this.slow(200);
let server;
let originalLogInfo;
before(function(done) {
originalLogInfo = log.info;
log.info = () => {};
stub(log, "info");
server = startLdapServer(done);
});
@ -142,7 +139,7 @@ describe("LDAP authentication plugin", function() {
after(function() {
server.close();
log.info = originalLogInfo;
log.info.restore();
});
beforeEach(function() {

View file

@ -3,28 +3,28 @@
const log = require("../src/log");
const Helper = require("../src/helper");
const expect = require("chai").expect;
const stub = require("sinon").stub;
const got = require("got");
const io = require("socket.io-client");
const changelog = require("../src/plugins/changelog");
describe("Server", function() {
// Increase timeout due to unpredictable I/O on CI services
this.timeout(process.env.CI ? 25000 : 5000);
let server;
let originalLogInfo;
before(function() {
originalLogInfo = log.info;
log.info = () => {};
stub(log, "info");
stub(changelog, "checkForUpdates");
server = require("../src/server")();
});
after(function(done) {
server.close(done);
log.info = originalLogInfo;
log.info.restore();
changelog.checkForUpdates.restore();
});
const webURL = `http://${Helper.config.host}:${Helper.config.port}/`;

View file

@ -248,9 +248,7 @@ describe("mergeConfig", function() {
});
it("should only merge same type", function() {
const originalLog = log.info;
log.warn = () => {};
stub(log, "warn");
expect(
mergeConfig(
@ -282,6 +280,6 @@ describe("mergeConfig", function() {
shouldBeString: "string",
});
log.warn = originalLog;
log.warn.restore();
});
});