mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2025-04-25 14:39:54 +03:00
feat: check for new versions of Docker Registry UI at start up and notify the user
This commit is contained in:
parent
ffb6d14baf
commit
b88dc4567d
6 changed files with 232 additions and 3 deletions
|
@ -17,7 +17,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
<docker-registry-ui>
|
||||
<header>
|
||||
<material-navbar>
|
||||
<span class="logo">Docker Registry UI</span>
|
||||
<span class="logo">
|
||||
<span>Docker Registry UI</span>
|
||||
<version-notification version="{ version }" on-notify="{ notifySnackbar }"></version-notification>
|
||||
</span>
|
||||
<div class="menu">
|
||||
<search-bar on-search="{ onSearch }"></search-bar>
|
||||
<dialogs-menu
|
||||
|
@ -142,6 +145,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
import DialogsMenu from './dialogs/dialogs-menu.riot';
|
||||
import SearchBar from './search-bar.riot';
|
||||
import ErrorPage from './error-page.riot';
|
||||
import VersionNotification from './version-notification.riot';
|
||||
import { stripHttps, getRegistryServers, setRegistryServers, truthy, stringToArray } from '../scripts/utils';
|
||||
import router from '../scripts/router';
|
||||
import { loadTheme } from '../scripts/theme';
|
||||
|
@ -156,6 +160,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
Router,
|
||||
Route,
|
||||
ErrorPage,
|
||||
VersionNotification,
|
||||
},
|
||||
onUpdated(props, state) {
|
||||
state.snackbarIsError = false;
|
||||
|
@ -170,7 +175,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
}
|
||||
|
||||
window.onselectstart = (e) => {
|
||||
if (e.target && e.target.className) {
|
||||
if (e.target && e.target.className && typeof e.target.className.indexOf === 'function') {
|
||||
return !['checkbox', 'checkmark', 'remove-tag'].find((elt) => e.target.className.indexOf(elt) >= 0);
|
||||
}
|
||||
};
|
||||
|
@ -274,6 +279,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
material-navbar .nav-wrapper .logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
material-footer {
|
||||
color: var(--footer-neutral-text);
|
||||
background-color: var(--footer-background);
|
||||
|
|
109
src/components/version-notification.riot
Normal file
109
src/components/version-notification.riot
Normal file
|
@ -0,0 +1,109 @@
|
|||
<version-notification>
|
||||
<span if="{ state.tag_name && !isNewestVersion(props.version, state.tag_name) }" onclick="{ onClick }"></span>
|
||||
<material-popup opened="{ state.open }" onClick="{ onClose }">
|
||||
<div class="material-popup-title">Check for updates</div>
|
||||
<div class="material-popup-content">
|
||||
<p>The version <b>{ state.tag_name }</b> of Docker Regisrty UI now available.</p>
|
||||
<p>You can download the lastest version with docker.</p>
|
||||
<code>joxit/docker-registry-ui:{ state.tag_name }</code>
|
||||
</div>
|
||||
<div class="material-popup-action">
|
||||
<material-button
|
||||
class="dialog-button release-note"
|
||||
waves-color="var(--hover-background)"
|
||||
href="{ state.latest && state.latest.html_url }"
|
||||
color="inherit"
|
||||
text-color="var(--accent-text)"
|
||||
target="_blank"
|
||||
>
|
||||
Release Note
|
||||
</material-button>
|
||||
<material-button
|
||||
class="dialog-button"
|
||||
waves-color="var(--hover-background)"
|
||||
onClick="{ onClose }"
|
||||
color="inherit"
|
||||
text-color="var(--primary-text)"
|
||||
>
|
||||
Close
|
||||
</material-button>
|
||||
</div>
|
||||
</material-popup>
|
||||
<script>
|
||||
import rocketIcon from '../images/rocket.svg';
|
||||
import { Http } from '../scripts/http';
|
||||
import { isNewestVersion, parseJSON } from '../scripts/utils';
|
||||
const LATEST = 'version-notification:latest';
|
||||
const EXPIRES = 'version-notification:expiration-date';
|
||||
const ONE_DAY = 24 * 60 * 60 * 1000;
|
||||
export default {
|
||||
onMounted(props, state) {
|
||||
const latest = parseJSON(localStorage.getItem(LATEST));
|
||||
const expires = parseInt(localStorage.getItem(EXPIRES));
|
||||
if (latest && latest.tag_name) {
|
||||
this.update({ tag_name: latest.tag_name, latest });
|
||||
}
|
||||
if (!latest || isNaN(expires) || new Date().getTime() > expires) {
|
||||
this.checkForUpdates(props, state);
|
||||
}
|
||||
},
|
||||
onUpdated(props, state) {
|
||||
const span = this.$('span');
|
||||
if (span) {
|
||||
span.innerHTML = rocketIcon().firstElementChild.outerHTML;
|
||||
}
|
||||
},
|
||||
onClose() {
|
||||
this.update({ open: false });
|
||||
},
|
||||
onClick() {
|
||||
this.update({ open: true });
|
||||
},
|
||||
checkForUpdates(props, state) {
|
||||
const oReq = new Http();
|
||||
const self = this;
|
||||
|
||||
oReq.addEventListener('load', function () {
|
||||
if (this.status === 200) {
|
||||
const latest = parseJSON(this.responseText);
|
||||
if (latest && self.tag_name !== latest.tag_name && !isNewestVersion(props.version, latest.tag_name)) {
|
||||
props.onNotify('A new version of Docker Registry UI is available!');
|
||||
}
|
||||
localStorage.setItem(LATEST, this.responseText);
|
||||
localStorage.setItem(EXPIRES, new Date().getTime() + ONE_DAY);
|
||||
self.update({ tag_name: latest.tag_name, latest });
|
||||
} else {
|
||||
props.onNotify('Cannot check for new updates. See the browser console.');
|
||||
console.error(`Got status code ${this.status} from Github API with response ${this.responseText}`);
|
||||
}
|
||||
});
|
||||
|
||||
oReq.open('GET', 'https://api.github.com/repos/joxit/docker-registry-ui/releases/latest');
|
||||
oReq.send();
|
||||
},
|
||||
isNewestVersion,
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
:host {
|
||||
display: inline;
|
||||
}
|
||||
:host svg {
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
material-popup material-button > a:first-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
material-popup .material-popup-content code {
|
||||
background-color: var(--hover-background);
|
||||
padding: 0 5px;
|
||||
border-radius: 4px;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
material-popup .material-popup-content b {
|
||||
color: var(--accent-text);
|
||||
}
|
||||
</style>
|
||||
</version-notification>
|
20
src/images/rocket.svg
Normal file
20
src/images/rocket.svg
Normal file
|
@ -0,0 +1,20 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="20" width="20" viewBox="0 0 128 128">
|
||||
<path fill="#ca2c31" d="M3.77 71.73l16.34-16.1 27.82-4.93-2.75 14.56L7.57 76.82l-2.43-1.05z"/>
|
||||
<path fill="#a02422" d="M22.94 59.76L5.2 75.88l13.05 6.36 19.81-10.11v-4.77l4.05-10.92zm41.98 28.39l-8.57 3.72-8.09 17.15s7.12 15.77 7.44 15.77c.32 0 4.37.32 4.37.32l14.4-16.1 3.64-27.5-13.19 6.64z"/>
|
||||
<path d="M56.5 100.84s4.77-.97 8.17-2.59c3.4-1.62 7.6-4.04 7.6-4.04l-1.54 13.43-15.05 17.13s-.59-.73-3.09-6.17c-1.99-4.34-2.68-5.89-2.68-5.89l6.59-11.87z" fill="#ca2c31"/>
|
||||
<path d="M31.58 80.66s-5.74-.48-12.03 7.47c-5.74 7.26-8.43 19.08-9.47 22.12s-3.53 3.66-2.7 5.05 4.42 1.31 8.85.76 8.23-1.94 8.23-1.94-.19.48-.83 1.52c-.23.37-1.03.9-.97 1.45.14 1.31 11.36 1.34 20.32-7.88 9.68-9.95 4.98-18.11 4.98-18.11L31.58 80.66z" fill="#f7d74d"/>
|
||||
<path d="M33.31 85.29s-6.19.33-11.31 8.28-7.5 17.16-7.01 17.78c.48.62 10.02-2.83 12.31-2.14 1.57.48.76 2.07 1.18 2.49.35.35 4.49.94 11.19-6.32 6.71-7.26 5.12-17.46 5.12-17.46l-11.48-2.63z" fill="#fbf0b4"/>
|
||||
<path d="M36.35 74.44s-3.11 2.77-4.22 4.36c-1.11 1.59-1.11 1.73-1.04 2.21.07.48 1.22 5.75 6.01 10.37 5.88 5.67 11.13 6.43 11.89 6.43.76 0 5.81-5.67 5.81-5.67l-18.45-17.7z" fill="#858585"/>
|
||||
<path d="M50.1 91.24s5.04 3.31 13.49.47c11.55-3.88 20.02-12.56 30.51-23.52 10.12-10.58 18.61-23.71 18.61-23.71l-5.95-19.93L50.1 91.24z" fill="#437687"/>
|
||||
<path d="M67.99 80.33l1.39-4.32 3.48.49s2.65 1.25 4.6 2.16c1.95.91 4.46 1.6 4.46 1.6l-4.95 4.18s-2.7-1.02-4.67-1.88c-2.22-.97-4.31-2.23-4.31-2.23z" fill="#3f545f"/>
|
||||
<path d="M84.32 16.14s-9.62 5.58-23.41 18.63c-12.43 11.76-21.64 22.4-23.87 31.45-1.86 7.58-.87 12.18 3.36 17.15 4.47 5.26 9.71 7.87 9.71 7.87s3.94.06 20.38-12.59C91 62.86 107.43 36.42 107.43 36.42L84.32 16.14z" fill="#8dafbf"/>
|
||||
<path d="M104.18 41.84s-8.37-3.57-14.34-11.9c-5.93-8.27-5.46-13.86-5.46-13.86s4.96-3.89 16.11-8.34c7.5-2.99 17.71-4.52 21.07-2.03s-2.3 14.98-2.3 14.98l-10.31 19.96-4.77 1.19z" fill="#d83f22"/>
|
||||
<path d="M68.17 80.4s-7.23-3.69-11.83-8.94c-8.7-9.91-10.5-20.79-10.5-20.79l4.37-5.13S51.3 57.1 60.63 67.09c6.08 6.51 12.43 9.49 12.43 9.49s-1.27 1.07-2.63 2.11c-.87.67-2.26 1.71-2.26 1.71z" fill="#6896a5"/>
|
||||
<path d="M112.71 44.48s4.34-5.23 8.45-17.02c5.74-16.44.74-21.42.74-21.42s-1.69 7.82-7.56 18.69c-4.71 8.71-10.41 17-10.41 17s3.14 1.41 4.84 1.9c2.14.62 3.94.85 3.94.85z" fill="#a02422"/>
|
||||
<path d="M39.81 69.66c1.3 1.24 3.27-.06 4.56-3.1 1.3-3.04 1.28-4.74.28-5.46-1.24-.9-3.32 1.07-4.23 2.82-1 1.94-1.59 4.8-.61 5.74zm45.14-49.53s-7.61 5.47-15.73 12.91c-7.45 6.83-12.39 12.17-13.07 13.41-.72 1.33-.73 3.21-.17 4.17s1.8 1.46 2.93.62c1.13-.85 9.18-9.75 16.45-16.11 6.65-5.82 11.78-9.51 11.78-9.51s2.08-3.68 1.74-4.52c-.34-.85-3.93-.97-3.93-.97z" fill="#b3e1ee"/>
|
||||
<path d="M84.95 20.13s5.62-4.31 11.74-7.34c5.69-2.82 11.35-5.17 12.37-3.13.97 1.94-5.37 4.58-10.95 8.14-5.58 3.56-10.95 7.81-10.95 7.81s-.82-1.5-1.35-2.89a23.7 23.7 0 01-.86-2.59z" fill="#ed6a65"/>
|
||||
<path d="M89.59 39.25c-5.57-5.13-13.32-3.75-17.14.81-3.92 4.7-3.63 11.88 1 16.2 4.21 3.92 12.04 4.81 16.76-.69 4.2-4.88 3.94-12.13-.62-16.32z" fill="#e1e1e1"/>
|
||||
<path d="M75.33 41.87c-3.31 3.25-3.13 9.69.81 12.63 3.44 2.57 8.32 2.44 11.38-.69 3.06-3.13 3.06-8.82.19-11.76-3.3-3.37-8.59-3.9-12.38-.18z" fill="#3f545f"/>
|
||||
<path d="M50 76.89s6.19-6.28 6.87-5.6c.68.68.59 4.49-2.37 8.73-2.97 4.24-9.5 11.79-14.67 16.88-5.1 5.01-12.29 10.74-12.97 10.64-.53-.08-2.68-1.15-3.54-2.19-.84-1.03 1.67-5.9 2.68-7.51 1.02-1.61 24-20.95 24-20.95z" fill="#a02524"/>
|
||||
<path d="M21.23 101.85c-.08 1.44 2.12 3.54 2.12 3.54L56.87 71.3s-1.57-1.77-6.19 1.1c-4.66 2.9-8.74 6.38-14.76 12.21-8.39 8.14-14.61 15.8-14.69 17.24z" fill="#ca2c31"/>
|
||||
</svg>
|
After Width: | Height: | Size: 3.6 KiB |
|
@ -220,3 +220,40 @@ export function truthy(value) {
|
|||
export function stringToArray(value) {
|
||||
return value && typeof value === 'string' ? value.split(',') : [];
|
||||
}
|
||||
|
||||
const compareNumbers = (a, b) => {
|
||||
const na = parseInt(a);
|
||||
const nb = parseInt(b);
|
||||
if (na > nb) return 1;
|
||||
if (nb > na) return -1;
|
||||
if (!isNaN(na) && isNaN(nb)) return 1;
|
||||
if (isNaN(na) && !isNaN(nb)) return -1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
export function isNewestVersion(current = '0.0.0', release = '0.0.0') {
|
||||
if (current === release) {
|
||||
return true;
|
||||
}
|
||||
current = current.split('.');
|
||||
release = release.split('.');
|
||||
const isDev = current[2].indexOf('-') >= 0;
|
||||
const major = compareNumbers(current[0], release[0]);
|
||||
const minor = compareNumbers(current[1], release[1]);
|
||||
const patch = compareNumbers(current[2], release[2]);
|
||||
if (!isDev && (major > 0 || (major === 0 && minor > 0) || (major === 0 && minor === 0 && patch >= 0))) {
|
||||
return true;
|
||||
} else if (isDev && (major > 0 || (major === 0 && minor > 0))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function parseJSON(json) {
|
||||
if (!json) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(json);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { getTagComparator } from '../src/scripts/taglist-order.js';
|
|||
import { DockerRegistryUIError } from '../src/scripts/error.js';
|
||||
import assert from 'assert';
|
||||
|
||||
describe('utils tests', () => {
|
||||
describe('taglist-order tests', () => {
|
||||
describe('taglistOrderVariants', () => {
|
||||
it(`should return the input when it's well formed and num first`, () => {
|
||||
const expected = ['num-asc;alpha-asc', 'num-asc;alpha-desc', 'num-desc;alpha-asc', 'num-desc;alpha-asc'];
|
||||
|
|
52
test/utils.test.js
Normal file
52
test/utils.test.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
import { isNewestVersion } from '../src/scripts/utils.js';
|
||||
import assert from 'assert';
|
||||
|
||||
describe('utils tests', () => {
|
||||
describe('isNewestVersion', () => {
|
||||
it(`should return true for the same version`, () => {
|
||||
const expected = ['2.0.0', '2.4.1', '2.5.0', null, undefined];
|
||||
expected.forEach((e) => assert.ok(isNewestVersion(e, e)));
|
||||
});
|
||||
|
||||
it(`should return true with on common versions`, () => {
|
||||
assert.ok(isNewestVersion('2.5.1', '2.5.0'));
|
||||
assert.ok(isNewestVersion('2.5.0', '2.0.0'));
|
||||
assert.ok(isNewestVersion('2.15.0', '1.25.10'));
|
||||
assert.ok(isNewestVersion('10.10.10', '2.25.20'));
|
||||
});
|
||||
|
||||
it(`should return false on common versions`, () => {
|
||||
assert.equal(isNewestVersion('1.0.0', '2.5.0'), false);
|
||||
assert.equal(isNewestVersion('10.10.10', '20.20.20'), false);
|
||||
assert.equal(isNewestVersion('2.4.10', '2.5.0'), false);
|
||||
assert.equal(isNewestVersion('2.5.0', '2.6.0'), false);
|
||||
});
|
||||
|
||||
it(`should return true for -dev next versions`, () => {
|
||||
assert.ok(isNewestVersion('2.5.0-dev', '2.4.1'));
|
||||
assert.ok(isNewestVersion('2.6.0-dev', '2.5.0'));
|
||||
assert.ok(isNewestVersion('2.15.0-dev', '2.14.1'));
|
||||
assert.ok(isNewestVersion('2.15.0-dev', '1.16.0'));
|
||||
});
|
||||
|
||||
it(`should return false for -dev with current minor version`, () => {
|
||||
assert.equal(isNewestVersion('2.5.0-dev', '2.5.0'), false);
|
||||
assert.equal(isNewestVersion('2.5.0-dev', '2.5.10'), false);
|
||||
assert.equal(isNewestVersion('2.15.0-dev', '2.15.0'), false);
|
||||
assert.equal(isNewestVersion('2.0.0-dev', '2.15.0'), false);
|
||||
});
|
||||
it(`should return true for -{commit sha} next versions`, () => {
|
||||
assert.ok(isNewestVersion('2.5.0-ffb6d14baf', '2.4.1'));
|
||||
assert.ok(isNewestVersion('2.6.0-ffb6d14baf', '2.5.0'));
|
||||
assert.ok(isNewestVersion('2.15.0-ffb6d14baf', '2.14.1'));
|
||||
assert.ok(isNewestVersion('2.15.0-ffb6d14baf', '1.16.0'));
|
||||
});
|
||||
|
||||
it(`should return false for -{commit sha} with current minor version`, () => {
|
||||
assert.equal(isNewestVersion('2.5.0-ffb6d14baf', '2.5.0'), false);
|
||||
assert.equal(isNewestVersion('2.5.0-ffb6d14baf', '2.5.10'), false);
|
||||
assert.equal(isNewestVersion('2.15.0-ffb6d14baf', '2.15.0'), false);
|
||||
assert.equal(isNewestVersion('2.0.0-ffb6d14baf', '2.15.0'), false);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue