feat(riot-v5): upgrade add-registry-url dialog

This commit is contained in:
Joxit 2021-03-26 00:04:28 +01:00
parent bb3182d56e
commit e6af9321a8
No known key found for this signature in database
GPG key ID: F526592B8E012263
9 changed files with 154 additions and 79 deletions

View file

@ -53,16 +53,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
},
display(props, state) {
this.state.repositories = [];
let repositories = [];
const self = this;
const oReq = new Http();
oReq.addEventListener('load', function () {
state.repositories = [];
if (this.status == 200) {
state.repositories = JSON.parse(this.responseText).repositories || [];
state.repositories.sort();
state.length = state.repositories.length;
state.repositories = state.repositories.reduce(function (acc, e) {
repositories = JSON.parse(this.responseText).repositories || [];
repositories.sort();
repositories = repositories.reduce(function (acc, e) {
const slash = e.indexOf('/');
if (slash > 0) {
const repoName = e.substring(0, slash) + '/';
@ -86,14 +84,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
});
oReq.addEventListener('error', function () {
self.props.onNotify(this.getErrorMessage(), true);
state.repositories = [];
});
oReq.addEventListener('loadend', function () {
self.update({
repositories,
length: repositories.length,
loadend: true
});
});
oReq.open('GET', props.registryUrl + '/v2/_catalog?n=' + state.catalogElementsLimit);
oReq.open('GET', `${props.registryUrl}/v2/_catalog?n=${state.catalogElementsLimit}`);
oReq.send();
}
}

View file

@ -0,0 +1,65 @@
<!--
Copyright (C) 2016-2021 Jones Magloire @Joxit
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<add-registry-url>
<material-popup opened="{ props.opened }" onClick="{ props.onClose }">
<div slot="title">Add your Server ?</div>
<div slot="content">
<material-input onkeyup="{ onKeyUp }" placeholder="Server URL"></material-input>
<span>Write your URL without /v2</span>
</div>
<div slot="action">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ add }">
Add
</material-button>
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="{ props.onClose }">
Cancel
</material-button>
</div>
</material-popup>
<script>
import {
getRegistryServers,
updateHistory
} from '../../scripts/utils';
import router from '../../scripts/router';
export default {
onKeyUp(event) {
// if keyCode is Enter
if (event.keyCode === 13) {
this.add();
}
},
add() {
const input = this.$('input');
if (!input || !input.value || input.value.length === 0) {
return this.props.onNotify('The input field is empty. Please enter an url.', true);
}
if (!input.value.startsWith('http')) {
return this.props.onNotify('The input field should start with http:// or https://.', true);
}
const url = input.value.trim().replace(/\/*$/, '');
const registryServer = getRegistryServers().filter(e => e !== url);
localStorage.setItem('registryServer', JSON.stringify([url].concat(registryServer)));
router.home()
this.props.onServerChange(url);
this.props.onClose()
setTimeout(() => updateHistory(url), 100);
}
}
</script>
</add-registry-url>

View file

@ -21,8 +21,15 @@
<material-dropdown-list items="{ dropdownItems }" onSelect="{ onDropdownSelect }"
opened="{ state.isDropdownOpened }" />
<div class="overlay" onclick="{ onClick }" if="{ state.isDropdownOpened }"></div>
<add-registry-url opened="{ state['add-registry-url'] }" on-close="{ onClose('add-registry-url') }"
on-notify="{ props.onNotify }" on-server-change="{ props.onServerChange }"></add-registry-url>
<script>
import AddRegistryUrl from './add-registry-url.riot';
export default {
components: {
AddRegistryUrl
},
dropdownItems: [{
title: 'Add URL',
name: 'add-registry-url'
@ -33,7 +40,20 @@
title: 'Remove URL',
name: 'remove-registry-url'
}],
onDropdownSelect(key, item) {},
onDropdownSelect(key, item) {
this.update({
[item.name]: true,
isDropdownOpened: false
});
},
onClose(name) {
return () => {
this.update({
[name]: false,
isDropdownOpened: false
})
}
},
onClick() {
this.update({
isDropdownOpened: !this.state.isDropdownOpened
@ -57,7 +77,7 @@
width: 100%;
top: 0;
right: 0;
z-index: 1;
z-index: 10;
}
:host material-button {
@ -86,5 +106,13 @@
font-size: 1rem;
line-height: 1.2em;
}
:host material-popup * {
line-height: 1em;
}
:host material-popup material-button .content {
line-height: 36px;
}
</style>
</dialogs-menu>

View file

@ -18,7 +18,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
<header>
<material-navbar>
<div class="logo">Docker Registry UI</div>
<dialogs-menu></dialogs-menu>
<dialogs-menu on-notify="{ notifySnackbar }" on-server-change="{ onServerChange }"></dialogs-menu>
</material-navbar>
</header>
<main>
@ -79,16 +79,28 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
Router,
Route
},
onUpdated(props, state) {
state.snackbarIsError = false;
state.snackbarMessage = undefined;
},
onBeforeMount(props) {
this.state.registryUrl = props.registryUrl || (window.location.origin + window.location.pathname.replace(/\/+$/,
''));
this.state.name = props.name || stripHttps(props.registryUrl);
this.state.catalogElementsLimit = props.catalogElementsLimit || 100000;
this.state.pullUrl = this.pullUrl(props);
this.state.pullUrl = this.pullUrl(this.state.registryUrl, props.pullUrl);
},
pullUrl(props) {
const url = props.pullUrl ||
(props.registryUrl && props.registryUrl.length > 0 && props.registryUrl) ||
onServerChange(registryUrl) {
this.update({
registryUrl,
name: stripHttps(registryUrl),
pullUrl: this.pullUrl(registryUrl),
snackbarMessage: 'Registry server changed to `' + registryUrl + '`.'
})
},
pullUrl(registryUrl, pullUrl) {
const url = pullUrl ||
(registryUrl && registryUrl.length > 0 && registryUrl) ||
window.location.host;
return stripHttps(url);
},

View file

@ -5,10 +5,13 @@ import {
MaterialNavbar,
MaterialFooter,
MaterialButton,
MaterialWaves,
MaterialCheckbox,
MaterialTabs,
MaterialSnackbar,
MaterialDropdownList
MaterialDropdownList,
MaterialPopup,
MaterialInput
} from 'riot-mui';
import DockerRegistryUI from './components/docker-registry-ui.riot';
@ -20,10 +23,13 @@ register('material-footer', MaterialFooter);
register('material-navbar', MaterialNavbar);
register('material-spinner', MaterialSpinner);
register('material-button', MaterialButton);
register('material-waves', MaterialWaves);
register('material-checkbox', MaterialCheckbox);
register('material-snackbar', MaterialSnackbar);
register('material-tabs', MaterialTabs);
register('material-dropdown-list', MaterialDropdownList);
register('material-popup', MaterialPopup);
register('material-input', MaterialInput);
const createApp = component(DockerRegistryUI);
const tags = document.getElementsByTagName('docker-registry-ui');

View file

@ -54,6 +54,7 @@ export class Http {
self.oReq.addEventListener('loadend', function () {
if (this.status == 401) {
const req = new XMLHttpRequest();
req._url = self._url;
req.open(self._method, self._url);
for (key in self._events) {
req.addEventListener(key, self._events[key]);
@ -97,6 +98,7 @@ export class Http {
open(m, u) {
this._method = m;
this._url = u;
this.oReq._url = u;
this.oReq.open(m, u);
}
@ -114,20 +116,20 @@ const hasHeader = function (header) {
};
const getErrorMessage = function () {
if (registryUI.url() && registryUI.url().match('^http://') && window.location.protocol === 'https:') {
if (this._url.match('^http://') && window.location.protocol === 'https:') {
return (
'Mixed Content: The page at `' +
window.location.origin +
'` was loaded over HTTPS, but requested an insecure server endpoint `' +
registryUI.url() +
new URL(this._url).origin +
'`. This request has been blocked; the content must be served over HTTPS.'
);
} else if (!registryUI.url()) {
} else if (!this._url || !this._url.match('^http')) {
return 'Incorrect server endpoint.';
} else if (this.withCredentials && !this.hasHeader('Access-Control-Allow-Credentials')) {
return (
"The `Access-Control-Allow-Credentials` header in the response is missing and must be set to `true` when the request's credentials mode is on. Origin `" +
registryUI.url() +
new URL(this._url).origin +
'` is therefore not allowed access.'
);
}

View file

@ -159,3 +159,22 @@ export const ERROR_CAN_NOT_READ_CONTENT_DIGEST = {
'https://docs.docker.com/registry/configuration/#http',
isError: true,
};
export function getRegistryServers(i) {
try {
const res = JSON.parse(localStorage.getItem('registryServer'));
if (res instanceof Array) {
return !isNaN(i) ? res[i] : res.map((url) => url.trim().replace(/\/*$/, ''));
}
} catch (e) {}
return !isNaN(i) ? '' : [];
}
export function encodeURI(url) {
if (!url) { return; }
return url.indexOf('&') < 0 ? window.encodeURIComponent(url) : btoa(url);
};
export function updateHistory(url) {
updateQueryString({ url: encodeURI(url) })
}

View file

@ -19,10 +19,13 @@
@import 'riot-mui/src/material-elements/material-card/material-card.scss';
@import 'riot-mui/src/material-elements/material-spinner/material-spinner.scss';
@import 'riot-mui/src/material-elements/material-button/material-button.scss';
@import 'riot-mui/src/material-elements/material-waves/material-waves.scss';
@import 'riot-mui/src/material-elements/material-checkbox/material-checkbox.scss';
@import 'riot-mui/src/material-elements/material-tabs/material-tabs.scss';
@import 'riot-mui/src/material-elements/material-snackbar/material-snackbar.scss';
@import 'riot-mui/src/material-elements/material-dropdown-list/material-dropdown-list.scss';
@import 'riot-mui/src/material-elements/material-popup/material-popup.scss';
@import 'riot-mui/src/material-elements/material-input/material-input.scss';
@import './roboto.scss';
@import './material-icons.scss';

View file

@ -1,59 +0,0 @@
<!--
Copyright (C) 2016-2019 Jones Magloire @Joxit
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<add>
<material-popup>
<div class="material-popup-title">Add your Server ?</div>
<div class="material-popup-content">
<material-input onkeyup="{ registryUI.addTag.onkeyup }" placeholder="Server URL"></material-input>
<span>Write your URL without /v2</span>
</div>
<div class="material-popup-action">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="registryUI.addTag.add();">Add</material-button>
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="registryUI.addTag.close();">Cancel</material-button>
</div>
</material-popup>
<script type="text/javascript">
registryUI.addTag = registryUI.addTag || {};
this.one('mount', function () {
registryUI.addTag.dialog = this.tags['material-popup'];
registryUI.addTag.dialog.getAddServer = function() {
return this.tags['material-input'] ? this.tags['material-input'].value : '';
}
});
registryUI.addTag.onkeyup = function (e) {
// if keyCode is Enter
if (e.keyCode == 13) {
registryUI.addTag.add();
}
};
registryUI.addTag.show = function () {
registryUI.addTag.dialog.open();
};
registryUI.addTag.add = function () {
if (registryUI.addTag.dialog.getAddServer().length > 0) {
registryUI.addServer(registryUI.addTag.dialog.getAddServer());
}
registryUI.home();
registryUI.addTag.close();
};
registryUI.addTag.close = function () {
registryUI.addTag.dialog.tags['material-input'].value = '';
registryUI.addTag.dialog.close();
};
</script>
</add>