mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2025-04-27 23:50:01 +03:00
feat(riot-v5): upgrade add-registry-url dialog
This commit is contained in:
parent
bb3182d56e
commit
e6af9321a8
9 changed files with 154 additions and 79 deletions
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
65
src/components/dialogs/add-registry-url.riot
Normal file
65
src/components/dialogs/add-registry-url.riot
Normal 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>
|
|
@ -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>
|
|
@ -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);
|
||||
},
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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.'
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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) })
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue