mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2025-04-26 06:59:52 +03:00
chore(cleanup): format code and move from js-beautify to prettier
This commit is contained in:
parent
ab12cceefc
commit
49fcba3f6c
21 changed files with 373 additions and 302 deletions
|
@ -2,6 +2,10 @@
|
|||
"name": "docker-registry-ui",
|
||||
"version": "2.1.0",
|
||||
"scripts": {
|
||||
"format": "npm run format-html && npm run format-js && npm run format-riot",
|
||||
"format-html": "find src rollup rollup.config.js -name '*.html' -exec prettier --config .prettierrc -w --parser html {} \\;",
|
||||
"format-js": "find src rollup rollup.config.js -name '*.js' -exec prettier --config .prettierrc -w {} \\;",
|
||||
"format-riot": "find src rollup rollup.config.js -name '*.riot' -exec prettier --config .prettierrc -w --parser html {} \\;",
|
||||
"start": "rollup -c -w --environment ROLLUP_SERVE:true",
|
||||
"build": "rollup -c",
|
||||
"build:electron": "npm run build && cd examples/electron && npm install && npm run dist"
|
||||
|
@ -25,8 +29,8 @@
|
|||
"@rollup/plugin-json": "^4.1.0",
|
||||
"@rollup/plugin-node-resolve": "^13.1.3",
|
||||
"core-js": "^3.19.1",
|
||||
"js-beautify": "^1.14.0",
|
||||
"node-sass": "^7.0.1",
|
||||
"prettier": "^2.6.2",
|
||||
"riot": "^6.1.2",
|
||||
"riot-mui": "github:joxit/riot-5-mui#4d68d7f",
|
||||
"rollup": "^2.59.0",
|
||||
|
|
|
@ -15,4 +15,4 @@ export default `/*
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @license AGPL
|
||||
*/`
|
||||
*/`;
|
||||
|
|
|
@ -96,10 +96,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
onAuthentication: props.onAuthentication,
|
||||
});
|
||||
oReq.addEventListener('load', function () {
|
||||
if (this.status == 200) {
|
||||
if (this.status === 200) {
|
||||
const nbTags = (JSON.parse(this.responseText).tags || []).length;
|
||||
self.update({ nbTags });
|
||||
} else if (this.status == 404) {
|
||||
} else if (this.status === 404) {
|
||||
props.onNotify('Server not found', true);
|
||||
} else {
|
||||
props.onNotify(this.responseText, true);
|
||||
|
|
|
@ -73,14 +73,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
onAuthentication: this.props.onAuthentication,
|
||||
});
|
||||
oReq.addEventListener('load', function () {
|
||||
if (this.status == 200) {
|
||||
if (this.status === 200) {
|
||||
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) + '/';
|
||||
if (acc.length == 0 || acc[acc.length - 1].repo != repoName) {
|
||||
if (acc.length === 0 || acc[acc.length - 1].repo != repoName) {
|
||||
acc.push({
|
||||
repo: repoName,
|
||||
images: [],
|
||||
|
@ -92,7 +92,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
acc.push(e);
|
||||
return acc;
|
||||
}, []);
|
||||
} else if (this.status == 404) {
|
||||
} else if (this.status === 404) {
|
||||
self.props.onNotify('Server not found', true);
|
||||
} else {
|
||||
self.props.onNotify(this.responseText);
|
||||
|
|
|
@ -31,9 +31,7 @@
|
|||
</div>
|
||||
</material-popup>
|
||||
<script>
|
||||
import {
|
||||
addRegistryServers
|
||||
} from '../../scripts/utils';
|
||||
import { addRegistryServers } from '../../scripts/utils';
|
||||
import router from '../../scripts/router';
|
||||
|
||||
export default {
|
||||
|
@ -52,11 +50,11 @@
|
|||
return this.props.onNotify('The input field should start with http:// or https://.', true);
|
||||
}
|
||||
const url = addRegistryServers(input.value);
|
||||
router.home()
|
||||
router.home();
|
||||
this.props.onServerChange(url);
|
||||
this.props.onClose()
|
||||
this.props.onClose();
|
||||
setTimeout(() => router.updateUrlQueryParam(url), 100);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
</add-registry-url>
|
||||
</add-registry-url>
|
||||
|
|
|
@ -32,10 +32,7 @@
|
|||
</div>
|
||||
</material-popup>
|
||||
<script>
|
||||
import {
|
||||
addRegistryServers,
|
||||
getRegistryServers
|
||||
} from '../../scripts/utils';
|
||||
import { addRegistryServers, getRegistryServers } from '../../scripts/utils';
|
||||
import router from '../../scripts/router';
|
||||
export default {
|
||||
change(event) {
|
||||
|
@ -47,13 +44,13 @@
|
|||
return this.props.onNotify('The select field should start with http:// or https://.', true);
|
||||
}
|
||||
const url = addRegistryServers(select.value);
|
||||
router.home()
|
||||
router.home();
|
||||
this.props.onServerChange(url);
|
||||
this.props.onClose()
|
||||
this.props.onClose();
|
||||
setTimeout(() => router.updateUrlQueryParam(url), 100);
|
||||
},
|
||||
getRegistryServers
|
||||
}
|
||||
getRegistryServers,
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
:host select {
|
||||
|
@ -74,4 +71,4 @@
|
|||
margin: 1.5em 0;
|
||||
}
|
||||
</style>
|
||||
</change-registry-url>
|
||||
</change-registry-url>
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
const oReq = new Http({ onAuthentication });
|
||||
const self = this;
|
||||
oReq.addEventListener('loadend', function () {
|
||||
if (this.status == 200 || this.status == 202) {
|
||||
if (this.status === 200 || this.status === 202) {
|
||||
oReq.getContentDigest(function (digest) {
|
||||
if (!digest) {
|
||||
onNotify(ERROR_CAN_NOT_READ_CONTENT_DIGEST);
|
||||
|
@ -60,7 +60,7 @@
|
|||
self.deleteImage({ name, tag, digest }, opts);
|
||||
}
|
||||
});
|
||||
} else if (this.status == 404) {
|
||||
} else if (this.status === 404) {
|
||||
onNotify(`Manifest for ${name}:${tag} not found`, true);
|
||||
} else {
|
||||
onNotify(this.responseText);
|
||||
|
@ -77,10 +77,10 @@
|
|||
const { registryUrl, ignoreError, onNotify, onAuthentication, onClick } = opts;
|
||||
const oReq = new Http({ onAuthentication });
|
||||
oReq.addEventListener('loadend', function () {
|
||||
if (this.status == 200 || this.status == 202) {
|
||||
if (this.status === 200 || this.status === 202) {
|
||||
router.taglist(name);
|
||||
onNotify(`Deleting ${name}:${tag} image. Run \`registry garbage-collect config.yml\` on your registry`);
|
||||
} else if (this.status == 404) {
|
||||
} else if (this.status === 404) {
|
||||
ignoreError ||
|
||||
onNotify({
|
||||
message: 'Digest not found for this image in your registry.',
|
||||
|
|
|
@ -15,18 +15,35 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<dialogs-menu>
|
||||
<add-registry-url if="{ !props.readOnlyRegistries }" opened="{ state['add-registry-url'] }" on-close="{ onClose('add-registry-url') }"
|
||||
on-notify="{ props.onNotify }" on-server-change="{ props.onServerChange }"></add-registry-url>
|
||||
<change-registry-url opened="{ state['change-registry-url'] }" on-close="{ onClose('change-registry-url') }"
|
||||
on-notify="{ props.onNotify }" on-server-change="{ props.onServerChange }"></change-registry-url>
|
||||
<remove-registry-url if="{ !props.readOnlyRegistries }" opened="{ state['remove-registry-url'] }" on-close="{ onClose('remove-registry-url') }"
|
||||
on-notify="{ props.onNotify }" on-server-change="{ props.onServerChange }"></remove-registry-url>
|
||||
<add-registry-url
|
||||
if="{ !props.readOnlyRegistries }"
|
||||
opened="{ state['add-registry-url'] }"
|
||||
on-close="{ onClose('add-registry-url') }"
|
||||
on-notify="{ props.onNotify }"
|
||||
on-server-change="{ props.onServerChange }"
|
||||
></add-registry-url>
|
||||
<change-registry-url
|
||||
opened="{ state['change-registry-url'] }"
|
||||
on-close="{ onClose('change-registry-url') }"
|
||||
on-notify="{ props.onNotify }"
|
||||
on-server-change="{ props.onServerChange }"
|
||||
></change-registry-url>
|
||||
<remove-registry-url
|
||||
if="{ !props.readOnlyRegistries }"
|
||||
opened="{ state['remove-registry-url'] }"
|
||||
on-close="{ onClose('remove-registry-url') }"
|
||||
on-notify="{ props.onNotify }"
|
||||
on-server-change="{ props.onServerChange }"
|
||||
></remove-registry-url>
|
||||
<div class="container">
|
||||
<material-button onClick="{ onClick }" waves-center="true" rounded="true" waves-opacity="0.6" waves-duration="600">
|
||||
<i class="material-icons">more_vert</i>
|
||||
</material-button>
|
||||
<material-dropdown-list items="{ dropdownItems.filter(item => item.ro || !props.readOnlyRegistries) }" onSelect="{ onDropdownSelect }"
|
||||
opened="{ state.isDropdownOpened }" />
|
||||
<material-dropdown-list
|
||||
items="{ dropdownItems.filter(item => item.ro || !props.readOnlyRegistries) }"
|
||||
onSelect="{ onDropdownSelect }"
|
||||
opened="{ state.isDropdownOpened }"
|
||||
/>
|
||||
</div>
|
||||
<div class="overlay" onclick="{ onClick }" if="{ state.isDropdownOpened }"></div>
|
||||
<script>
|
||||
|
@ -38,44 +55,48 @@
|
|||
components: {
|
||||
AddRegistryUrl,
|
||||
ChangeRegistryUrl,
|
||||
RemoveRegistryUrl
|
||||
RemoveRegistryUrl,
|
||||
},
|
||||
dropdownItems: [{
|
||||
title: 'Add URL',
|
||||
name: 'add-registry-url',
|
||||
ro: false
|
||||
}, {
|
||||
title: 'Change URL',
|
||||
name: 'change-registry-url',
|
||||
ro: true
|
||||
}, {
|
||||
title: 'Remove URL',
|
||||
name: 'remove-registry-url',
|
||||
ro: false
|
||||
}],
|
||||
dropdownItems: [
|
||||
{
|
||||
title: 'Add URL',
|
||||
name: 'add-registry-url',
|
||||
ro: false,
|
||||
},
|
||||
{
|
||||
title: 'Change URL',
|
||||
name: 'change-registry-url',
|
||||
ro: true,
|
||||
},
|
||||
{
|
||||
title: 'Remove URL',
|
||||
name: 'remove-registry-url',
|
||||
ro: false,
|
||||
},
|
||||
],
|
||||
onDropdownSelect(key, item) {
|
||||
this.update({
|
||||
[item.name]: true,
|
||||
isDropdownOpened: false
|
||||
isDropdownOpened: false,
|
||||
});
|
||||
},
|
||||
onClose(name) {
|
||||
return () => {
|
||||
this.update({
|
||||
[name]: false,
|
||||
isDropdownOpened: false
|
||||
})
|
||||
}
|
||||
isDropdownOpened: false,
|
||||
});
|
||||
};
|
||||
},
|
||||
onClick() {
|
||||
this.update({
|
||||
isDropdownOpened: !this.state.isDropdownOpened
|
||||
})
|
||||
}
|
||||
}
|
||||
isDropdownOpened: !this.state.isDropdownOpened,
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
:host > .container{
|
||||
:host > .container {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 16px;
|
||||
|
@ -128,4 +149,4 @@
|
|||
line-height: 36px;
|
||||
}
|
||||
</style>
|
||||
</dialogs-menu>
|
||||
</dialogs-menu>
|
||||
|
|
|
@ -21,8 +21,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
<ul class="list">
|
||||
<li each="{ url in getRegistryServers() }">
|
||||
<span>
|
||||
<material-button onClick="{ remove }" url="{ url }" rounded="true" waves-color="rgba(158,158,158,.4)"
|
||||
waves-center="true">
|
||||
<material-button
|
||||
onClick="{ remove }"
|
||||
url="{ url }"
|
||||
rounded="true"
|
||||
waves-color="rgba(158,158,158,.4)"
|
||||
waves-center="true"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</material-button>
|
||||
<span class="url">{ url }</span>
|
||||
|
@ -37,18 +42,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
</div>
|
||||
</material-popup>
|
||||
<script>
|
||||
import {
|
||||
getRegistryServers,
|
||||
removeRegistryServers
|
||||
} from '../../scripts/utils';
|
||||
import { getRegistryServers, removeRegistryServers } from '../../scripts/utils';
|
||||
export default {
|
||||
remove(event) {
|
||||
const url = event.currentTarget.attributes.url && event.currentTarget.attributes.url.value;
|
||||
removeRegistryServers(url);
|
||||
setTimeout(() => this.update(), 100);
|
||||
},
|
||||
getRegistryServers
|
||||
}
|
||||
getRegistryServers,
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
:host material-popup .popup material-button {
|
||||
|
@ -59,4 +61,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
color: #777;
|
||||
}
|
||||
</style>
|
||||
</remove-registry-url>
|
||||
</remove-registry-url>
|
||||
|
|
|
@ -19,35 +19,63 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
<material-navbar>
|
||||
<div class="logo">Docker Registry UI</div>
|
||||
<search-bar on-search="{ onSearch }"></search-bar>
|
||||
<dialogs-menu if="{props.singleRegistry !== 'true'}" on-notify="{ notifySnackbar }"
|
||||
on-server-change="{ onServerChange }" default-registries="{ props.defaultRegistries }"
|
||||
read-only-registries="{ truthy(props.readOnlyRegistries) }"></dialogs-menu>
|
||||
<dialogs-menu
|
||||
if="{props.singleRegistry !== 'true'}"
|
||||
on-notify="{ notifySnackbar }"
|
||||
on-server-change="{ onServerChange }"
|
||||
default-registries="{ props.defaultRegistries }"
|
||||
read-only-registries="{ truthy(props.readOnlyRegistries) }"
|
||||
></dialogs-menu>
|
||||
</material-navbar>
|
||||
</header>
|
||||
<main>
|
||||
<router base="#!">
|
||||
<route path="{baseRoute}">
|
||||
<catalog registry-url="{ state.registryUrl }" registry-name="{ state.name }"
|
||||
catalog-elements-limit="{ state.catalogElementsLimit }" on-notify="{ notifySnackbar }"
|
||||
filter-results="{ state.filter }" on-authentication="{ onAuthentication }"
|
||||
show-catalog-nb-tags="{ truthy(props.showCatalogNbTags) }"/>
|
||||
<catalog
|
||||
registry-url="{ state.registryUrl }"
|
||||
registry-name="{ state.name }"
|
||||
catalog-elements-limit="{ state.catalogElementsLimit }"
|
||||
on-notify="{ notifySnackbar }"
|
||||
filter-results="{ state.filter }"
|
||||
on-authentication="{ onAuthentication }"
|
||||
show-catalog-nb-tags="{ truthy(props.showCatalogNbTags) }"
|
||||
/>
|
||||
</route>
|
||||
<route path="{baseRoute}taglist/(.*)">
|
||||
<tag-list registry-url="{ state.registryUrl }" registry-name="{ state.name }" pull-url="{ state.pullUrl }"
|
||||
image="{ router.getTagListImage() }" show-content-digest="{ truthy(props.showContentDigest) }"
|
||||
is-image-remove-activated="{ truthy(props.isImageRemoveActivated) }" on-notify="{ notifySnackbar }"
|
||||
filter-results="{ state.filter }" on-authentication="{ onAuthentication }"></tag-list>
|
||||
<tag-list
|
||||
registry-url="{ state.registryUrl }"
|
||||
registry-name="{ state.name }"
|
||||
pull-url="{ state.pullUrl }"
|
||||
image="{ router.getTagListImage() }"
|
||||
show-content-digest="{ truthy(props.showContentDigest) }"
|
||||
is-image-remove-activated="{ truthy(props.isImageRemoveActivated) }"
|
||||
on-notify="{ notifySnackbar }"
|
||||
filter-results="{ state.filter }"
|
||||
on-authentication="{ onAuthentication }"
|
||||
></tag-list>
|
||||
</route>
|
||||
<route path="{baseRoute}taghistory/(.*)">
|
||||
<tag-history registry-url="{ state.registryUrl }" registry-name="{ state.name }" pull-url="{ state.pullUrl }"
|
||||
image="{ router.getTagHistoryImage() }" tag="{ router.getTagHistoryTag() }"
|
||||
is-image-remove-activated="{ truthy(props.isImageRemoveActivated) }" on-notify="{ notifySnackbar }"
|
||||
on-authentication="{ onAuthentication }" history-custom-labels="{ stringToArray(props.historyCustomLabels) }"></tag-history>
|
||||
<tag-history
|
||||
registry-url="{ state.registryUrl }"
|
||||
registry-name="{ state.name }"
|
||||
pull-url="{ state.pullUrl }"
|
||||
image="{ router.getTagHistoryImage() }"
|
||||
tag="{ router.getTagHistoryTag() }"
|
||||
is-image-remove-activated="{ truthy(props.isImageRemoveActivated) }"
|
||||
on-notify="{ notifySnackbar }"
|
||||
on-authentication="{ onAuthentication }"
|
||||
history-custom-labels="{ stringToArray(props.historyCustomLabels) }"
|
||||
></tag-history>
|
||||
</route>
|
||||
</router>
|
||||
<registry-authentication realm="{ state.realm }" scope="{ state.scope }" service="{ state.service }"
|
||||
on-close="{ onAuthenticationClose }" on-authenticated="{ state.onAuthenticated }"
|
||||
opened="{ state.authenticationDialogOpened }"></registry-authentication>
|
||||
<registry-authentication
|
||||
realm="{ state.realm }"
|
||||
scope="{ state.scope }"
|
||||
service="{ state.service }"
|
||||
on-close="{ onAuthenticationClose }"
|
||||
on-authenticated="{ state.onAuthenticated }"
|
||||
opened="{ state.authenticationDialogOpened }"
|
||||
></registry-authentication>
|
||||
<material-snackbar message="{ state.snackbarMessage }" is-error="{ state.snackbarIsError }"></material-snackbar>
|
||||
</main>
|
||||
<footer>
|
||||
|
@ -64,25 +92,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
</material-footer>
|
||||
</footer>
|
||||
<script>
|
||||
import {
|
||||
version
|
||||
} from '../../package.json';
|
||||
import {
|
||||
Router,
|
||||
Route,
|
||||
} from '@riotjs/route'
|
||||
import { version } from '../../package.json';
|
||||
import { Router, Route } from '@riotjs/route';
|
||||
import Catalog from './catalog/catalog.riot';
|
||||
import TagList from './tag-list/tag-list.riot';
|
||||
import TagHistory from './tag-history/tag-history.riot';
|
||||
import DialogsMenu from './dialogs/dialogs-menu.riot';
|
||||
import SearchBar from './search-bar.riot'
|
||||
import {
|
||||
stripHttps,
|
||||
getRegistryServers,
|
||||
setRegistryServers,
|
||||
truthy,
|
||||
stringToArray
|
||||
} from '../scripts/utils';
|
||||
import SearchBar from './search-bar.riot';
|
||||
import { stripHttps, getRegistryServers, setRegistryServers, truthy, stringToArray } from '../scripts/utils';
|
||||
import router from '../scripts/router';
|
||||
|
||||
export default {
|
||||
|
@ -93,22 +110,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
DialogsMenu,
|
||||
SearchBar,
|
||||
Router,
|
||||
Route
|
||||
Route,
|
||||
},
|
||||
onUpdated(props, state) {
|
||||
state.snackbarIsError = false;
|
||||
state.snackbarMessage = undefined;
|
||||
},
|
||||
onBeforeMount(props) {
|
||||
if ((props.defaultRegistries && props.defaultRegistries.length > 0 && getRegistryServers().length === 0) ||
|
||||
truthy(props.readOnlyRegistries)) {
|
||||
if (
|
||||
(props.defaultRegistries && props.defaultRegistries.length > 0 && getRegistryServers().length === 0) ||
|
||||
truthy(props.readOnlyRegistries)
|
||||
) {
|
||||
setRegistryServers(props.defaultRegistries);
|
||||
}
|
||||
|
||||
// props.singleRegistry === 'true' means old static version
|
||||
const registryUrl = props.registryUrl ||
|
||||
(props.singleRegistry === 'true' ? undefined : (router.getUrlQueryParam() || getRegistryServers(0))) ||
|
||||
(window.location.origin + window.location.pathname.replace(/\/+$/, ''));
|
||||
// props.singleRegistry === 'true' means old static version
|
||||
const registryUrl =
|
||||
props.registryUrl ||
|
||||
(props.singleRegistry === 'true' ? undefined : router.getUrlQueryParam() || getRegistryServers(0)) ||
|
||||
window.location.origin + window.location.pathname.replace(/\/+$/, '');
|
||||
this.state.registryUrl = registryUrl.replace(/\/$/, '').replace(/index(\.html?)?$/, '');
|
||||
this.state.name = props.name || stripHttps(props.registryUrl);
|
||||
this.state.catalogElementsLimit = props.catalogElementsLimit || 100000;
|
||||
|
@ -119,65 +139,59 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
registryUrl,
|
||||
name: stripHttps(registryUrl),
|
||||
pullUrl: this.pullUrl(registryUrl),
|
||||
snackbarMessage: 'Registry server changed to `' + registryUrl + '`.'
|
||||
})
|
||||
snackbarMessage: 'Registry server changed to `' + registryUrl + '`.',
|
||||
});
|
||||
},
|
||||
onAuthentication(opts, onAuthenticated) {
|
||||
if (opts && opts.realm && opts.service && opts.scope) {
|
||||
const {
|
||||
realm,
|
||||
service,
|
||||
scope,
|
||||
} = opts;
|
||||
const req = new XMLHttpRequest()
|
||||
const { realm, service, scope } = opts;
|
||||
const req = new XMLHttpRequest();
|
||||
req.addEventListener('loadend', () => {
|
||||
try {
|
||||
const bearer = JSON.parse(req.responseText);
|
||||
onAuthenticated(bearer)
|
||||
onAuthenticated(bearer);
|
||||
} catch (e) {
|
||||
this.notifySnackbar(`Failed to log in: ${e.message}`, true)
|
||||
this.notifySnackbar(`Failed to log in: ${e.message}`, true);
|
||||
}
|
||||
})
|
||||
req.open('GET', `${realm}?service=${service}&scope=${scope}`)
|
||||
req.send()
|
||||
});
|
||||
req.open('GET', `${realm}?service=${service}&scope=${scope}`);
|
||||
req.send();
|
||||
} else {
|
||||
onAuthenticated()
|
||||
onAuthenticated();
|
||||
}
|
||||
},
|
||||
onAuthenticationClose() {
|
||||
this.update({
|
||||
authenticationDialogOpened: false
|
||||
})
|
||||
authenticationDialogOpened: false,
|
||||
});
|
||||
},
|
||||
pullUrl(registryUrl, pullUrl) {
|
||||
const url = pullUrl ||
|
||||
(registryUrl && registryUrl.length > 0 && registryUrl) ||
|
||||
window.location.host;
|
||||
const url = pullUrl || (registryUrl && registryUrl.length > 0 && registryUrl) || window.location.host;
|
||||
return stripHttps(url);
|
||||
},
|
||||
notifySnackbar(message, isError) {
|
||||
if (typeof message === 'string') {
|
||||
this.update({
|
||||
snackbarMessage: message,
|
||||
snackbarIsError: isError || false
|
||||
snackbarIsError: isError || false,
|
||||
});
|
||||
} else if (message && message.message) {
|
||||
this.update({
|
||||
snackbarMessage: message.message,
|
||||
snackbarIsError: message.isError
|
||||
snackbarIsError: message.isError,
|
||||
});
|
||||
}
|
||||
},
|
||||
onSearch(value) {
|
||||
this.update({
|
||||
filter: value
|
||||
})
|
||||
filter: value,
|
||||
});
|
||||
},
|
||||
baseRoute: '([^#]*?)/(\\?[^#]*?)?(#!)?(/?)',
|
||||
router,
|
||||
version,
|
||||
truthy,
|
||||
stringToArray
|
||||
}
|
||||
stringToArray,
|
||||
};
|
||||
</script>
|
||||
</docker-registry-ui>
|
||||
</docker-registry-ui>
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<search-bar>
|
||||
<material-input placeholder="Search in page"></material-input>
|
||||
<script>
|
||||
import {
|
||||
router
|
||||
} from '@riotjs/route';
|
||||
import { router } from '@riotjs/route';
|
||||
|
||||
export default {
|
||||
onMounted(props, state) {
|
||||
|
@ -11,16 +9,16 @@
|
|||
let value = '';
|
||||
const notify = () => {
|
||||
if (value !== input.value) {
|
||||
props.onSearch(input.value.toLowerCase())
|
||||
props.onSearch(input.value.toLowerCase());
|
||||
}
|
||||
value = input.value;
|
||||
}
|
||||
};
|
||||
input.addEventListener('keyup', notify);
|
||||
router.on.value(() => {
|
||||
input.value = '';
|
||||
notify();
|
||||
})
|
||||
window.addEventListener('keydown', e => {
|
||||
});
|
||||
window.addEventListener('keydown', (e) => {
|
||||
// F3 or CTRL + F
|
||||
if (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70)) {
|
||||
// already focused, fallback to default behavior
|
||||
|
@ -31,9 +29,9 @@
|
|||
input.focus();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export function matchSearch(search, value) {
|
||||
return !search || (value && value.toLowerCase().indexOf(search) >= 0);
|
||||
|
@ -57,4 +55,4 @@
|
|||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
</search-bar>
|
||||
</search-bar>
|
||||
|
|
|
@ -16,16 +16,19 @@
|
|||
-->
|
||||
<copy-to-clipboard>
|
||||
<div class="copy-to-clipboard">
|
||||
<input style="display: none; width: 1px; height: 1px;" value="{ getDockerCmd(props) }">
|
||||
<material-button waves-center="true" rounded="true" waves-color="#ddd" onClick="{ copy }"
|
||||
title="Copy pull command.">
|
||||
<input style="display: none; width: 1px; height: 1px" value="{ getDockerCmd(props) }" />
|
||||
<material-button
|
||||
waves-center="true"
|
||||
rounded="true"
|
||||
waves-color="#ddd"
|
||||
onClick="{ copy }"
|
||||
title="Copy pull command."
|
||||
>
|
||||
<i class="material-icons">content_copy</i>
|
||||
</material-button>
|
||||
</div>
|
||||
<script>
|
||||
import {
|
||||
ERROR_CAN_NOT_READ_CONTENT_DIGEST
|
||||
} from '../../scripts/utils';
|
||||
import { ERROR_CAN_NOT_READ_CONTENT_DIGEST } from '../../scripts/utils';
|
||||
export default {
|
||||
onMounted(props, state) {
|
||||
this.load(props, state);
|
||||
|
@ -37,13 +40,13 @@
|
|||
if (props.target === 'tag') {
|
||||
return `docker pull ${props.pullUrl}/${props.image.name}:${props.image.tag}`;
|
||||
} else {
|
||||
return `docker pull ${props.pullUrl}/${props.image.name}@${props.image.digest}`
|
||||
return `docker pull ${props.pullUrl}/${props.image.name}@${props.image.digest}`;
|
||||
}
|
||||
},
|
||||
load(props, state) {
|
||||
if (props.target !== 'tag' && !props.image.digest) {
|
||||
props.image.one('content-digest', (digest) => {
|
||||
this.update()
|
||||
this.update();
|
||||
});
|
||||
props.image.trigger('get-content-digest');
|
||||
}
|
||||
|
@ -60,8 +63,8 @@
|
|||
document.execCommand('copy');
|
||||
copyText.style.display = 'none';
|
||||
|
||||
this.props.onNotify('`' + copyText.value + '` has been copied to clipboard.')
|
||||
}
|
||||
}
|
||||
this.props.onNotify('`' + copyText.value + '` has been copied to clipboard.');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
</copy-to-clipboard>
|
||||
</copy-to-clipboard>
|
||||
|
|
|
@ -39,12 +39,12 @@ Copyright (C) 2016-2021 Jones Magloire @Joxit
|
|||
onResize(chars) {
|
||||
if (chars !== this.state.chars) {
|
||||
this.update({
|
||||
chars
|
||||
chars,
|
||||
});
|
||||
}
|
||||
},
|
||||
getTitle(image, chars) {
|
||||
return chars >= 70 ? '' : (image.digest || '');
|
||||
return chars >= 70 ? '' : image.digest || '';
|
||||
},
|
||||
getDigest(image, chars) {
|
||||
if (chars >= 70) {
|
||||
|
@ -54,7 +54,7 @@ Copyright (C) 2016-2021 Jones Magloire @Joxit
|
|||
} else {
|
||||
return image.digest && image.digest.slice(0, chars) + '...';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
</image-content-digest>
|
||||
</image-content-digest>
|
||||
|
|
|
@ -17,9 +17,7 @@
|
|||
<image-size>
|
||||
<div title="Compressed size of your image.">{ getImageSize(props.image) }</div>
|
||||
<script>
|
||||
import {
|
||||
bytesToSize,
|
||||
} from '../../scripts/utils';
|
||||
import { bytesToSize } from '../../scripts/utils';
|
||||
export default {
|
||||
onMounted(props, state) {
|
||||
this.load(props, state);
|
||||
|
@ -33,15 +31,14 @@
|
|||
}
|
||||
props.image.on('size', (size) => {
|
||||
this.update({
|
||||
size
|
||||
size,
|
||||
});
|
||||
});
|
||||
props.image.trigger('get-size');
|
||||
|
||||
},
|
||||
getImageSize(image) {
|
||||
return bytesToSize(image.size)
|
||||
}
|
||||
}
|
||||
return bytesToSize(image.size);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
</image-size>
|
||||
</image-size>
|
||||
|
|
|
@ -17,14 +17,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
<pagination>
|
||||
<div class="conatianer">
|
||||
<div class="pagination-centered">
|
||||
<material-button waves-color="rgba(158,158,158,.4)" each="{p in props.pages}"
|
||||
<material-button
|
||||
waves-color="rgba(158,158,158,.4)"
|
||||
each="{p in props.pages}"
|
||||
class="{ p.current ? 'current' : ''} { p['space-left'] ? 'space-left' : '' } { p['space-right'] ? 'space-right' : ''}"
|
||||
onClick="{() => props.onPageUpdate(p.page)}">
|
||||
onClick="{() => props.onPageUpdate(p.page)}"
|
||||
>
|
||||
<i if="{ p.icon }" class="material-icons">{ p.icon }</i>
|
||||
<div if="{ !p.icon }">{ p.page }</div>
|
||||
</material-button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
</script>
|
||||
</pagination>
|
||||
<script></script>
|
||||
</pagination>
|
||||
|
|
|
@ -15,29 +15,35 @@ 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/>.
|
||||
-->
|
||||
<remove-image>
|
||||
<material-button waves-center="true" rounded="true" waves-color="#ddd" title="This will delete the image."
|
||||
if="{ !props.multiDelete }" disabled="{ !state.digest }" onClick="{ deleteImage }">
|
||||
<material-button
|
||||
waves-center="true"
|
||||
rounded="true"
|
||||
waves-color="#ddd"
|
||||
title="This will delete the image."
|
||||
if="{ !props.multiDelete }"
|
||||
disabled="{ !state.digest }"
|
||||
onClick="{ deleteImage }"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</material-button>
|
||||
<material-checkbox if="{ props.multiDelete }" title="Select this tag to delete it." disabled="{ !state.digest }"
|
||||
onChange="{ handleCheckboxChange }" checked="{ state.checked }">
|
||||
<material-checkbox
|
||||
if="{ props.multiDelete }"
|
||||
title="Select this tag to delete it."
|
||||
disabled="{ !state.digest }"
|
||||
onChange="{ handleCheckboxChange }"
|
||||
checked="{ state.checked }"
|
||||
>
|
||||
</material-checkbox>
|
||||
<script>
|
||||
import {
|
||||
Http
|
||||
} from '../../scripts/http';
|
||||
import router from '../../scripts/router'
|
||||
import {
|
||||
ACTION_CHECK_TO_DELETE,
|
||||
ACTION_UNCHECK_TO_DELETE,
|
||||
ACTION_DELETE_IMAGE
|
||||
} from './tag-table.riot';
|
||||
import { Http } from '../../scripts/http';
|
||||
import router from '../../scripts/router';
|
||||
import { ACTION_CHECK_TO_DELETE, ACTION_UNCHECK_TO_DELETE, ACTION_DELETE_IMAGE } from './tag-table.riot';
|
||||
export default {
|
||||
onBeforeMount(props, state) {
|
||||
state.checked = props.checked;
|
||||
props.image.one('content-digest', (digest) => {
|
||||
this.update({
|
||||
digest
|
||||
digest,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -53,7 +59,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
handleCheckboxChange(checked) {
|
||||
const action = checked ? ACTION_CHECK_TO_DELETE : ACTION_UNCHECK_TO_DELETE;
|
||||
this.props.handleCheckboxChange(action, this.props.image);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
</remove-image>
|
||||
</remove-image>
|
||||
|
|
|
@ -16,15 +16,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
-->
|
||||
<tag-list>
|
||||
<material-card class="header">
|
||||
<div class="material-card-title-action ">
|
||||
<div class="material-card-title-action">
|
||||
<material-button waves-center="true" rounded="true" waves-color="#ddd" onClick="{ router.home }">
|
||||
<i class="material-icons">arrow_back</i>
|
||||
</material-button>
|
||||
<h2>
|
||||
Tags of { props.image }
|
||||
<div class="source-hint">
|
||||
Sourced from { state.registryName + '/' + props.image }
|
||||
</div>
|
||||
<div class="source-hint">Sourced from { state.registryName + '/' + props.image }</div>
|
||||
<div class="item-count">{ state.tags.length } tags</div>
|
||||
</h2>
|
||||
</div>
|
||||
|
@ -36,30 +34,31 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
<pagination pages="{ getPageLabels(state.page, getNumPages(state.tags)) }" onPageUpdate="{onPageUpdate}"></pagination>
|
||||
|
||||
<tag-table if="{ state.loadend }" tags="{state.tags}" asc="{state.asc}" page="{ state.page }"
|
||||
show-content-digest="{props.showContentDigest}" is-image-remove-activated="{props.isImageRemoveActivated}"
|
||||
onReverseOrder="{ onReverseOrder }" registry-url="{ props.registryUrl }" pull-url="{ props.pullUrl }"
|
||||
on-notify="{ props.onNotify }" filter-results="{ props.filterResults }"
|
||||
on-authentication="{ props.onAuthentication }">
|
||||
<tag-table
|
||||
if="{ state.loadend }"
|
||||
tags="{state.tags}"
|
||||
asc="{state.asc}"
|
||||
page="{ state.page }"
|
||||
show-content-digest="{props.showContentDigest}"
|
||||
is-image-remove-activated="{props.isImageRemoveActivated}"
|
||||
onReverseOrder="{ onReverseOrder }"
|
||||
registry-url="{ props.registryUrl }"
|
||||
pull-url="{ props.pullUrl }"
|
||||
on-notify="{ props.onNotify }"
|
||||
filter-results="{ props.filterResults }"
|
||||
on-authentication="{ props.onAuthentication }"
|
||||
>
|
||||
</tag-table>
|
||||
|
||||
<pagination pages="{ getPageLabels(state.page, getNumPages(state.tags)) }" onPageUpdate="{onPageUpdate}"></pagination>
|
||||
|
||||
<script>
|
||||
import {
|
||||
Http
|
||||
} from '../../scripts/http';
|
||||
import {
|
||||
DockerImage,
|
||||
compare
|
||||
} from '../../scripts/docker-image';
|
||||
import {
|
||||
getNumPages,
|
||||
getPageLabels
|
||||
} from '../../scripts/utils'
|
||||
import Pagination from './pagination.riot'
|
||||
import TagTable from './tag-table.riot'
|
||||
import router from '../../scripts/router'
|
||||
import { Http } from '../../scripts/http';
|
||||
import { DockerImage, compare } from '../../scripts/docker-image';
|
||||
import { getNumPages, getPageLabels } from '../../scripts/utils';
|
||||
import Pagination from './pagination.riot';
|
||||
import TagTable from './tag-table.riot';
|
||||
import router from '../../scripts/router';
|
||||
export default {
|
||||
components: {
|
||||
Pagination,
|
||||
|
@ -71,11 +70,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
tags: [],
|
||||
loadend: false,
|
||||
asc: true,
|
||||
page: router.getPageQueryParam() || 1
|
||||
}
|
||||
page: router.getPageQueryParam() || 1,
|
||||
};
|
||||
},
|
||||
onMounted(props, state) {
|
||||
this.display(props, state)
|
||||
this.display(props, state);
|
||||
window.addEventListener('resize', this.onResize);
|
||||
// this may be run before the final document size is available, so schedule
|
||||
// a correction once everything is set up.
|
||||
|
@ -85,23 +84,26 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
state.tags = [];
|
||||
const self = this;
|
||||
const oReq = new Http({
|
||||
onAuthentication: props.onAuthentication
|
||||
onAuthentication: props.onAuthentication,
|
||||
});
|
||||
oReq.addEventListener('load', function () {
|
||||
if (this.status == 200) {
|
||||
if (this.status === 200) {
|
||||
const tags = (JSON.parse(this.responseText).tags || [])
|
||||
.map(tag => new DockerImage(props.image, tag, {
|
||||
registryUrl: props.registryUrl,
|
||||
onNotify: props.onNotify,
|
||||
onAuthentication: props.onAuthentication
|
||||
}))
|
||||
.map(
|
||||
(tag) =>
|
||||
new DockerImage(props.image, tag, {
|
||||
registryUrl: props.registryUrl,
|
||||
onNotify: props.onNotify,
|
||||
onAuthentication: props.onAuthentication,
|
||||
})
|
||||
)
|
||||
.sort(compare);
|
||||
window.requestAnimationFrame(self.onResize);
|
||||
self.update({
|
||||
page: Math.min(state.page, getNumPages(tags)),
|
||||
tags
|
||||
})
|
||||
} else if (this.status == 404) {
|
||||
tags,
|
||||
});
|
||||
} else if (this.status === 404) {
|
||||
self.props.onNotify('Server not found', true);
|
||||
} else {
|
||||
self.props.onNotify(this.responseText, true);
|
||||
|
@ -113,7 +115,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
});
|
||||
oReq.addEventListener('loadend', function () {
|
||||
self.update({
|
||||
loadend: true
|
||||
loadend: true,
|
||||
});
|
||||
});
|
||||
oReq.open('GET', props.registryUrl + '/v2/' + props.image + '/tags/list');
|
||||
|
@ -123,7 +125,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
|
||||
onPageUpdate(page) {
|
||||
this.update({
|
||||
page: page
|
||||
page: page,
|
||||
});
|
||||
router.updatePageQueryParam(page);
|
||||
},
|
||||
|
@ -143,8 +145,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
// SHA256:12345678 + scaled between 1024 and 1440px
|
||||
chars = 15 + 56 * ((innerWidth - 1024) / 416);
|
||||
}
|
||||
if (max > 20) chars -= (max - 20);
|
||||
chars = Math.floor(chars)
|
||||
if (max > 20) chars -= max - 20;
|
||||
chars = Math.floor(chars);
|
||||
this.state.tags.map(function (image) {
|
||||
image.trigger('content-digest-chars', chars);
|
||||
});
|
||||
|
@ -162,7 +164,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
},
|
||||
getPageLabels,
|
||||
getNumPages,
|
||||
router
|
||||
}
|
||||
router,
|
||||
};
|
||||
</script>
|
||||
</tag-list>
|
||||
</tag-list>
|
||||
|
|
|
@ -15,39 +15,61 @@ 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/>.
|
||||
-->
|
||||
<tag-table>
|
||||
<confirm-delete-image opened="{ state.confirmDeleteImage }" on-click="{ onConfirmDeleteImageClick }"
|
||||
registry-url="{ props.registryUrl }" on-notify="{ props.onNotify }" on-authentication="{ props.onAuthentication }"
|
||||
tags="{ props.tags }" to-delete="{ state.toDelete }"></confirm-delete-image>
|
||||
<confirm-delete-image
|
||||
opened="{ state.confirmDeleteImage }"
|
||||
on-click="{ onConfirmDeleteImageClick }"
|
||||
registry-url="{ props.registryUrl }"
|
||||
on-notify="{ props.onNotify }"
|
||||
on-authentication="{ props.onAuthentication }"
|
||||
tags="{ props.tags }"
|
||||
to-delete="{ state.toDelete }"
|
||||
></confirm-delete-image>
|
||||
<material-card class="taglist">
|
||||
<table style="border: none;">
|
||||
<table style="border: none">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
class="creation-date { (state.desc && state.orderType === 'date') ? 'material-card-th-sorted-descending' : 'material-card-th-sorted-ascending' }"
|
||||
onclick="{() => onPageReorder('date') }">
|
||||
onclick="{() => onPageReorder('date') }"
|
||||
>
|
||||
Creation date
|
||||
</th>
|
||||
<th
|
||||
class="image-size { (state.desc && state.orderType === 'size') ? 'material-card-th-sorted-descending' : 'material-card-th-sorted-ascending' }"
|
||||
onclick="{() => onPageReorder('size') }">
|
||||
onclick="{() => onPageReorder('size') }"
|
||||
>
|
||||
Size
|
||||
</th>
|
||||
<th id="image-content-digest-header" if="{ props.showContentDigest }">Content Digest</th>
|
||||
|
||||
<th id="image-tag-header"
|
||||
<th
|
||||
id="image-tag-header"
|
||||
class="{ props.asc ? 'material-card-th-sorted-ascending' : 'material-card-th-sorted-descending' }"
|
||||
onclick="{ onReverseOrder }">Tag
|
||||
onclick="{ onReverseOrder }"
|
||||
>
|
||||
Tag
|
||||
</th>
|
||||
<th class="show-tag-history">History</th>
|
||||
<th class="remove-tag { state.toDelete.size > 0 && !state.singleDeleteAction ? 'delete' : '' }"
|
||||
if="{ props.isImageRemoveActivated }">
|
||||
<material-checkbox class="indeterminate" checked="{ state.multiDelete }"
|
||||
<th
|
||||
class="remove-tag { state.toDelete.size > 0 && !state.singleDeleteAction ? 'delete' : '' }"
|
||||
if="{ props.isImageRemoveActivated }"
|
||||
>
|
||||
<material-checkbox
|
||||
class="indeterminate"
|
||||
checked="{ state.multiDelete }"
|
||||
if="{ state.toDelete.size === 0 || state.singleDeleteAction }"
|
||||
title="Toggle multi-delete. Alt+Click to select all tags." onChange="{ onRemoveImageHeaderChange }">
|
||||
title="Toggle multi-delete. Alt+Click to select all tags."
|
||||
onChange="{ onRemoveImageHeaderChange }"
|
||||
>
|
||||
</material-checkbox>
|
||||
<material-button waves-center="true" rounded="true" waves-color="#ddd"
|
||||
title="This will delete selected images." onClick="{ deleteImages }"
|
||||
if="{ state.toDelete.size > 0 && !state.singleDeleteAction }">
|
||||
<material-button
|
||||
waves-center="true"
|
||||
rounded="true"
|
||||
waves-color="#ddd"
|
||||
title="This will delete selected images."
|
||||
onClick="{ deleteImages }"
|
||||
if="{ state.toDelete.size > 0 && !state.singleDeleteAction }"
|
||||
>
|
||||
<i class="material-icons">delete</i>
|
||||
</material-button>
|
||||
</th>
|
||||
|
@ -63,30 +85,42 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
</td>
|
||||
<td if="{ props.showContentDigest }">
|
||||
<image-content-digest image="{ image }" />
|
||||
<copy-to-clipboard target="digest" image="{ image }" pull-url="{ props.pullUrl }"
|
||||
on-notify="{ props.onNotify }" />
|
||||
<copy-to-clipboard
|
||||
target="digest"
|
||||
image="{ image }"
|
||||
pull-url="{ props.pullUrl }"
|
||||
on-notify="{ props.onNotify }"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<image-tag image="{ image }" />
|
||||
<copy-to-clipboard target="tag" image="{ image }" pull-url="{ props.pullUrl }"
|
||||
on-notify="{ props.onNotify }" />
|
||||
<copy-to-clipboard
|
||||
target="tag"
|
||||
image="{ image }"
|
||||
pull-url="{ props.pullUrl }"
|
||||
on-notify="{ props.onNotify }"
|
||||
/>
|
||||
</td>
|
||||
<td class="show-tag-history">
|
||||
<tag-history-button image="{ image }" />
|
||||
</td>
|
||||
<td if="{ props.isImageRemoveActivated }" class="remove-tag">
|
||||
<remove-image multi-delete="{ state.multiDelete }" image="{ image }" registry-url="{ props.registryUrl }"
|
||||
handleCheckboxChange="{ onRemoveImageChange }" checked="{ state.toDelete.has(image) }"
|
||||
on-notify="{ props.onNotify }" on-authentication="{ props.onAuthentication }" />
|
||||
<remove-image
|
||||
multi-delete="{ state.multiDelete }"
|
||||
image="{ image }"
|
||||
registry-url="{ props.registryUrl }"
|
||||
handleCheckboxChange="{ onRemoveImageChange }"
|
||||
checked="{ state.toDelete.has(image) }"
|
||||
on-notify="{ props.onNotify }"
|
||||
on-authentication="{ props.onAuthentication }"
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</material-card>
|
||||
<script>
|
||||
import {
|
||||
getPage,
|
||||
} from '../../scripts/utils';
|
||||
import { getPage } from '../../scripts/utils';
|
||||
import ImageDate from './image-date.riot';
|
||||
import ImageSize from './image-size.riot';
|
||||
import ImageTag from './image-tag.riot';
|
||||
|
@ -94,9 +128,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
import CopyToClipboard from './copy-to-clipboard.riot';
|
||||
import TagHistoryButton from './tag-history-button.riot';
|
||||
import RemoveImage from './remove-image.riot';
|
||||
import {
|
||||
matchSearch
|
||||
} from '../search-bar.riot';
|
||||
import { matchSearch } from '../search-bar.riot';
|
||||
import ConfirmDeleteImage from '../dialogs/confirm-delete-image.riot';
|
||||
const ACTION_CHECK_TO_DELETE = 'CHECK';
|
||||
const ACTION_UNCHECK_TO_DELETE = 'UNCHECK';
|
||||
|
@ -118,18 +150,18 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
toDelete: new Set(),
|
||||
multiDelete: false,
|
||||
page: props.page,
|
||||
}
|
||||
};
|
||||
},
|
||||
onBeforeUpdate(props, state) {
|
||||
if (state.page !== props.page) {
|
||||
state.toDelete.clear();
|
||||
}
|
||||
state.page = props.page
|
||||
state.page = props.page;
|
||||
},
|
||||
deleteImages() {
|
||||
this.update({
|
||||
confirmDeleteImage: true
|
||||
})
|
||||
confirmDeleteImage: true,
|
||||
});
|
||||
},
|
||||
onConfirmDeleteImageClick() {
|
||||
if (this.state.singleDeleteAction) {
|
||||
|
@ -137,22 +169,23 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
}
|
||||
this.update({
|
||||
singleDeleteAction: false,
|
||||
confirmDeleteImage: false
|
||||
})
|
||||
confirmDeleteImage: false,
|
||||
});
|
||||
},
|
||||
onRemoveImageHeaderChange(checked, event) {
|
||||
if (event.altKey === true) {
|
||||
const tags = getPage(this.props.tags, this.props.page);
|
||||
tags.filter(image => matchSearch(this.props.filterResults, image.tag))
|
||||
.forEach(tag => this.state.toDelete.add(tag));
|
||||
tags
|
||||
.filter((image) => matchSearch(this.props.filterResults, image.tag))
|
||||
.forEach((tag) => this.state.toDelete.add(tag));
|
||||
this.update({
|
||||
multiDelete: true,
|
||||
toDelete: this.state.toDelete
|
||||
})
|
||||
toDelete: this.state.toDelete,
|
||||
});
|
||||
} else {
|
||||
this.update({
|
||||
multiDelete: checked
|
||||
})
|
||||
multiDelete: checked,
|
||||
});
|
||||
}
|
||||
},
|
||||
onRemoveImageChange(action, image) {
|
||||
|
@ -177,8 +210,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
this.update({
|
||||
toDelete: this.state.toDelete,
|
||||
confirmDeleteImage,
|
||||
singleDeleteAction
|
||||
})
|
||||
singleDeleteAction,
|
||||
});
|
||||
},
|
||||
onReverseOrder() {
|
||||
this.state.orderType = null;
|
||||
|
@ -188,30 +221,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
onPageReorder(type) {
|
||||
this.update({
|
||||
orderType: type,
|
||||
desc: (this.state.orderType && this.state.orderType !== type) || !this.state.desc
|
||||
})
|
||||
desc: (this.state.orderType && this.state.orderType !== type) || !this.state.desc,
|
||||
});
|
||||
},
|
||||
getPage(tags, page) {
|
||||
const sortedTags = getPage(tags, page);
|
||||
if (this.state.orderType === 'date') {
|
||||
sortedTags.sort((e1, e2) =>
|
||||
!this.state.desc ?
|
||||
e2.creationDate.getTime() - e1.creationDate.getTime() :
|
||||
e1.creationDate.getTime() - e2.creationDate.getTime());
|
||||
!this.state.desc
|
||||
? e2.creationDate.getTime() - e1.creationDate.getTime()
|
||||
: e1.creationDate.getTime() - e2.creationDate.getTime()
|
||||
);
|
||||
} else if (this.state.orderType === 'size') {
|
||||
sortedTags.sort((e1, e2) =>
|
||||
!this.state.desc ?
|
||||
e2.size - e1.size :
|
||||
e1.size - e2.size);
|
||||
sortedTags.sort((e1, e2) => (!this.state.desc ? e2.size - e1.size : e1.size - e2.size));
|
||||
}
|
||||
return sortedTags;
|
||||
},
|
||||
matchSearch
|
||||
}
|
||||
export {
|
||||
ACTION_CHECK_TO_DELETE,
|
||||
ACTION_UNCHECK_TO_DELETE,
|
||||
ACTION_DELETE_IMAGE
|
||||
}
|
||||
matchSearch,
|
||||
};
|
||||
export { ACTION_CHECK_TO_DELETE, ACTION_UNCHECK_TO_DELETE, ACTION_DELETE_IMAGE };
|
||||
</script>
|
||||
</tag-table>
|
||||
</tag-table>
|
||||
|
|
|
@ -97,7 +97,7 @@ export class DockerImage {
|
|||
const oReq = new Http({ onAuthentication: this.opts.onAuthentication });
|
||||
const self = this;
|
||||
oReq.addEventListener('loadend', function () {
|
||||
if (this.status == 200 || this.status == 202) {
|
||||
if (this.status === 200 || this.status === 202) {
|
||||
const response = JSON.parse(this.responseText);
|
||||
if (response.mediaType === 'application/vnd.docker.distribution.manifest.list.v2+json' && self.opts.list) {
|
||||
self.trigger('list', response);
|
||||
|
@ -131,7 +131,7 @@ export class DockerImage {
|
|||
self.trigger('blobs');
|
||||
self.trigger('oci-image');
|
||||
}
|
||||
} else if (this.status == 404) {
|
||||
} else if (this.status === 404) {
|
||||
self.opts.onNotify(`Manifest for ${self.name}:${self.tag} not found`, true);
|
||||
} else {
|
||||
self.opts.onNotify(this.responseText);
|
||||
|
@ -149,7 +149,7 @@ export class DockerImage {
|
|||
const oReq = new Http({ onAuthentication: this.opts.onAuthentication });
|
||||
const self = this;
|
||||
oReq.addEventListener('loadend', function () {
|
||||
if (this.status == 200 || this.status == 202) {
|
||||
if (this.status === 200 || this.status === 202) {
|
||||
const response = JSON.parse(this.responseText);
|
||||
self.creationDate = new Date(response.created);
|
||||
self.blobs = response;
|
||||
|
@ -164,7 +164,7 @@ export class DockerImage {
|
|||
self.blobs.id = blob.replace('sha256:', '');
|
||||
self.trigger('creation-date', self.creationDate);
|
||||
self.trigger('blobs', self.blobs);
|
||||
} else if (this.status == 404) {
|
||||
} else if (this.status === 404) {
|
||||
self.opts.onNotify(`Blobs for ${self.name}:${self.tag} not found`, true);
|
||||
} else {
|
||||
self.opts.onNotify(this.responseText);
|
||||
|
|
|
@ -52,7 +52,7 @@ export class Http {
|
|||
switch (e) {
|
||||
case 'loadend': {
|
||||
self.oReq.addEventListener('loadend', function () {
|
||||
if (this.status == 401 && !this.withCredentials) {
|
||||
if (this.status === 401 && !this.withCredentials) {
|
||||
const tokenAuth =
|
||||
this.hasHeader('www-authenticate') && parseAuthenticateHeader(this.getResponseHeader('www-authenticate'));
|
||||
self.onAuthentication(tokenAuth, (bearer) => {
|
||||
|
|
|
@ -4,7 +4,7 @@ export function bytesToSize(bytes) {
|
|||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (bytes == undefined || isNaN(bytes)) {
|
||||
return '?';
|
||||
} else if (bytes == 0) {
|
||||
} else if (bytes === 0) {
|
||||
return '0 Byte';
|
||||
}
|
||||
const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue