Skip to content

Commit

Permalink
feat(catalog): show number of tags per image (#239)
Browse files Browse the repository at this point in the history
  • Loading branch information
Joxit authored Mar 21, 2022
1 parent 05cbb51 commit 19e72e4
Show file tree
Hide file tree
Showing 10 changed files with 84 additions and 39 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/).

Expand Down
1 change: 1 addition & 0 deletions arm32v7.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions arm64v8.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions bin/entrypoint
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions debian.dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
69 changes: 51 additions & 18 deletions src/components/catalog/catalog-element.riot
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,41 @@ 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 }
<div if="{state.images}" class="item-count right">
{ 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) {
Expand All @@ -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;
}
Expand All @@ -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>
40 changes: 22 additions & 18 deletions src/components/catalog/catalog.riot
Original file line number Diff line number Diff line change
Expand Up @@ -26,34 +26,38 @@ 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) {
this.state.registryName = props.registryName;
this.state.catalogElementsLimit = props.catalogElementsLimit;
},
onMounted(props, state) {
this.display(props, state)
this.display(props, state);
},
onUpdated(props, state) {
this.display(props, state);
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -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>
3 changes: 2 additions & 1 deletion src/components/docker-registry-ui.riot
Original file line number Diff line number Diff line change
Expand Up @@ -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 }"
Expand Down
5 changes: 3 additions & 2 deletions src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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 -->
Expand Down

0 comments on commit 19e72e4

Please sign in to comment.