Compare commits

...

38 Commits
0.2.1 ... 0.3.4

Author SHA1 Message Date
Joxit
7ebab2f71c Update version to 0.3.4 2018-03-16 23:46:15 +01:00
Jones Magloire
480cde5750 Merge pull request #32 from Joxit/feat/add-title-docker-static
[feat #28] Add Title when using `REGISTRY_URL` …
2018-03-12 00:07:29 +01:00
Joxit
d7af9f1822 Update version to 0.3.3 2018-03-04 00:27:00 +01:00
Jones Magloire
6f5a5ff756 Merge pull request #31 from Joxit/feat/display-image-size
[feat #30] Display image size
2018-03-04 00:11:51 +01:00
Joxit
b98b0c383d [feat #28] Add Title when using REGISTRY_URL (will be REGISTRY_TITLE) 2018-03-02 23:20:11 +01:00
Joxit
23a57e6c34 [feat #30] Display image size 2018-02-27 20:03:16 +01:00
Jones Magloire
e41d6cfb85 Merge pull request #27 from syndbg/patch-2
Fix `REGISTRY_URL` not working
2018-02-18 17:28:04 +01:00
Anton Antonov
c563f7dd4c Fix REGISTRY_URL not working
Tested with nginx version `1.13.8` and  docker image `joxit/docker-registry-ui:static`
2018-02-14 15:47:22 +02:00
Joxit
bf3e3c9fa8 Add REGISTRY_URL option for #25, this will avoid CORS errors 2018-01-25 18:39:47 +01:00
Joxit
325a2417f4 Update version to 0.3.1 Fix for #24 2017-11-08 23:50:43 -04:00
Joxit
adab8b03de Use nginx alpine in static dockerfile 2017-10-22 11:59:44 +02:00
Joxit
89c532edbd Update version to 0.3.0: riot-mui (#17) 2017-10-19 09:56:47 +02:00
Jones Magloire
b9effee691 Merge pull request #23 from Joxit/riot-mui
Move from material-design lite to riot-mui
2017-10-17 00:02:31 +02:00
Joxit
05090d121d [riot-mui] Update gulpfile
Use riot.min.js instead of riot+compiler.min.js
2017-10-15 23:49:37 +02:00
Joxit
c8d98f68d1 [riot-mui] Update readme 2017-10-14 20:53:35 +02:00
Joxit
a8bede51db [riot-mui] Add css for select and code clean up (#17) 2017-10-13 00:27:14 +02:00
Joxit
cce858cbd9 [riot-mui] Remove links to material-design-lite 2017-10-12 23:45:40 +02:00
Joxit
215733c6e4 [riot-mui] Add new riot-mui footer (#17, kysonic/riot-mui#51, kysonic/riot-mui#52) 2017-10-11 00:01:45 +02:00
Joxit
93a8d4a188 [riot-mui] Fix when there is an error, resets the view 2017-09-30 19:08:58 +02:00
Joxit
ab0571eaa1 [riot-mui] Add riot-mui in remove taf (#17)
When we remove the current server, it will update the view.
2017-09-29 21:08:19 +02:00
Joxit
dc929951cf [riot-mui] Clean up and fix in add tag, again 2017-09-28 20:53:59 +02:00
Joxit
91497a06c3 [riot-mui] Add riot-mui in change tag (#17) 2017-09-27 20:52:52 +02:00
Joxit
4d1db1f884 [riot-mui] Fix in add tag dialog 2017-09-26 20:53:32 +02:00
Joxit
a59e759c9c [riot-mui] Minor clean up and fix in catalog and menu (#17) 2017-09-25 00:32:12 +02:00
Joxit
46b80e73f2 [riot-mui] Add riot-mui in add (#17) 2017-09-25 00:32:09 +02:00
Joxit
bdc0d34a87 [riot-mui] Add riot-mui in menu (#17) 2017-09-25 00:32:06 +02:00
Joxit
d4b99b2cb2 [riot-mui] Add riot-mui in remove-image (#17) 2017-09-25 00:32:02 +02:00
Joxit
c1f9d02afb [riot-mui] Add riot-mui in taglist (#17) 2017-09-25 00:31:58 +02:00
Joxit
d404ead587 [riot-mui] Add riot-mui in app and catalog (#17)
- Refactoring index.html/app.tag, now only <app></app> in index.html
- Add riot-mui in catalog.tag
2017-09-25 00:31:40 +02:00
Jones Magloire
5569f1f0bf Merge pull request #22 from Joxit/dependencies
Upgrade dependencies and use riot v3
2017-09-16 23:27:13 +02:00
Joxit
9d48643957 [dependencies] upgrade to riot v3 2017-09-10 20:14:16 +02:00
Joxit
961342d34c Update 0.2.4: query param 2017-09-09 00:07:23 +02:00
Jones Magloire
02c95fee44 Merge pull request #21 from Joxit/query-param
[query-param] Add url query param for sharing
2017-09-08 23:54:51 +02:00
Joxit
ca5fb09fc6 [query-param] Change decode/encodeURI to URIComponent 2017-09-06 23:32:10 +02:00
Joxit
83e2befd5d [dependencies] Upgrade packages dependencies 2017-09-05 23:09:19 +02:00
Joxit
06e4d62543 [query-param] Add url query param for sharing 2017-09-03 00:39:28 +02:00
Joxit
5cf136541f Update 0.2.3: Fix issue #19 again...
Add link to project page
2017-08-06 22:20:58 +02:00
Joxit
d34d793b73 Update 0.2.2: Fix issue #19 2017-08-06 11:39:01 +02:00
30 changed files with 747 additions and 1089 deletions

View File

@@ -12,15 +12,16 @@
#
# 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/>.
FROM nginx
FROM nginx:alpine
MAINTAINER Jones MAGLOIRE @Joxit
WORKDIR /usr/share/nginx/html/
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
COPY dist/ /usr/share/nginx/html/
COPY dist/scripts/script-static.js /usr/share/nginx/html/scripts/script.js
COPY dist/scripts/tags-static.js /usr/share/nginx/html/scripts/tags.js
COPY bin/entrypoint /bin
ENTRYPOINT entrypoint
ENTRYPOINT entrypoint

View File

@@ -5,9 +5,9 @@
This project aims to provide a user interface for your private docker registry v2.
There is no default registry on this UI, you should add your own with the UI.
You can manage more than one registry server.
All registry will be stored in the [local storage](https://en.wikipedia.org/wiki/Web_storage#Local_and_session_storage) of your browser.
All registries will be stored in the [local storage](https://en.wikipedia.org/wiki/Web_storage#Local_and_session_storage) of your browser.
This web user interface use [Riot](https://github.com/Riot/riot) the react-like user interface micro-library and [Material Design Lite](https://github.com/google/material-design-lite) components.
This web user interface uses [Riot](https://github.com/Riot/riot) the react-like user interface micro-library and [riot-mui](https://github.com/kysonic/riot-mui) components.
## [GitHub Page](https://joxit.github.io/docker-registry-ui) and [Live Demo](https://joxit.github.io/docker-registry-ui/demo/)
@@ -18,8 +18,11 @@ This web user interface use [Riot](https://github.com/Riot/riot) the react-like
- List all your repositories/images.
- List all tags for a repository/image
- Sort the tag list
- One interface for many registry
- One interface for many registries
- Use a secured docker registry
- Share your docker registry with query parameter `url` (e.g. `https://joxit.github.io/docker-registry-ui/demo?url=https://registry.example.com`)
- Use `joxit/docker-registry-ui:static` as reverse proxy to your docker registry (This will avoid CORS).
- Display image size (see #30)
## Getting Started
@@ -48,7 +51,7 @@ The docker contains the source code and a node webserver in order to serve the d
You can get the image in three ways
From sources with this command :
From sources with this command:
```sh
git clone https://github.com/Joxit/docker-registry-ui.git
@@ -56,14 +59,14 @@ docker build -t joxit/docker-registry-ui docker-registry-ui
docker build -t joxit/docker-registry-ui -f docker-registry-ui/Dockerfile.static docker-registry-ui
```
Or build with the url :
Or build with the url:
```sh
docker build -t joxit/docker-registry-ui github.com/Joxit/docker-registry-ui
docker build -t joxit/docker-registry-ui -f Dockerfile.static github.com/Joxit/docker-registry-ui
```
Or pull the image from [docker hub](https://hub.docker.com/r/joxit/docker-registry-ui/) :
Or pull the image from [docker hub](https://hub.docker.com/r/joxit/docker-registry-ui/):
```sh
docker pull joxit/docker-registry-ui
@@ -72,7 +75,7 @@ docker pull joxit/docker-registry-ui:static
#### Run the docker
To run the docker and see the website on your 80 port, try this :
To run the docker and see the website on your 80 port, try this:
```sh
docker run -d -p 80:80 joxit/docker-registry-ui
@@ -82,22 +85,35 @@ docker run -d -p 80:80 joxit/docker-registry-ui
Some env options are available for use this interface for only one server.
- `URL`: set the static URL to use. (`Required`)
- `DELETE_IMAGES`: if this variable is empty or `false`, delete feature is desactivated. It is activated otherwise.
- `URL`: set the static URL to use (You will need CORS configuration). Example: `http://127.0.0.1:5000`. (`Required`)
- `REGISTRY_URL`: your docker registry URL to contact (CORS configuration is not needed). Example: `http://my-docker-container:5000`. (Can't be used with `URL`, since 0.3.2).
- `DELETE_IMAGES`: if this variable is empty or `false`, delete feature is deactivated. It is activated otherwise.
Example with `URL` option.
```sh
docker run -d -p 80:80 -e URL=http://127.0.0.1:5000 -e DELETE_IMAGES=true joxit/docker-registry-ui:static
```
Example with `REGISTRY_URL`, this will add a proxy to your registry.
Your registry will be accessible here : `http://127.0.0.1/v2`, this will avoid CORS errors (see #25).
Be careful, `joxit/docker-registry-ui` and `registry:2` will communicate, both containers should be in the same network or use your private IP.
```sh
docker network create registry-ui-net
docker run -d --net registry-ui-net --name registry-srv registry:2
docker run -d --net registry-ui-net -p 80:80 -e REGISTRY_URL=http://registry-srv:5000 -e DELETE_IMAGES=true joxit/docker-registry-ui:static
```
## Using CORS
Your server should be configured to accept CORS.
If your docker registry does not need credentials, you will need to send this HEADER :
If your docker registry does not need credentials, you will need to send this HEADER:
Access-Control-Allow-Origin: '*'
If your docker registry need credentials, you will need to send these HEADERS :
If your docker registry need credentials, you will need to send these HEADERS:
```yml
http:
@@ -109,7 +125,7 @@ http:
## Using delete
For deleting images, you need to activate the delete feature in your registry :
For deleting images, you need to activate the delete feature in your registry:
```yml
storage:
@@ -117,7 +133,7 @@ storage:
enabled: true
```
And you need to add these HEADERS :
And you need to add these HEADERS:
```yml
http:

View File

@@ -1,13 +1,19 @@
#!/bin/bash
#!/bin/sh
$@
sed -i "s,\${URL},${URL}," scripts/script.js
sed -i "s,\${REGISTRY_TITLE},${REGISTRY_TITLE}," scripts/script.js
if [ -z "${DELETE_IMAGES}" ] || [ "${DELETE_IMAGES}" = false ] ; then
sed -i "s/registryUI.isImageRemoveActivated *= *[^,;]*/registryUI.isImageRemoveActivated=false/" scripts/script.js
fi
if [ -n "${REGISTRY_URL}" ] ; then
sed -i "s,\${REGISTRY_URL},${REGISTRY_URL}," /etc/nginx/conf.d/default.conf
sed -i "s,#!,," /etc/nginx/conf.d/default.conf
fi
if [ -z "$@" ]; then
nginx -g "daemon off;"
else
$@
fi
fi

View File

@@ -26,32 +26,7 @@
</head>
<body>
<!-- Always shows a header, even in smaller screens. -->
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
<header class="mdl-layout__header">
<div class="mdl-layout__header-row">
<!-- Title --><span class="mdl-layout-title">Docker Registry UI</span>
<menu></menu>
</div>
</header>
<main class="mdl-layout__content">
<div class="page-content">
<app></app>
</div>
</main>
<change></change>
<add></add>
<remove></remove>
<footer class="mdl-mini-footer">
<div class="mdl-mini-footer__left-section">
<div class="mdl-logo">Docker Registry UI</div>
<ul class="mdl-mini-footer__link-list">
<li><a href="https://github.com/Joxit/docker-registry-ui">Contribute on GitHub</a></li>
<li><a href="https://github.com/Joxit/docker-registry-ui/blob/master/LICENSE">Privacy &amp; Terms</a></li>
</ul>
</div>
</footer>
</div>
<app></app>
<script src="../dist/scripts/vendor.js"></script>
<script src="../dist/scripts/tags.js"></script>
<script src="../dist/scripts/script.js"></script>

2
dist/index.html vendored
View File

@@ -13,4 +13,4 @@
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/>.
--><!DOCTYPE html><html><head><meta charset="UTF-8"><link rel="stylesheet" href="vendor.css"><link rel="stylesheet" href="style.css"><link href="https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&amp;lang=en" rel="stylesheet" type="text/css"><title>Docker Registry UI</title></head><body><!-- Always shows a header, even in smaller screens. --><div class="mdl-layout mdl-js-layout mdl-layout--fixed-header"><header class="mdl-layout__header"><div class="mdl-layout__header-row"><!-- Title --> <span class="mdl-layout-title">Docker Registry UI</span><menu></menu></div></header><main class="mdl-layout__content"><div class="page-content"><app></app></div></main><change></change><add></add><remove></remove><footer class="mdl-mini-footer"><div class="mdl-mini-footer__left-section"><div class="mdl-logo">Docker Registry UI</div><ul class="mdl-mini-footer__link-list"><li><a href="https://github.com/Joxit/docker-registry-ui">Contribute on GitHub</a></li><li><a href="https://github.com/Joxit/docker-registry-ui/blob/master/LICENSE">Privacy &amp; Terms</a></li></ul></div></footer></div><script src="scripts/vendor.js"></script><script src="scripts/tags.js"></script><script src="scripts/script.js"></script></body></html>
--><!DOCTYPE html><html><head><meta charset="UTF-8"><link rel="stylesheet" href="vendor.css"><link rel="stylesheet" href="style.css"><link href="https://fonts.googleapis.com/css?family=Roboto:regular,bold,italic,thin,light,bolditalic,black,medium&amp;lang=en" rel="stylesheet" type="text/css"><title>Docker Registry UI</title></head><body><app></app><script src="scripts/vendor.js"></script><script src="scripts/tags.js"></script><script src="scripts/script.js"></script></body></html>

View File

@@ -1,6 +1,6 @@
/*!
* docker-registry-ui
* Copyright (C) 2016 Jones Magloire @Joxit
* Copyright (C) 2016-2018 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
@@ -15,4 +15,4 @@
* 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/>.
*/
function Http(){this.oReq=new XMLHttpRequest,this._events={},this._headers={}}Http.prototype.addEventListener=function(t,e){this._events[t]=e;var s=this;switch(t){case"loadend":s.oReq.addEventListener("loadend",function(){if(401==this.status){var t=new XMLHttpRequest;for(key in s._events)t.addEventListener(key,s._events[key]);for(key in s._headers)t.setRequestHeader(key,s._headers[key]);t.withCredentials=!0,t.open(s._method,s._url),t.send()}else e.bind(this)()});break;case"load":s.oReq.addEventListener("load",function(){401!==this.status&&e.bind(this)()});break;default:s.oReq.addEventListener(t,function(){e.bind(this)()})}},Http.prototype.setRequestHeader=function(t,e){this.oReq.setRequestHeader(t,e),this._headers[t]=e},Http.prototype.open=function(t,e){this._method=t,this._url=e,this.oReq.open(t,e)},Http.prototype.send=function(){this.oReq.send()};var registryUI={};registryUI.url=function(){return"${URL}"},registryUI.isImageRemoveActivated=!0,registryUI.catalog={},registryUI.taglist={},riot.mount("catalog"),riot.mount("taglist"),riot.mount("app");
function Http(){this.oReq=new XMLHttpRequest,this.oReq.hasHeader=Http.hasHeader,this._events={},this._headers={}}Http.prototype.addEventListener=function(e,t){this._events[e]=t;var s=this;switch(e){case"loadend":s.oReq.addEventListener("loadend",function(){if(401==this.status){var e=new XMLHttpRequest;e.open(s._method,s._url);for(key in s._events)e.addEventListener(key,s._events[key]);for(key in s._headers)e.setRequestHeader(key,s._headers[key]);e.withCredentials=!0,e.hasHeader=Http.hasHeader,e.send()}else t.bind(this)()});break;case"load":s.oReq.addEventListener("load",function(){401!==this.status&&t.bind(this)()});break;default:s.oReq.addEventListener(e,function(){t.bind(this)()})}},Http.prototype.setRequestHeader=function(e,t){this.oReq.setRequestHeader(e,t),this._headers[e]=t},Http.prototype.open=function(e,t){this._method=e,this._url=t,this.oReq.open(e,t)},Http.prototype.send=function(){this.oReq.send()},Http.hasHeader=function(e){return this.getAllResponseHeaders().split("\n").some(function(t){return new RegExp("^"+e+":","i").test(t)})};var registryUI={};registryUI.url=function(){return"${URL}"},registryUI.name=function(){return"${REGISTRY_TITLE}"},registryUI.isImageRemoveActivated=!0,registryUI.catalog={},registryUI.taglist={},riot.mount("catalog"),riot.mount("taglist"),riot.mount("app");

View File

@@ -1,6 +1,6 @@
/*!
* docker-registry-ui
* Copyright (C) 2016 Jones Magloire @Joxit
* Copyright (C) 2016-2018 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
@@ -15,4 +15,4 @@
* 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/>.
*/
function Http(){this.oReq=new XMLHttpRequest,this._events={},this._headers={}}Http.prototype.addEventListener=function(e,t){this._events[e]=t;var r=this;switch(e){case"loadend":r.oReq.addEventListener("loadend",function(){if(401==this.status){var e=new XMLHttpRequest;for(key in r._events)e.addEventListener(key,r._events[key]);for(key in r._headers)e.setRequestHeader(key,r._headers[key]);e.withCredentials=!0,e.open(r._method,r._url),e.send()}else t.bind(this)()});break;case"load":r.oReq.addEventListener("load",function(){401!==this.status&&t.bind(this)()});break;default:r.oReq.addEventListener(e,function(){t.bind(this)()})}},Http.prototype.setRequestHeader=function(e,t){this.oReq.setRequestHeader(e,t),this._headers[e]=t},Http.prototype.open=function(e,t){this._method=e,this._url=t,this.oReq.open(e,t)},Http.prototype.send=function(){this.oReq.send()};var registryUI={};registryUI.url=function(){return registryUI.getRegistryServer(0)},registryUI.getRegistryServer=function(e){try{var t=JSON.parse(localStorage.getItem("registryServer"));if(t instanceof Array)return isNaN(e)?t.map(function(e){return e.trim().replace(/\/*$/,"")}):t[e]}catch(e){}return isNaN(e)?[]:""},registryUI.addServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r==-1&&(t.push(e),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.changeServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r!=-1&&(t.splice(r,1),t=[e].concat(t),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.removeServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r!=-1&&(t.splice(r,1),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.isImageRemoveActivated=!0,registryUI.catalog={},registryUI.taglist={},riot.mount("add"),riot.mount("change"),riot.mount("remove"),riot.mount("menu"),riot.mount("app");
function Http(){this.oReq=new XMLHttpRequest,this.oReq.hasHeader=Http.hasHeader,this._events={},this._headers={}}Http.prototype.addEventListener=function(e,t){this._events[e]=t;var r=this;switch(e){case"loadend":r.oReq.addEventListener("loadend",function(){if(401==this.status){var e=new XMLHttpRequest;e.open(r._method,r._url);for(key in r._events)e.addEventListener(key,r._events[key]);for(key in r._headers)e.setRequestHeader(key,r._headers[key]);e.withCredentials=!0,e.hasHeader=Http.hasHeader,e.send()}else t.bind(this)()});break;case"load":r.oReq.addEventListener("load",function(){401!==this.status&&t.bind(this)()});break;default:r.oReq.addEventListener(e,function(){t.bind(this)()})}},Http.prototype.setRequestHeader=function(e,t){this.oReq.setRequestHeader(e,t),this._headers[e]=t},Http.prototype.open=function(e,t){this._method=e,this._url=t,this.oReq.open(e,t)},Http.prototype.send=function(){this.oReq.send()},Http.hasHeader=function(e){return this.getAllResponseHeaders().split("\n").some(function(t){return new RegExp("^"+e+":","i").test(t)})};var registryUI={};registryUI.URL_QUERY_PARAM_REGEX=/[&?]url=/,registryUI.URL_PARAM_REGEX=/^url=/,registryUI.name=registryUI.url=function(e){if(!registryUI._url){var t=registryUI.getUrlQueryParam();if(t)try{return registryUI._url=registryUI.decodeURI(t),registryUI._url}catch(e){console.log(e)}registryUI._url=registryUI.getRegistryServer(0)}return registryUI._url},registryUI.getRegistryServer=function(e){try{var t=JSON.parse(localStorage.getItem("registryServer"));if(t instanceof Array)return isNaN(e)?t.map(function(e){return e.trim().replace(/\/*$/,"")}):t[e]}catch(e){}return isNaN(e)?[]:""},registryUI.addServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r==-1&&(t.push(e),registryUI._url||registryUI.updateHistory(e),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.changeServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r!=-1&&(t.splice(r,1),t=[e].concat(t),registryUI.updateHistory(e),localStorage.setItem("registryServer",JSON.stringify(t)))},registryUI.removeServer=function(e){var t=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var r=t.indexOf(e);r!=-1&&(t.splice(r,1),localStorage.setItem("registryServer",JSON.stringify(t)),e==registryUI.url()&&(registryUI.updateHistory(registryUI.getRegistryServer(0)),route("")))},registryUI.updateHistory=function(e){history.pushState(null,"",(e?"?url="+registryUI.encodeURI(e):"?")+window.location.hash),registryUI._url=e},registryUI.getUrlQueryParam=function(){var e=window.location.search;if(registryUI.URL_QUERY_PARAM_REGEX.test(e)){var t=e.split(/^\?|&/).find(function(e){return e&&registryUI.URL_PARAM_REGEX.test(e)});return t?t.replace(registryUI.URL_PARAM_REGEX,""):t}},registryUI.encodeURI=function(e){return e.indexOf("&")<0?window.encodeURIComponent(e):btoa(e)},registryUI.decodeURI=function(e){return e.startsWith("http")?window.decodeURIComponent(e):atob(e)},registryUI.isImageRemoveActivated=!0,registryUI.catalog={},registryUI.taglist={},riot.mount("*");

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

671
dist/scripts/vendor.js vendored

File diff suppressed because one or more lines are too long

4
dist/style.css vendored
View File

@@ -1,6 +1,6 @@
/*!
* docker-registry-ui
* Copyright (C) 2016 Jones Magloire @Joxit
* Copyright (C) 2016-2018 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
@@ -15,4 +15,4 @@
* 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/>.
*/
@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(MaterialIcons-Regular.eot);src:local('fonts/Material Icons'),local('fonts/MaterialIcons-Regular'),url(fonts/MaterialIcons-Regular.woff2) format('woff2'),url(fonts/MaterialIcons-Regular.woff) format('woff'),url(fonts/MaterialIcons-Regular.ttf) format('truetype')}.material-icons{font-family:'Material Icons';font-weight:400;font-style:normal;font-size:24px;display:inline-block;width:1em;height:1em;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:'liga'}html>body{font-family:Roboto,Helvetica,Arial,sans-serif!important}.mdl-mini-footer{padding-top:8px;padding-bottom:8px}.catalog,.taglist{padding:16px}.section-centerd{margin:auto}.mdl-data-table th{font-size:18px}.mdl-data-table td{font-size:16px}.full-table{width:100%;border:none}.url{font-size:14px;word-break:break-all}
@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:url(MaterialIcons-Regular.eot);src:local('fonts/Material Icons'),local('fonts/MaterialIcons-Regular'),url(fonts/MaterialIcons-Regular.woff2) format('woff2'),url(fonts/MaterialIcons-Regular.woff) format('woff'),url(fonts/MaterialIcons-Regular.ttf) format('truetype')}.material-icons{font-family:'Material Icons';font-weight:400;font-style:normal;font-size:24px;display:inline-block;width:1em;height:1em;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:'liga'}html>body{font-family:Roboto,Helvetica,Arial,sans-serif!important}body,html{margin:0;height:100%}main{margin-bottom:100px}.section-centerd{margin:auto}.full-table{width:100%;border:none}.url{font-size:14px;word-break:break-all}.material-card-title-action a{color:inherit;text-decoration:none;font-weight:inherit}material-card{min-height:200px;max-width:75%;margin:auto;margin-top:20px;margin-bottom:20px}material-spinner{align-self:center}.spinner-wrapper{margin-top:50px;display:flex;flex-direction:column}material-navbar{height:64px}.logo{padding:0 16px 0 72px;text-decoration:none;font-size:20px;line-height:1;letter-spacing:.02em;font-weight:400}h2{padding:16px;margin:auto;font-size:24px;font-weight:300;line-height:normal;overflow:hidden}.list{display:block;padding:8px 0;list-style:none}.list.highlight>li:hover{background-color:#eee;cursor:pointer}.list>li{box-sizing:border-box;line-height:1;height:48px;padding:0 16px;overflow:hidden}.list>li i.material-icons{margin-right:32px;height:24px;width:24px;font-size:24px;box-sizing:border-box;color:#757575}.list>li>span{height:100%;text-decoration:none;box-sizing:border-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-align-items:center;-ms-flex-align:center;align-items:center}.material-card-title-action{-webkit-align-items:center;-ms-flex-align:center;align-items:center;display:block;display:-webkit-flex;display:-ms-flexbox;display:flex;padding:16px}.material-card-title-action h2{margin:0}material-card table{width:100%;border:none;position:relative;border:1px solid rgba(0,0,0,.12);border-collapse:collapse;white-space:nowrap;font-size:13px;background-color:#fff;border:none}material-card table th{font-size:18px;vertical-align:bottom;line-height:24px;height:48px;color:rgba(0,0,0,.54);box-sizing:border-box;padding:0 18px 12px 18px;text-align:right}.material-card-th-left{text-align:left}material-card table tbody tr:hover{background-color:#eee}material-card table tbody tr{position:relative;height:48px;transition-duration:.28s;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-property:background-color}material-card table td{font-size:16px;position:relative;height:48px;border-top:1px solid rgba(0,0,0,.12);border-bottom:1px solid rgba(0,0,0,.12);padding:12px 18px;box-sizing:border-box;vertical-align:middle;text-align:right}material-card table th.material-card-th-sorted-ascending:hover,material-card table th.material-card-th-sorted-descending:hover{cursor:pointer}material-card table th.material-card-th-sorted-ascending:hover:before,material-card table th.material-card-th-sorted-descending:hover:before{color:rgba(0,0,0,.26)}material-card table th.material-card-th-sorted-ascending:before,material-card table th.material-card-th-sorted-descending:before{font-family:'Material Icons';font-weight:400;font-style:normal;line-height:1;font-size:16px;content:"\e5d8";margin-right:5px;vertical-align:sub}material-card table th.material-card-th-sorted-descending:before{content:"\e5db"}.material-icons{color:#777}material-snackbar .toast{height:auto}menu{position:absolute;top:0;right:16px;color:#000}menu .overlay{position:fixed;height:100%;width:100%;top:0;right:0;z-index:1}#menu-control-button{background:rgba(255,255,255,0);float:right}#menu-control-button i{color:#fff;font-size:24px}#menu-control-dropdown{display:inline-block;position:relative}.dropdown{min-width:124px;padding:8px 0;margin:0}#menu-control-dropdown p,dropdown-item{padding:0 16px;margin:auto;line-height:48px;height:48px;cursor:pointer}#menu-control-dropdown p:hover{background-color:#eee}#menu-control-dropdown p:active,.material-button-active:active{background-color:#e0e0e0}material-popup material-button{background-color:#fff;color:#000}material-popup material-button:hover material-waves{background-color:hsla(0,0%,75%,.2)}material-popup .popup{max-width:450px}footer{width:100%;position:fixed;bottom:0}.select-padding{padding:20px 0}select{position:relative;outline:0;box-shadow:none;padding:0;width:100%;background:0 0;border:none;font-weight:400;line-height:24px;height:24px;border-bottom:1px solid #2f6975;appearance:none;-moz-appearance:none;-webkit-appearance:none}

47
dist/vendor.css vendored

File diff suppressed because one or more lines are too long

View File

@@ -14,7 +14,7 @@ var uglify = require('uglify-js-harmony');
var useref = require('gulp-useref');
gulp.task('html', function() {
var htmlFilter = filter('**/*.html');
var htmlFilter = filter('**/*.html', {restore: true});
return gulp.src(['src/index.html'])
.pipe(useref())
.pipe(gIf(['*.js', '!*.min.js'], minifier({}, uglify))) // FIXME
@@ -26,12 +26,12 @@ gulp.task('html', function() {
removeEmptyAttributes: true,
minifyJS: uglify
}))
.pipe(htmlFilter.restore())
.pipe(htmlFilter.restore)
.pipe(gulp.dest('dist'));
});
gulp.task('clean', function(done) {
del(['dist'], done);
return del(['dist']);
});
gulp.task('riot-tag', ['html'], function() {
@@ -42,21 +42,21 @@ gulp.task('riot-tag', ['html'], function() {
.pipe(license('agpl3', {
tiny: false,
project: 'docker-registry-ui',
year: '2016',
year: '2016-2018',
organization: 'Jones Magloire @Joxit'
}))
.pipe(gulp.dest('dist/scripts'));
});
gulp.task('riot-static-tag', ['html'], function() {
return gulp.src(['src/tags/catalog.tag', 'src/tags/app.tag', 'src/tags/taglist.tag', 'src/tags/remove-image.tag'])
return gulp.src(['src/tags/catalog.tag', 'src/tags/app.tag', 'src/tags/taglist.tag', 'src/tags/remove-image.tag', 'src/tags/image-size.tag'])
.pipe(concat('tags-static.js'))
.pipe(riot())
.pipe(minifier({}, uglify))
.pipe(license('agpl3', {
tiny: false,
project: 'docker-registry-ui',
year: '2016',
year: '2016-2018',
organization: 'Jones Magloire @Joxit'
}))
.pipe(gulp.dest('dist/scripts'));
@@ -69,7 +69,7 @@ gulp.task('scripts-static', ['html'], function() {
.pipe(license('agpl3', {
tiny: false,
project: 'docker-registry-ui',
year: '2016',
year: '2016-2018',
organization: 'Jones Magloire @Joxit'
}))
.pipe(gulp.dest('dist/scripts'));
@@ -82,12 +82,18 @@ gulp.task('scripts', ['html'], function() {
.pipe(license('agpl3', {
tiny: false,
project: 'docker-registry-ui',
year: '2016',
year: '2016-2018',
organization: 'Jones Magloire @Joxit'
}))
.pipe(gulp.dest('dist/scripts'));
});
gulp.task('vendor', ['html'], function() {
return gulp.src(['node_modules/riot/riot.min.js', 'node_modules/riot-route/dist/route.min.js', 'node_modules/riot-mui/build/js/riot-mui-min.js'])
.pipe(concat('vendor.js'))
.pipe(gulp.dest('dist/scripts'));
});
gulp.task('styles', ['html'], function() {
return gulp.src(['src/*.css'])
.pipe(concat('style.css'))
@@ -97,7 +103,7 @@ gulp.task('styles', ['html'], function() {
.pipe(license('agpl3', {
tiny: false,
project: 'docker-registry-ui',
year: '2016',
year: '2016-2018',
organization: 'Jones Magloire @Joxit'
}))
.pipe(gulp.dest('dist/'));
@@ -109,7 +115,7 @@ gulp.task('fonts', function() {
.pipe(gulp.dest('dist/fonts'));
});
gulp.task('sources', ['riot-tag', 'riot-static-tag', 'scripts', 'scripts-static', 'styles'], function() {
gulp.task('sources', ['riot-tag', 'riot-static-tag', 'scripts', 'vendor', 'scripts-static', 'styles'], function() {
gulp.start();
});

33
nginx/default.conf Normal file
View File

@@ -0,0 +1,33 @@
server {
listen 80;
server_name localhost;
#! resolver 127.0.0.11; # This is for docker container name resolver
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
#! location /v2 {
#! proxy_pass ${REGISTRY_URL};
#! }
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

View File

@@ -1,6 +1,6 @@
{
"name": "docker-registry-ui",
"version": "0.2.1",
"version": "0.3.4",
"scripts": {
"build": "./node_modules/gulp/bin/gulp.js build"
},
@@ -9,26 +9,25 @@
"url": "https://github.com/Joxit/docker-registry-ui.git"
},
"author": "Jones Magloire (Joxit)",
"license": "AGPLv3",
"license": "AGPL-3.0",
"description": "A web UI for private docker registry",
"dependencies": {
},
"dependencies": {},
"devDependencies": {
"del": "^0.1.3",
"dialog-polyfill": "^0.4",
"del": "^3.0.0",
"gulp": "^3.9",
"gulp-clean-css": "^3.9.3",
"gulp-concat": "^2.6.0",
"gulp-filter": "^1.0.0",
"gulp-htmlmin": "^2.0.0",
"gulp-filter": "^5.1.0",
"gulp-htmlmin": "^3.0.0",
"gulp-if": "^2.0.0",
"gulp-license": "^1.1.0",
"gulp-clean-css": "^2.0.11",
"gulp-riot": "^0.5.2",
"gulp-uglify": "^1.0.0",
"gulp-useref": "^3.0.0",
"material-design-lite": "^1.1",
"riot": "^2.3",
"riotgear-router": "^1.3.1",
"uglify-js-harmony": "^2.6.2"
"gulp-riot": "^1.1.4",
"gulp-uglify": "^2.1.2",
"gulp-useref": "^3.1.5",
"riot": "^3.9.0",
"riot-mui": "^0.1.1",
"riot-route": "^3.1.3",
"uglify-js": "^3.3.16",
"uglify-js-harmony": "^2.7.7"
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -20,8 +20,7 @@
<head>
<meta charset="UTF-8">
<!-- build:css vendor.css -->
<LINK href="../node_modules/dialog-polyfill/dialog-polyfill.css" rel="stylesheet" type="text/css">
<LINK href="../node_modules/material-design-lite/dist/material.min.css" rel="stylesheet" type="text/css">
<LINK href="../node_modules/riot-mui/build/styles/riot-mui.min.css" rel="stylesheet" type="text/css">
<!-- endbuild -->
<!-- build:css style.css -->
<LINK href="style.css" rel="stylesheet" type="text/css">
@@ -32,38 +31,11 @@
</head>
<body>
<!-- Always shows a header, even in smaller screens. -->
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
<header class="mdl-layout__header">
<div class="mdl-layout__header-row">
<!-- Title -->
<span class="mdl-layout-title">Docker Registry UI</span>
<menu></menu>
</div>
</header>
<main class="mdl-layout__content">
<div class="page-content">
<app></app>
</div>
</main>
<change></change>
<add></add>
<remove></remove>
<footer class="mdl-mini-footer">
<div class="mdl-mini-footer__left-section">
<div class="mdl-logo">Docker Registry UI</div>
<ul class="mdl-mini-footer__link-list">
<li><a href="https://github.com/Joxit/docker-registry-ui">Contribute on GitHub</a></li>
<li><a href="https://github.com/Joxit/docker-registry-ui/blob/master/LICENSE">Privacy &amp; Terms</a></li>
</ul>
</div>
</footer>
</div>
<app></app>
<!-- build:js scripts/vendor.js -->
<script src="../node_modules/riot/riot+compiler.min.js"></script>
<script src="../node_modules/riotgear-router/dist/rg-router.min.js"></script>
<script src="../node_modules/dialog-polyfill/dialog-polyfill.js"></script>
<script src="../node_modules/material-design-lite/dist/material.min.js"></script>
<script src="../node_modules/riot-route/dist/route.js"></script>
<script src="../node_modules/riot-mui/build/js/riot-mui.js"></script>
<!-- endbuild -->
<!-- build:js scripts/tags.js -->
<script src="tags/catalog.tag" type="riot/tag"></script>
@@ -73,6 +45,7 @@
<script src="tags/change.tag" type="riot/tag"></script>
<script src="tags/remove.tag" type="riot/tag"></script>
<script src="tags/menu.tag" type="riot/tag"></script>
<script src="tags/image-size.tag" type="riot/tag"></script>
<script src="tags/app.tag" type="riot/tag"></script>
<!-- endbuild -->
<!-- build:js scripts/script.js -->

View File

@@ -16,6 +16,7 @@
*/
function Http() {
this.oReq = new XMLHttpRequest();
this.oReq.hasHeader = Http.hasHeader;
this._events = {};
this._headers = {};
}
@@ -29,6 +30,7 @@ Http.prototype.addEventListener = function(e, f) {
self.oReq.addEventListener('loadend', function() {
if (this.status == 401) {
var req = new XMLHttpRequest();
req.open(self._method, self._url);
for (key in self._events) {
req.addEventListener(key, self._events[key]);
}
@@ -36,7 +38,7 @@ Http.prototype.addEventListener = function(e, f) {
req.setRequestHeader(key, self._headers[key]);
}
req.withCredentials = true;
req.open(self._method, self._url);
req.hasHeader = Http.hasHeader;
req.send();
} else {
f.bind(this)();
@@ -77,3 +79,9 @@ Http.prototype.open = function(m, u) {
Http.prototype.send = function() {
this.oReq.send();
};
Http.hasHeader = function(header) {
return this.getAllResponseHeaders().split('\n').some(function(h) {
return new RegExp('^' + header + ':', 'i').test(h);
});
};

View File

@@ -15,8 +15,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
var registryUI = {}
registryUI.url = function() {
return registryUI.getRegistryServer(0);
registryUI.URL_QUERY_PARAM_REGEX = /[&?]url=/;
registryUI.URL_PARAM_REGEX = /^url=/;
registryUI.name = registryUI.url = function(byPassQueryParam) {
if (!registryUI._url) {
var url = registryUI.getUrlQueryParam();
if (url) {
try {
registryUI._url = registryUI.decodeURI(url);
return registryUI._url;
} catch (e) {
console.log(e);
}
}
registryUI._url = registryUI.getRegistryServer(0);
}
return registryUI._url;
}
registryUI.getRegistryServer = function(i) {
try {
@@ -37,6 +52,9 @@ registryUI.addServer = function(url) {
return;
}
registryServer.push(url);
if (!registryUI._url) {
registryUI.updateHistory(url);
}
localStorage.setItem('registryServer', JSON.stringify(registryServer));
}
registryUI.changeServer = function(url) {
@@ -48,6 +66,7 @@ registryUI.changeServer = function(url) {
}
registryServer.splice(index, 1);
registryServer = [url].concat(registryServer);
registryUI.updateHistory(url);
localStorage.setItem('registryServer', JSON.stringify(registryServer));
}
registryUI.removeServer = function(url) {
@@ -59,13 +78,37 @@ registryUI.removeServer = function(url) {
}
registryServer.splice(index, 1);
localStorage.setItem('registryServer', JSON.stringify(registryServer));
if (url == registryUI.url()) {
registryUI.updateHistory(registryUI.getRegistryServer(0));
route('');
}
}
registryUI.updateHistory = function(url) {
history.pushState(null, '', (url ? '?url=' + registryUI.encodeURI(url) : '?') + window.location.hash);
registryUI._url = url;
}
registryUI.getUrlQueryParam = function () {
var search = window.location.search;
if (registryUI.URL_QUERY_PARAM_REGEX.test(search)) {
var param = search.split(/^\?|&/).find(function(param) {
return param && registryUI.URL_PARAM_REGEX.test(param);
});
return param ? param.replace(registryUI.URL_PARAM_REGEX, '') : param;
}
};
registryUI.encodeURI = function(url) {
return url.indexOf('&') < 0 ? window.encodeURIComponent(url) : btoa(url);
};
registryUI.decodeURI = function(url) {
return url.startsWith('http') ? window.decodeURIComponent(url) : atob(url);
};
registryUI.isImageRemoveActivated = true;
registryUI.catalog = {};
registryUI.taglist = {};
riot.mount('add');
riot.mount('change');
riot.mount('remove');
riot.mount('menu');
riot.mount('app');
riot.mount('*');

View File

@@ -18,6 +18,9 @@ var registryUI = {}
registryUI.url = function() {
return '${URL}';
};
registryUI.name = function() {
return '${REGISTRY_TITLE}' || registryUI.url();
};
registryUI.isImageRemoveActivated = true;
registryUI.catalog = {};
registryUI.taglist = {};

View File

@@ -18,28 +18,19 @@
html > body {
font-family: 'Roboto', 'Helvetica', 'Arial', sans-serif !important;
}
.mdl-mini-footer {
padding-top: 8px;
padding-bottom: 8px;
html, body {
margin: 0;
height: 100%;
}
.catalog, .taglist {
padding: 16px;
main {
margin-bottom: 100px;
}
.section-centerd {
margin: auto;
}
.mdl-data-table th {
font-size: 18px;
}
.mdl-data-table td {
font-size: 16px;
}
.full-table {
width: 100%;
border: none;
@@ -48,4 +39,272 @@ html > body {
.url {
font-size: 14px;
word-break: break-all;
}
.material-card-title-action a {
color: inherit;
text-decoration: none;
font-weight: inherit;
}
material-card {
min-height: 200px;
max-width: 75%;
margin: auto;
margin-top: 20px;
margin-bottom: 20px;
}
material-spinner {
align-self: center;
}
.spinner-wrapper {
margin-top: 50px;
display: flex;
flex-direction: column;
}
material-navbar {
height: 64px;
}
.logo {
padding: 0 16px 0 72px;
text-decoration: none;
font-size: 20px;
line-height: 1;
letter-spacing: .02em;
font-weight: 400;
}
h2 {
padding: 16px;
margin: auto;
font-size: 24px;
font-weight: 300;
line-height: normal;
overflow: hidden;
}
.list {
display: block;
padding: 8px 0;
list-style: none;
}
.list.highlight>li:hover {
background-color: #eee;
cursor: pointer;
}
.list>li {
box-sizing: border-box;
line-height: 1;
height: 48px;
padding: 0 16px;
overflow: hidden;
}
.list>li i.material-icons {
margin-right: 32px;
height: 24px;
width: 24px;
font-size: 24px;
box-sizing: border-box;
color: #757575;
}
.list>li>span {
height: 100%;
text-decoration: none;
box-sizing: border-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
}
.material-card-title-action {
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
display: block;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
padding: 16px;
}
.material-card-title-action h2 {
margin: 0;
}
material-card table {
width: 100%;
border: none;
position: relative;
border: 1px solid rgba(0,0,0,.12);
border-collapse: collapse;
white-space: nowrap;
font-size: 13px;
background-color: #fff;
border: none;
}
material-card table th {
font-size: 18px;
vertical-align: bottom;
line-height: 24px;
height: 48px;
color: rgba(0,0,0,.54);
box-sizing: border-box;
padding: 0 18px 12px 18px;
text-align: right;
}
.material-card-th-left {
text-align: left;
}
material-card table tbody tr:hover {
background-color: #eee;
}
material-card table tbody tr {
position: relative;
height: 48px;
transition-duration: .28s;
transition-timing-function: cubic-bezier(.4,0,.2,1);
transition-property: background-color;
}
material-card table td {
font-size: 16px;
position: relative;
height: 48px;
border-top: 1px solid rgba(0,0,0,.12);
border-bottom: 1px solid rgba(0,0,0,.12);
padding: 12px 18px;
box-sizing: border-box;
vertical-align: middle;
text-align: right;
}
material-card table th.material-card-th-sorted-ascending:hover,
material-card table th.material-card-th-sorted-descending:hover {
cursor: pointer;
}
material-card table th.material-card-th-sorted-ascending:hover:before,
material-card table th.material-card-th-sorted-descending:hover:before {
color: rgba(0,0,0,.26);
}
material-card table th.material-card-th-sorted-ascending:before,
material-card table th.material-card-th-sorted-descending:before {
font-family: 'Material Icons';
font-weight: 400;
font-style: normal;
line-height: 1;
font-size: 16px;
content: "\e5d8";
margin-right: 5px;
vertical-align: sub;
}
material-card table th.material-card-th-sorted-descending:before {
content: "\e5db";
}
.material-icons {
color: #777;
}
material-snackbar .toast {
height: auto;
}
menu {
position: absolute;
top: 0px;
right: 16px;
color: #000;
}
menu .overlay {
position: fixed;
height: 100%;
width: 100%;
top: 0;
right: 0;
z-index: 1;
}
#menu-control-button {
background: rgba(255,255,255,0);
float: right;
}
#menu-control-button i {
color: #fff;
font-size: 24px;
}
#menu-control-dropdown {
display: inline-block;
position: relative;
}
.dropdown {
min-width: 124px;
padding: 8px 0;
margin: 0;
}
dropdown-item, #menu-control-dropdown p {
padding: 0 16px;
margin: auto;
line-height: 48px;
height: 48px;
cursor: pointer;
}
#menu-control-dropdown p:hover {
background-color: #eee;
}
#menu-control-dropdown p:active, .material-button-active:active {
background-color: #e0e0e0;
}
material-popup material-button {
background-color: #fff;
color: #000;
}
material-popup material-button:hover material-waves {
background-color: hsla(0,0%,75%,.2);
}
material-popup .popup {
max-width: 450px;
}
footer {
width: 100%;
position: fixed;
bottom: 0;
}
.select-padding {
padding: 20px 0;
}
select {
position: relative;
outline: 0;
box-shadow: none;
padding: 0;
width: 100%;
background: 0 0;
border: none;
font-weight: 400;
line-height: 24px;
height: 24px;
border-bottom: 1px solid #2f6975;
appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
}

View File

@@ -15,51 +15,45 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<add>
<dialog id="add-server-dialog" class="mdl-dialog">
<h4 class="mdl-dialog__title">Add your Server ?</h4>
<div class="mdl-dialog__content">
<div class="mdl-textfield mdl-js-textfield">
<input class="mdl-textfield__input" type="text" id="add-server-input">
<label class="mdl-textfield__label" for="add-server-input">Server url</label>
</div>
</div>
<div class="mdl-dialog__actions">
<button type="button" class="mdl-button change" onClick="registryUI.addTag.add();">Add</button>
<button type="button" class="mdl-button close" onClick="registryUI.addTag.close();">Cancel</button>
</div>
</dialog>
<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 || {};
registryUI.addTag.update = this.update;
this.on('updated', function () {
componentHandler.upgradeElements(this['add-server-dialog']);
registryUI.addTag.dialog = registryUI.addTag.dialog || document.querySelector('#add-server-dialog');
registryUI.addTag.addServer = registryUI.addTag.tileServerList || registryUI.addTag.dialog.querySelector('#add-server-input');
if (!registryUI.addTag.dialog.showModal) {
dialogPolyfill.registerDialog(registryUI.addTag.dialog);
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 : '';
}
this['add-server-input'].onkeyup = function (e) {
// if keyCode is Enter
if (e.keyCode == 13) {
registryUI.addTag.add();
}
};
});
registryUI.addTag.onkeyup = function (e) {
// if keyCode is Enter
if (e.keyCode == 13) {
registryUI.addTag.add();
}
};
registryUI.addTag.show = function () {
registryUI.addTag.dialog.showModal();
registryUI.addTag.dialog.open();
};
registryUI.addTag.add = function () {
if (registryUI.addTag.addServer.value && registryUI.addTag.addServer.value.length > 0) {
registryUI.addServer(registryUI.addTag.addServer.value);
if (registryUI.addTag.dialog.getAddServer().length > 0) {
registryUI.addServer(registryUI.addTag.dialog.getAddServer());
}
registryUI.addTag.addServer.value = '';
rg.router.go('home');
registryUI.addTag.dialog.close();
registryUI.home();
registryUI.addTag.close();
};
registryUI.addTag.close = function () {
registryUI.addTag.addServer.value = '';
registryUI.addTag.dialog.tags['material-input'].value = '';
registryUI.addTag.dialog.close();
};
registryUI.addTag.update();
</script>
</add>

View File

@@ -15,29 +15,77 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<app>
<catalog if="{!rg.router.current || rg.router.current.name == 'home'}"></catalog>
<taglist if="{rg.router.current.name == 'taglist'}"></taglist>
<header>
<material-navbar>
<div class="logo">Docker Registry UI</div>
<menu></menu>
</material-navbar>
</header>
<main>
<catalog if="{route.routeName == 'home'}"></catalog>
<taglist if="{route.routeName == 'taglist'}"></taglist>
<change></change>
<add></add>
<remove></remove>
<material-snackbar></material-snackbar>
</main>
<footer>
<material-footer>
<a class="material-footer-logo" href="https://joxit.github.io/docker-registry-ui/">Docker Registry UI</a>
<ul class="material-footer-link-list">
<li>
<a href="https://github.com/Joxit/docker-registry-ui">Contribute on GitHub</a>
</li>
<li>
<a href="https://github.com/Joxit/docker-registry-ui/blob/master/LICENSE">Privacy &amp; Terms</a>
</li>
</ul>
</material-footer>
</footer>
<script>
this.mixin('rg.router');
this.router.add({name: 'home', url: ''});
this.router.add({name: 'taglist', url: '/taglist/:repository/:image'});
this.router.on('go', state => {
switch (state.name) {
case 'taglist':
if (registryUI.taglist.display) {
registryUI.taglist.loadend = false;
registryUI.taglist.display();
}
break;
case 'home':
if (registryUI.catalog.display) {
registryUI.catalog.loadend = false;
registryUI.catalog.display();
}
break;
registryUI.appTag = this;
route.base('#!')
route('', function() {
route.routeName = 'home';
if (registryUI.catalog.display) {
registryUI.catalog.loadend = false;
registryUI.catalog.display();
}
})
this.router.start();
registryUI.appTag.update();
});
route('/taglist/*', function(image) {
route.routeName = 'taglist';
registryUI.taglist.name = image
if (registryUI.taglist.display) {
registryUI.taglist.loadend = false;
registryUI.taglist.display();
}
registryUI.appTag.update();
});
registryUI.home = function() {
if(route.routeName == 'home') {
registryUI.catalog.display();
} else {
route('');
}
};
registryUI.snackbar = function (message, isError) {
registryUI.appTag.tags['material-snackbar'].addToast({'message': message, 'isError': isError});
};
registryUI.errorSnackbar = function (message) {
return registryUI.snackbar(message, true);
}
route.parser(null, function(path, filter) {
const f = filter
.replace(/\?/g, '\\?')
.replace(/\*/g, '([^?#]+?)')
.replace(/\.\./, '.*')
const re = new RegExp('^' + f + '$')
const args = path.match(re)
if (args) return args.slice(1)
});
route.start(true);
</script>
</app>
</app>

View File

@@ -16,57 +16,42 @@
-->
<catalog>
<!-- Begin of tag -->
<div id="catalog-tag" class="catalog">
<div class="section-centerd mdl-card mdl-shadow--2dp mdl-cell--6-col">
<div class="mdl-card__title">
<h2 class="mdl-card__title-text">Repositories of { registryUI.url() }</h2>
</div>
<div id="catalog-spinner" hide="{ registryUI.catalog.loadend }" class="mdl-spinner mdl-js-spinner is-active section-centerd"></div>
<ul class="mdl-list" show="{ registryUI.catalog.loadend }">
<li class="mdl-list__item mdl-menu__item" style="opacity: 1;" each="{ item in registryUI.catalog.repositories }" onclick="registryUI.catalog.go('{item}');">
<span class="mdl-list__item-primary-content">
<i class="material-icons mdl-list__item-icon">send</i>
{ item }
</span>
</li>
</ul>
<material-card ref="catalog-tag" class="catalog">
<div class="material-card-title-action">
<h2>Repositories of { registryUI.name() }</h2>
</div>
<div id="error-snackbar" aria-live="assertive" aria-atomic="true" aria-relevant="text" class="mdl-js-snackbar mdl-snackbar">
<div class="mdl-snackbar__text"></div>
<button class="mdl-snackbar__action" type="button"></button>
<div hide="{ registryUI.catalog.loadend }" class="spinner-wrapper">
<material-spinner></material-spinner>
</div>
</div>
<ul class="list highlight" show="{ registryUI.catalog.loadend }">
<li each="{ item in registryUI.catalog.repositories }" onclick="registryUI.catalog.go('{item}');">
<span>
<i class="material-icons">send</i>
{ item }
</span>
</li>
</ul>
</material-card>
<script>
registryUI.catalog.instance = this;
this.mixin('rg.router');
registryUI.catalog.display = function () {
registryUI.catalog.repositories = [];
var oReq = new Http();
registryUI.catalog.createSnackbar = function (msg) {
var snackbar = document.querySelector('#error-snackbar');
registryUI.catalog.error = msg;
var data = {
message: registryUI.catalog.error,
timeout: 100000,
actionHandler: function () {
snackbar.classList.remove('mdl-snackbar--active');
},
actionText: 'Undo'
};
snackbar.MaterialSnackbar.showSnackbar(data);
};
oReq.addEventListener('load', function () {
registryUI.catalog.repositories = [];
if (this.status == 200) {
registryUI.catalog.repositories = JSON.parse(this.responseText).repositories || [];
registryUI.catalog.repositories.sort();
} else if (this.status == 404) {
registryUI.catalog.createSnackbar('Server not found');
registryUI.snackbar('Server not found', true);
} else {
registryUI.catalog.createSnackbar(this.responseText);
registryUI.snackbar(this.responseText);
}
});
oReq.addEventListener('error', function () {
registryUI.catalog.createSnackbar('An error occured');
registryUI.snackbar('An error occured', true);
registryUI.catalog.repositories = [];
});
oReq.addEventListener('loadend', function () {
registryUI.catalog.loadend = true;
@@ -75,14 +60,8 @@
oReq.open('GET', registryUI.url() + '/v2/_catalog');
oReq.send();
};
this.on('updated', function () {
componentHandler.upgradeElements(this['catalog-tag']);
});
registryUI.catalog.go = function (image) {
rg.router.go('taglist', {
repository: image.split('/')[0],
image: image.split('/')[1]
});
route('taglist/' + image);
};
registryUI.catalog.display();
</script>

View File

@@ -15,52 +15,47 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<change>
<dialog id="change-server-dialog" class="mdl-dialog">
<h4 class="mdl-dialog__title">Change your Server ?</h4>
<div class="mdl-dialog__content">
<div class="mdl-textfield mdl-js-textfield">
<select class="mdl-textfield__input mdl-textfield__select" name="server-list" id="server-list">
<material-popup>
<div class="material-popup-title">Change your Server ?</div>
<div class="material-popup-content">
<div class="select-padding">
<select class="mdl-textfield__input mdl-textfield__select" name="server-list" ref="server-list">
<option each="{ url in registryUI.getRegistryServer() }" value={url}>{url}</option>
</select>
</div>
</div>
<div class="mdl-dialog__actions">
<button type="button" class="mdl-button change" onClick="registryUI.changeTag.change();">Change</button>
<button type="button" class="mdl-button close" onClick="registryUI.changeTag.close();">Cancel</button>
<div class="material-popup-action">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="registryUI.changeTag.change();">Change</material-button>
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="registryUI.changeTag.close();">Cancel</material-button>
</div>
</dialog>
</material-popup>
<script type="text/javascript">
registryUI.changeTag = registryUI.changeTag || {};
registryUI.changeTag.update = this.update;
this.on('updated', function () {
componentHandler.upgradeElements(this['change-server-dialog']);
registryUI.changeTag.dialog = registryUI.changeTag.dialog || document.querySelector('#change-server-dialog');
registryUI.changeTag.serverList = registryUI.changeTag.serverList || registryUI.changeTag.dialog.querySelector('#server-list');
if (!registryUI.changeTag.dialog.showModal) {
dialogPolyfill.registerDialog(registryUI.changeTag.dialog);
}
this['server-list'].onkeyup = function (e) {
// if keyCode is Enter
if (e.keyCode == 13) {
registryUI.changeTag.change();
}
this.one('mount', function () {
registryUI.changeTag.dialog = this.tags['material-popup'];
registryUI.changeTag.dialog.getServerUrl = function () {
return this.refs['server-list']
? this.refs['server-list'].value
: '';
};
registryUI.changeTag.dialog.on('updated', function () {
if (this.refs['server-list']) {
this.refs['server-list'].value = registryUI.url();
}
});
});
registryUI.changeTag.show = function () {
registryUI.changeTag.update();
registryUI.changeTag.serverList.value = registryUI.url();;
registryUI.changeTag.dialog.showModal();
registryUI.changeTag.dialog.open();
};
registryUI.changeTag.change = function () {
if (registryUI.changeTag.serverList.value && registryUI.changeTag.serverList.value.length > 0) {
registryUI.changeServer(registryUI.changeTag.serverList.value);
if (registryUI.changeTag.dialog.getServerUrl().length > 0) {
registryUI.changeServer(registryUI.changeTag.dialog.getServerUrl());
}
registryUI.changeTag.serverList.value = '';
rg.router.go('home');
registryUI.home();
registryUI.changeTag.dialog.close();
};
registryUI.changeTag.close = function () {
registryUI.changeTag.dialog.close();
};
</script>
</change>
</change>

48
src/tags/image-size.tag Normal file
View File

@@ -0,0 +1,48 @@
<!--
Copyright (C) 2018 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/>.
-->
<image-size>
<div>{ this.bytesToSize(this.size) }</div>
<script type="text/javascript">
var self = this;
this.bytesToSize = function (bytes) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes == undefined || isNaN(bytes)) {
return '?';
} else if (bytes == 0) {
return '0 Byte';
}
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return Math.ceil(bytes / Math.pow(1024, i)) + ' ' + sizes[i];
};
var oReq = new Http();
oReq.addEventListener('loadend', function () {
if (this.status == 200 || this.status == 202) {
self.size = JSON.parse(this.responseText).layers.reduce(function (acc, e) {
return acc + e.size;
}, 0);
self.update();
} else if (this.status == 404) {
registryUI.errorSnackbar('Manifest for ' + opts.name + ':' + opts.tag + ' not found');
} else {
registryUI.snackbar(this.responseText);
}
});
oReq.open('GET', registryUI.url() + '/v2/' + opts.name + '/manifests/' + opts.tag);
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
oReq.send();
</script>
</image-size>

View File

@@ -15,23 +15,31 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<menu>
<div id="card-menu" class="mdl-card__menu">
<button id="registry-menu" name="registry-menu" class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect">
<i class="material-icons">more_vert</i>
</button>
<ul class="mdl-menu mdl-menu--bottom-right mdl-js-menu mdl-js-ripple-effect" for="registry-menu">
<li class="mdl-menu__item" onclick="registryUI.addTag.show();">Add URL</li>
<li class="mdl-menu__item" onclick="registryUI.changeTag.show();">Change URL</li>
<li class="mdl-menu__item" onclick="registryUI.removeTag.show();">Remove URL</li>
</ul>
</div>
<material-button id="menu-control-button" onclick="registryUI.menuTag.toggle();" waves-center="true" rounded="true" waves-opacity="0.6" waves-duration="600">
<i class="material-icons">more_vert</i>
</material-button>
<material-dropdown id="menu-control-dropdown">
<p onclick="registryUI.addTag.show(); registryUI.menuTag.close();">Add URL</p>
<p onclick="registryUI.changeTag.show(); registryUI.menuTag.close();">Change URL</p>
<p onclick="registryUI.removeTag.show(); registryUI.menuTag.close();">Remove URL</p>
</material-dropdown>
<div class="overlay" onclick="registryUI.menuTag.close();" show="{ registryUI.menuTag.isOpen && registryUI.menuTag.isOpen() }"></div>
<script type="text/javascript">
registryUI.menuTag = registryUI.menuTag || {};
registryUI.menuTag.update = this.update;
this.on('updated', function () {
componentHandler.upgradeElements(this['card-menu']);
this.one('mount', function(args) {
var self = this;
registryUI.menuTag.close = function() {
self.tags['material-dropdown'].close();
self.update();
}
registryUI.menuTag.isOpen = function() {
return self.tags['material-dropdown'].opened;
}
registryUI.menuTag.toggle = function() {
self.tags['material-dropdown'].opened ? self.tags['material-dropdown'].close() : self.tags['material-dropdown'].open();
self.update();
};
});
registryUI.menuTag.update();
</script>
</menu>

View File

@@ -16,7 +16,7 @@
-->
<remove-image>
<a href="#" onclick="registryUI.removeImage.remove('{ opts.name }', '{ opts.tag }')">
<i class="material-icons mdl-list__item-icon">delete</i>
<i class="material-icons">delete</i>
</a>
<script type="text/javascript">
registryUI.removeImage = registryUI.removeImage || {}
@@ -24,35 +24,35 @@
registryUI.removeImage.remove = function (name, tag) {
var oReq = new Http();
oReq.addEventListener('load', function () {
oReq.addEventListener('loadend', function () {
registryUI.taglist.refresh();
if (this.status == 200) {
if (!this.getAllResponseHeaders().includes('Docker-Content-Digest')) {
registryUI.taglist.createSnackbar('You need tu add Access-Control-Expose-Headers: [\'Docker-Content-Digest\'] in your server configuration.');
if (!this.hasHeader('Docker-Content-Digest')) {
registryUI.errorSnackbar('You need to add Access-Control-Expose-Headers: [\'Docker-Content-Digest\'] in your server configuration.');
return;
}
var digest = this.getResponseHeader('Docker-Content-Digest');
var oReq = new Http();
oReq.addEventListener('load', function () {
oReq.addEventListener('loadend', function () {
if (this.status == 200 || this.status == 202) {
registryUI.taglist.refresh();
registryUI.taglist.createSnackbar('Deleting ' + name + ':' + tag + ' image. Run `registry garbage-collect config.yml` on your registry');
registryUI.snackbar('Deleting ' + name + ':' + tag + ' image. Run `registry garbage-collect config.yml` on your registry');
} else if (this.status == 404) {
registryUI.taglist.createSnackbar('Digest not found');
registryUI.errorSnackbar('Digest not found');
} else {
registryUI.taglist.createSnackbar(this.responseText);
registryUI.snackbar(this.responseText);
}
});
oReq.open('DELETE', registryUI.url() + '/v2/' + name + '/manifests/' + digest);
oReq.setRequestHeader('Accept', 'application/vnd.docker.distribution.manifest.v2+json');
oReq.addEventListener('error', function () {
registryUI.taglist.createSnackbar('An error occurred when deleting image. Check if your server accept DELETE methods Access-Control-Allow-Methods: [\'DELETE\'].');
registryUI.errorSnackbar('An error occurred when deleting image. Check if your server accept DELETE methods Access-Control-Allow-Methods: [\'DELETE\'].');
});
oReq.send();
} else if (this.status == 404) {
registryUI.taglist.createSnackbar('Manifest for' + name + ':' + tag + 'not found');
registryUI.errorSnackbar('Manifest for ' + name + ':' + tag + ' not found');
} else {
registryUI.taglist.createSnackbar(this.responseText);
registryUI.snackbar(this.responseText);
}
});
oReq.open('HEAD', registryUI.url() + '/v2/' + name + '/manifests/' + tag);

View File

@@ -15,33 +15,32 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<remove>
<dialog id="remove-server-dialog" class="mdl-dialog">
<h4 class="mdl-dialog__title">Remove your Registry Server ?</h4>
<div class="mdl-dialog__content">
<div class="mdl-textfield mdl-js-textfield">
<ul class="mdl-list">
<li class="mdl-list__item" each="{ url in registryUI.getRegistryServer() }">
<span class="mdl-list__item-primary-content">
<a href="#" onClick="registryUI.removeTag.removeUrl('{url}');">
<i class="material-icons mdl-list__item-icon">delete</i>
</a>
<span class="url">{url}</span>
</span>
</li>
</ul>
</div>
</div>
<div class="mdl-dialog__actions">
<button type="button" class="mdl-button close" onClick="registryUI.removeTag.close();">Close</button>
</div>
</dialog>
<material-popup>
<div class="material-popup-title">Remove your Registry Server ?</div>
<div class="material-popup-content">
<ul class="list">
<li each="{ url in registryUI.getRegistryServer() }">
<span>
<a href="#" onClick="registryUI.removeTag.removeUrl('{url}');">
<i class="material-icons">delete</i>
</a>
<span class="url">{ url }</span>
</span>
</li>
</ul>
</div>
<div class="material-popup-action">
<material-button class="dialog-button" waves-color="rgba(158,158,158,.4)" onClick="registryUI.removeTag.close();">Close</material-button>
</div>
</material-popup>
<script type="text/javascript">
registryUI.removeTag = registryUI.removeTag || {}
registryUI.removeTag.update = this.update;
registryUI.removeTag.removeUrl = function (url) {
registryUI.removeServer(url);
registryUI.removeTag.update();
registryUI.removeTag.close();
};
registryUI.removeTag.close = function () {
@@ -50,15 +49,11 @@
};
registryUI.removeTag.show = function () {
registryUI.removeTag.update();
registryUI.removeTag.dialog.showModal();
registryUI.removeTag.dialog.open();
};
this.on('updated', function () {
registryUI.removeTag.dialog = this['remove-server-dialog'];
if (!registryUI.removeTag.dialog.showModal) {
dialogPolyfill.registerDialog(registryUI.removeTag.dialog);
}
this.one('mount', function () {
registryUI.removeTag.dialog = this.tags['material-popup'];
});
</script>
</remove>

View File

@@ -16,90 +16,70 @@
-->
<taglist>
<!-- Begin of tag -->
<div id="taglist-tag" class="taglist">
<div class="section-centerd mdl-card mdl-shadow--2dp mdl-cell--6-col">
<div class="mdl-card__title">
<a href="#" onclick="registryUI.taglist.back();">
<i class="material-icons mdl-list__item-icon">arrow_back</i>
</a>
<h2 class="mdl-card__title-text">Tags of { registryUI.url() + '/' + registryUI.taglist.name }</h2>
</div>
<div id="taglist-spinner" hide="{ registryUI.taglist.loadend }" class="mdl-spinner mdl-js-spinner section-centerd is-active"></div>
<table class="mdl-data-table mdl-js-data-table full-table" show="{ registryUI.taglist.loadend }" style="border: none;">
<thead>
<tr>
<th class="mdl-data-table__cell--non-numeric">Repository</th>
<th class="{ registryUI.taglist.asc ? 'mdl-data-table__header--sorted-ascending' : 'mdl-data-table__header--sorted-descending' }" onclick="registryUI.taglist.reverse();">Tag</th>
<th show="{ registryUI.isImageRemoveActivated }" ></th>
</tr>
</thead>
<tbody>
<tr each="{ item in registryUI.taglist.tags }">
<td class="mdl-data-table__cell--non-numeric">{ registryUI.taglist.name }</td>
<td>{ item }</td>
<td show="{ registryUI.isImageRemoveActivated }" >
<remove-image name={ registryUI.taglist.name } tag={ item } />
</td>
</tr>
</tbody>
</table>
<material-card ref="taglist-tag" class="taglist">
<div class="material-card-title-action">
<a href="#!" onclick="registryUI.home();">
<i class="material-icons">arrow_back</i>
</a>
<h2>Tags of { registryUI.name() + '/' + registryUI.taglist.name }</h2>
</div>
<div id="error-snackbar" aria-live="assertive" aria-atomic="true" aria-relevant="text" class="mdl-js-snackbar mdl-snackbar">
<div class="mdl-snackbar__text"></div>
<button class="mdl-snackbar__action" type="button"></button>
<div hide="{ registryUI.taglist.loadend }" class="spinner-wrapper">
<material-spinner></material-spinner>
</div>
</div>
<table show="{ registryUI.taglist.loadend }" style="border: none;">
<thead>
<tr>
<th class="material-card-th-left">Repository</th>
<th>Size</th>
<th class="{ registryUI.taglist.asc ? 'material-card-th-sorted-ascending' : 'material-card-th-sorted-descending' }" onclick="registryUI.taglist.reverse();">Tag</th>
<th show="{ registryUI.isImageRemoveActivated }"></th>
</tr>
</thead>
<tbody>
<tr each="{ item in registryUI.taglist.tags }">
<td class="material-card-th-left">{ registryUI.taglist.name }</td>
<td><image-size name={ registryUI.taglist.name } tag={ item } /></td>
<td>{ item }</td>
<td show="{ registryUI.isImageRemoveActivated }">
<remove-image name={ registryUI.taglist.name } tag={ item }/>
</td>
</tr>
</tbody>
</table>
</material-card>
<script>
registryUI.taglist.instance = this;
registryUI.taglist.display = function () {
if (rg.router.current && rg.router.current.name == 'taglist') {
name = rg.router.current.params.repository + (rg.router.current.params.image
? '/' + rg.router.current.params.image
: '');
registryUI.taglist.tags = [];
if (route.routeName == 'taglist') {
var oReq = new Http();
registryUI.taglist.name = name;
registryUI.taglist.instance.update();
registryUI.taglist.createSnackbar = function (msg) {
var snackbar = document.querySelector('#error-snackbar');
registryUI.taglist.error = msg;
var data = {
message: registryUI.taglist.error,
timeout: 100000,
actionHandler: function () {
snackbar.classList.remove('mdl-snackbar--active');
},
actionText: 'Undo'
};
snackbar.MaterialSnackbar.showSnackbar(data);
};
oReq.addEventListener('load', function () {
registryUI.taglist.tags = [];
if (this.status == 200) {
registryUI.taglist.tags = JSON.parse(this.responseText).tags || [];
registryUI.taglist.tags.sort();
} else if (this.status == 404) {
registryUI.taglist.createSnackbar('Server not found');
registryUI.snackbar('Server not found', true);
} else {
registryUI.taglist.createSnackbar(this.responseText);
registryUI.snackbar(this.responseText, true);
}
});
oReq.addEventListener('error', function () {
registryUI.taglist.createSnackbar('An error occured');
registryUI.snackbar('An error occured', true);
registryUI.taglist.tags = [];
});
oReq.addEventListener('loadend', function () {
registryUI.taglist.loadend = true;
registryUI.taglist.instance.update();
});
oReq.open('GET', registryUI.url() + '/v2/' + name + '/tags/list');
oReq.open('GET', registryUI.url() + '/v2/' + registryUI.taglist.name + '/tags/list');
oReq.send();
registryUI.taglist.asc = true;
}
};
registryUI.taglist.display();
registryUI.taglist.instance.update();
this.on('updated', function () {
componentHandler.upgradeElements(this['taglist-tag']);
});
registryUI.taglist.reverse = function () {
if (registryUI.taglist.asc) {
@@ -111,11 +91,8 @@
}
registryUI.taglist.instance.update();
};
registryUI.taglist.back = function () {
rg.router.go('home');
};
registryUI.taglist.refresh = function () {
rg.router.go(rg.router.current.name, rg.router.current.params);
route(registryUI.taglist.name);
};
</script>
<!-- End of tag -->