mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2025-04-27 07:29:54 +03:00
feat(catalog): show number of tags per image (#239)
This commit is contained in:
parent
05cbb51125
commit
19e72e4a5f
10 changed files with 84 additions and 39 deletions
|
@ -20,6 +20,7 @@ WORKDIR /usr/share/nginx/html/
|
|||
|
||||
ENV NGINX_PROXY_HEADER_Host '$http_host'
|
||||
ENV NGINX_LISTEN_PORT '80'
|
||||
ENV SHOW_CATALOG_NB_TAGS 'false'
|
||||
|
||||
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY bin/entrypoint /docker-entrypoint.d/90-docker-registry-ui.sh
|
||||
|
|
|
@ -99,6 +99,7 @@ Some env options are available for use this interface for **only one server**.
|
|||
- `NGINX_LISTEN_PORT`: Listen on a port other than 80. (default: `80` when the user is root, `8080` otherwise).
|
||||
- `DEFAULT_REGISTRIES`: List of comma separated registry URLs (e.g `http://registry.example.com,http://registry:5000`), available only when `SINGLE_REGISTRY=false`. (default: ` `).
|
||||
- `READ_ONLY_REGISTRIES`: Desactivate dialog for remove and add new registries, available only when `SINGLE_REGISTRY=false`. (default: `false`).
|
||||
- `SHOW_CATALOG_NB_TAGS`: Show number of tags per images on catalog page. This will produce + nb images requests, not recommended on large registries. (default: `false`).
|
||||
|
||||
There are some examples with [docker-compose](https://docs.docker.com/compose/) and docker-registry-ui as proxy [here](https://github.com/Joxit/docker-registry-ui/tree/main/examples/ui-as-proxy/) or docker-registry-ui as standalone [here](https://github.com/Joxit/docker-registry-ui/tree/main/examples/ui-as-standalone/).
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ WORKDIR /usr/share/nginx/html/
|
|||
|
||||
ENV NGINX_PROXY_HEADER_Host '$http_host'
|
||||
ENV NGINX_LISTEN_PORT '80'
|
||||
ENV SHOW_CATALOG_NB_TAGS 'false'
|
||||
|
||||
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY bin/entrypoint /docker-entrypoint.d/90-docker-registry-ui.sh
|
||||
|
|
|
@ -20,6 +20,7 @@ WORKDIR /usr/share/nginx/html/
|
|||
|
||||
ENV NGINX_PROXY_HEADER_Host '$http_host'
|
||||
ENV NGINX_LISTEN_PORT '80'
|
||||
ENV SHOW_CATALOG_NB_TAGS 'false'
|
||||
|
||||
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY bin/entrypoint /docker-entrypoint.d/90-docker-registry-ui.sh
|
||||
|
|
|
@ -8,6 +8,7 @@ sed -i "s~\${CATALOG_ELEMENTS_LIMIT}~${CATALOG_ELEMENTS_LIMIT}~" index.html
|
|||
sed -i "s~\${SHOW_CONTENT_DIGEST}~${SHOW_CONTENT_DIGEST}~" index.html
|
||||
sed -i "s~\${DEFAULT_REGISTRIES}~${DEFAULT_REGISTRIES}~" index.html
|
||||
sed -i "s~\${READ_ONLY_REGISTRIES}~${READ_ONLY_REGISTRIES}~" index.html
|
||||
sed -i "s~\${SHOW_CATALOG_NB_TAGS}~${SHOW_CATALOG_NB_TAGS}~" index.html
|
||||
|
||||
if [ -z "${DELETE_IMAGES}" ] || [ "${DELETE_IMAGES}" = false ] ; then
|
||||
sed -i "s/\${DELETE_IMAGES}/false/" index.html
|
||||
|
|
|
@ -20,6 +20,7 @@ WORKDIR /usr/share/nginx/html/
|
|||
|
||||
ENV NGINX_PROXY_HEADER_Host '$http_host'
|
||||
ENV NGINX_LISTEN_PORT '80'
|
||||
ENV SHOW_CATALOG_NB_TAGS 'false'
|
||||
|
||||
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY bin/entrypoint /docker-entrypoint.d/90-docker-registry-ui.sh
|
||||
|
|
|
@ -16,11 +16,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
-->
|
||||
<catalog-element>
|
||||
<!-- Begin of tag -->
|
||||
<div class="content"
|
||||
if="{!props.filterResults || state.nImages > 0 || matchSearch(props.filterResults, state.image)}">
|
||||
<div
|
||||
class="content"
|
||||
if="{!props.filterResults || state.nImages > 0 || matchSearch(props.filterResults, state.image)}"
|
||||
>
|
||||
<material-card class="list highlight" expanded="{state.expanded}" onclick="{ onClick }">
|
||||
<material-waves onmousedown="{this.triggerLaunch}" center="true" color="#ddd"
|
||||
setLaunchListener="{ setLaunchListener }" />
|
||||
<material-waves center="true" color="#ddd"></material-waves>
|
||||
<span>
|
||||
<i class="material-icons">send</i>
|
||||
{ state.image || state.repo }
|
||||
|
@ -28,17 +29,28 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
{ state.nImages } images
|
||||
<i class="material-icons animated {state.expanded ? 'expanded' : ''}">expand_more</i>
|
||||
</div>
|
||||
<div if="{props.showCatalogNbTags && state.image}" class="item-count right">
|
||||
{ state.nbTags } tags
|
||||
<i class="material-icons animated"></i>
|
||||
</div>
|
||||
</span>
|
||||
</material-card>
|
||||
<catalog-element if="{ state.images }" filter-results="{ props.filterResults }"
|
||||
<catalog-element
|
||||
if="{ state.images }"
|
||||
filter-results="{ props.filterResults }"
|
||||
registry-url="{ props.registryUrl }"
|
||||
on-notify="{ props.onnNotify }"
|
||||
on-authentication="{ props.onAuthentication }"
|
||||
show-catalog-nb-tags="{ props.showCatalogNbTags }"
|
||||
class="animated {!state.expanded && !props.filterResults ? 'hide' : ''} {state.expanding ? 'expanding' : ''}"
|
||||
each="{item in state.images}" item="{ item }" />
|
||||
each="{item in state.images}"
|
||||
item="{ item }"
|
||||
></catalog-element>
|
||||
</div>
|
||||
<script>
|
||||
import router from '../../scripts/router';
|
||||
import {
|
||||
matchSearch
|
||||
} from '../search-bar.riot';
|
||||
import { Http } from '../../scripts/http';
|
||||
import { matchSearch } from '../search-bar.riot';
|
||||
|
||||
export default {
|
||||
onBeforeMount(props, state) {
|
||||
|
@ -51,10 +63,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
state.repo = props.item.repo;
|
||||
state.nImages = props.item.images.length;
|
||||
}
|
||||
if (props.showCatalogNbTags && state.image) {
|
||||
this.getNbTags(props, state);
|
||||
}
|
||||
},
|
||||
onBeforeUpdate(props, state) {
|
||||
if (props.filterResults && state.images) {
|
||||
state.nImages = state.images.filter(image => matchSearch(props.filterResults, image)).length;
|
||||
state.nImages = state.images.filter((image) => matchSearch(props.filterResults, image)).length;
|
||||
} else {
|
||||
state.nImages = state.images && state.images.length;
|
||||
}
|
||||
|
@ -66,20 +81,38 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
} else {
|
||||
this.update({
|
||||
expanded: !this.state.expanded,
|
||||
expanding: true
|
||||
expanding: true,
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.update({
|
||||
expanding: false
|
||||
expanding: false,
|
||||
});
|
||||
}, 50)
|
||||
}, 50);
|
||||
}
|
||||
},
|
||||
setLaunchListener(cb) {
|
||||
this.triggerLaunch = cb;
|
||||
getNbTags(props, state) {
|
||||
const self = this;
|
||||
const oReq = new Http({
|
||||
onAuthentication: props.onAuthentication,
|
||||
});
|
||||
oReq.addEventListener('load', function () {
|
||||
if (this.status == 200) {
|
||||
const nbTags = (JSON.parse(this.responseText).tags || []).length;
|
||||
self.update({ nbTags });
|
||||
} else if (this.status == 404) {
|
||||
props.onNotify('Server not found', true);
|
||||
} else {
|
||||
props.onNotify(this.responseText, true);
|
||||
}
|
||||
});
|
||||
oReq.addEventListener('error', function () {
|
||||
props.onNotify(this.getErrorMessage(), true);
|
||||
});
|
||||
oReq.open('GET', props.registryUrl + '/v2/' + state.image + '/tags/list');
|
||||
oReq.send();
|
||||
},
|
||||
matchSearch
|
||||
}
|
||||
matchSearch,
|
||||
};
|
||||
</script>
|
||||
<!-- End of tag -->
|
||||
</catalog-element>
|
||||
</catalog-element>
|
||||
|
|
|
@ -26,26 +26,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
<div if="{ !state.loadend }" class="spinner-wrapper">
|
||||
<material-spinner></material-spinner>
|
||||
</div>
|
||||
<catalog-element each="{ item in state.repositories }" item="{ item }" filter-results="{ props.filterResults }" />
|
||||
<catalog-element
|
||||
each="{ item in state.repositories }"
|
||||
item="{ item }"
|
||||
filter-results="{ props.filterResults }"
|
||||
registry-url="{ props.registryUrl }"
|
||||
on-notify="{ props.onNotify }"
|
||||
on-authentication="{ props.onAuthentication }"
|
||||
show-catalog-nb-tags="{ props.showCatalogNbTags }"
|
||||
></catalog-element>
|
||||
<script>
|
||||
import CatalogElement from './catalog-element.riot'
|
||||
import {
|
||||
Http
|
||||
} from '../../scripts/http';
|
||||
import {
|
||||
getRegistryServers
|
||||
} from '../../scripts/utils';
|
||||
import CatalogElement from './catalog-element.riot';
|
||||
import { Http } from '../../scripts/http';
|
||||
import { getRegistryServers } from '../../scripts/utils';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CatalogElement
|
||||
CatalogElement,
|
||||
},
|
||||
state: {
|
||||
registryName: '',
|
||||
length: 0,
|
||||
loadend: false,
|
||||
repositories: [],
|
||||
registryUrl: ''
|
||||
registryUrl: '',
|
||||
},
|
||||
|
||||
onBeforeMount(props) {
|
||||
|
@ -53,7 +57,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
this.state.catalogElementsLimit = props.catalogElementsLimit;
|
||||
},
|
||||
onMounted(props, state) {
|
||||
this.display(props, state)
|
||||
this.display(props, state);
|
||||
},
|
||||
onUpdated(props, state) {
|
||||
this.display(props, state);
|
||||
|
@ -66,7 +70,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
let repositories = [];
|
||||
const self = this;
|
||||
const oReq = new Http({
|
||||
onAuthentication: this.props.onAuthentication
|
||||
onAuthentication: this.props.onAuthentication,
|
||||
});
|
||||
oReq.addEventListener('load', function () {
|
||||
if (this.status == 200) {
|
||||
|
@ -79,7 +83,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
if (acc.length == 0 || acc[acc.length - 1].repo != repoName) {
|
||||
acc.push({
|
||||
repo: repoName,
|
||||
images: []
|
||||
images: [],
|
||||
});
|
||||
}
|
||||
acc[acc.length - 1].images.push(e);
|
||||
|
@ -101,13 +105,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
self.update({
|
||||
repositories,
|
||||
nRepositories: repositories.length,
|
||||
nImages: repositories.reduce((acc, e) => acc + (e.images && e.images.length || 1), 0),
|
||||
loadend: true
|
||||
nImages: repositories.reduce((acc, e) => acc + ((e.images && e.images.length) || 1), 0),
|
||||
loadend: true,
|
||||
});
|
||||
});
|
||||
oReq.open('GET', `${props.registryUrl}/v2/_catalog?n=${state.catalogElementsLimit}`);
|
||||
oReq.send();
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
</catalog>
|
||||
</catalog>
|
||||
|
|
|
@ -29,7 +29,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
<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 }" />
|
||||
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 }"
|
||||
|
|
|
@ -38,12 +38,13 @@
|
|||
<docker-registry-ui registry-url="${REGISTRY_URL}" name="${REGISTRY_TITLE}" pull-url="${PULL_URL}"
|
||||
show-content-digest="${SHOW_CONTENT_DIGEST}" is-image-remove-activated="${DELETE_IMAGES}"
|
||||
catalog-elements-limit="${CATALOG_ELEMENTS_LIMIT}" single-registry="${SINGLE_REGISTRY}"
|
||||
default-registries="${DEFAULT_REGISTRIES}" read-only-registries="${READ_ONLY_REGISTRIES}">
|
||||
default-registries="${DEFAULT_REGISTRIES}" read-only-registries="${READ_ONLY_REGISTRIES}"
|
||||
show-catalog-nb-tags="${SHOW_CATALOG_NB_TAGS}">
|
||||
</docker-registry-ui>
|
||||
<!-- endbuild -->
|
||||
<!-- build:keep developement -->
|
||||
<docker-registry-ui registry-url="" name="Developement Registry" pull-url="" show-content-digest="true"
|
||||
is-image-remove-activated="true" catalog-elements-limit="1000" single-registry="false">
|
||||
is-image-remove-activated="true" catalog-elements-limit="1000" single-registry="false" show-catalog-nb-tags="true">
|
||||
</docker-registry-ui>
|
||||
<!-- endbuild -->
|
||||
<!-- build:js docker-registry-ui.js -->
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue