mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2026-02-17 21:19:51 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf3e3c9fa8 | ||
|
|
325a2417f4 | ||
|
|
adab8b03de |
@@ -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
|
||||
|
||||
18
README.md
18
README.md
@@ -21,6 +21,7 @@ This web user interface uses [Riot](https://github.com/Riot/riot) the react-like
|
||||
- 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).
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -83,13 +84,26 @@ 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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/bin/sh
|
||||
$@
|
||||
sed -i "s,\${URL},${URL}," scripts/script.js
|
||||
|
||||
@@ -6,8 +6,13 @@ 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
|
||||
|
||||
2
dist/scripts/script.js
vendored
2
dist/scripts/script.js
vendored
@@ -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.oReq.hasHeader=Http.hasHeader,this._events={},this._headers={}}Http.prototype.addEventListener=function(e,r){this._events[e]=r;var t=this;switch(e){case"loadend":t.oReq.addEventListener("loadend",function(){if(401==this.status){var e=new XMLHttpRequest;e.open(t._method,t._url);for(key in t._events)e.addEventListener(key,t._events[key]);for(key in t._headers)e.setRequestHeader(key,t._headers[key]);e.withCredentials=!0,e.hasHeader=Http.hasHeader,e.send()}else r.bind(this)()});break;case"load":t.oReq.addEventListener("load",function(){401!==this.status&&r.bind(this)()});break;default:t.oReq.addEventListener(e,function(){r.bind(this)()})}},Http.prototype.setRequestHeader=function(e,r){this.oReq.setRequestHeader(e,r),this._headers[e]=r},Http.prototype.open=function(e,r){this._method=e,this._url=r,this.oReq.open(e,r)},Http.prototype.send=function(){this.oReq.send()},Http.hasHeader=function(e){return this.getAllResponseHeaders().split("\n").some(function(r){return new RegExp("^"+e+":","i").test(r)})};var registryUI={};registryUI.URL_QUERY_PARAM_REGEX=/[&?]url=/,registryUI.URL_PARAM_REGEX=/^url=/,registryUI.url=function(e){if(!registryUI._url){var r=registryUI.getUrlQueryParam();if(r)try{return registryUI._url=registryUI.decodeURI(r),registryUI._url}catch(e){console.log(e)}registryUI._url=registryUI.getRegistryServer(0)}return registryUI._url},registryUI.getRegistryServer=function(e){try{var r=JSON.parse(localStorage.getItem("registryServer"));if(r instanceof Array)return isNaN(e)?r.map(function(e){return e.trim().replace(/\/*$/,"")}):r[e]}catch(e){}return isNaN(e)?[]:""},registryUI.addServer=function(e){var r=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var t=r.indexOf(e);t==-1&&(r.push(e),registryUI._url||registryUI.updateHistory(e),localStorage.setItem("registryServer",JSON.stringify(r)))},registryUI.changeServer=function(e){var r=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var t=r.indexOf(e);t!=-1&&(r.splice(t,1),r=[e].concat(r),registryUI.updateHistory(e),localStorage.setItem("registryServer",JSON.stringify(r)))},registryUI.removeServer=function(e){var r=registryUI.getRegistryServer();e=e.trim().replace(/\/*$/,"");var t=r.indexOf(e);t!=-1&&(r.splice(t,1),localStorage.setItem("registryServer",JSON.stringify(r)),e==registryUI.url()&&(registryUI.updateHistory(registryUI.getRegistryServer(0)),rg.router.go("home")))},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 r=e.split(/^\?|&/).find(function(e){return e&®istryUI.URL_PARAM_REGEX.test(e)});return r?r.replace(registryUI.URL_PARAM_REGEX,""):r}},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("*");
|
||||
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.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&®istryUI.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("*");
|
||||
2
dist/scripts/tags-static.js
vendored
2
dist/scripts/tags-static.js
vendored
File diff suppressed because one or more lines are too long
2
dist/scripts/tags.js
vendored
2
dist/scripts/tags.js
vendored
File diff suppressed because one or more lines are too long
6
dist/scripts/vendor.js
vendored
6
dist/scripts/vendor.js
vendored
File diff suppressed because one or more lines are too long
@@ -89,7 +89,7 @@ gulp.task('scripts', ['html'], function() {
|
||||
});
|
||||
|
||||
gulp.task('vendor', ['html'], function() {
|
||||
return gulp.src(['node_modules/riot/riot.min.js', 'node_modules/riotgear-router/dist/rg-router.min.js', 'node_modules/riot-mui/build/js/riot-mui-min.js'])
|
||||
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'));
|
||||
});
|
||||
|
||||
34
nginx/default.conf
Normal file
34
nginx/default.conf
Normal file
@@ -0,0 +1,34 @@
|
||||
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 {
|
||||
#! set $upstream ${REGISTRY_URL};
|
||||
#! proxy_pass $upstream;
|
||||
#! }
|
||||
|
||||
#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;
|
||||
#}
|
||||
}
|
||||
|
||||
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docker-registry-ui",
|
||||
"version": "0.3.0",
|
||||
"version": "0.3.1",
|
||||
"scripts": {
|
||||
"build": "./node_modules/gulp/bin/gulp.js build"
|
||||
},
|
||||
@@ -23,12 +23,12 @@
|
||||
"gulp-license": "^1.1.0",
|
||||
"gulp-riot": "^1.1.1",
|
||||
"gulp-uglify": "^2.1.2",
|
||||
"gulp-useref": "^3.0.0",
|
||||
"gulp-useref": "^3.1.3",
|
||||
"pump": "^1.0.2",
|
||||
"riot": "^3.7.0",
|
||||
"riot": "^3.7.4",
|
||||
"riot-mui": "^0.1.1",
|
||||
"riotgear-router": "^1.3.1",
|
||||
"uglify-js": "^3.0.28",
|
||||
"riot-route": "^3.1.2",
|
||||
"uglify-js": "^3.1.8",
|
||||
"uglify-js-harmony": "^2.7.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<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/riot-route/dist/route.js"></script>
|
||||
<script src="../node_modules/riot-mui/build/js/riot-mui.js"></script>
|
||||
<!-- endbuild -->
|
||||
<!-- build:js scripts/tags.js -->
|
||||
|
||||
@@ -80,7 +80,7 @@ registryUI.removeServer = function(url) {
|
||||
localStorage.setItem('registryServer', JSON.stringify(registryServer));
|
||||
if (url == registryUI.url()) {
|
||||
registryUI.updateHistory(registryUI.getRegistryServer(0));
|
||||
rg.router.go('home');
|
||||
route('');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
if (registryUI.addTag.dialog.getAddServer().length > 0) {
|
||||
registryUI.addServer(registryUI.addTag.dialog.getAddServer());
|
||||
}
|
||||
rg.router.go('home');
|
||||
registryUI.home();
|
||||
registryUI.addTag.close();
|
||||
};
|
||||
registryUI.addTag.close = function () {
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
</material-navbar>
|
||||
</header>
|
||||
<main>
|
||||
<catalog if="{!rg.router.current || rg.router.current.name == 'home'}"></catalog>
|
||||
<taglist if="{rg.router.current && rg.router.current.name == 'taglist'}"></taglist>
|
||||
<catalog if="{route.routeName == 'home'}"></catalog>
|
||||
<taglist if="{route.routeName == 'taglist'}"></taglist>
|
||||
<change></change>
|
||||
<add></add>
|
||||
<remove></remove>
|
||||
@@ -45,32 +45,47 @@
|
||||
</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();
|
||||
}
|
||||
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);
|
||||
}
|
||||
this.router.start();
|
||||
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>
|
||||
@@ -35,8 +35,8 @@
|
||||
|
||||
<script>
|
||||
registryUI.catalog.instance = this;
|
||||
this.mixin('rg.router');
|
||||
registryUI.catalog.display = function () {
|
||||
registryUI.catalog.repositories = [];
|
||||
var oReq = new Http();
|
||||
oReq.addEventListener('load', function () {
|
||||
registryUI.catalog.repositories = [];
|
||||
@@ -61,10 +61,7 @@
|
||||
oReq.send();
|
||||
};
|
||||
registryUI.catalog.go = function (image) {
|
||||
rg.router.go('taglist', {
|
||||
repository: image.split('/')[0],
|
||||
image: image.split('/')[1]
|
||||
});
|
||||
route('taglist/' + image);
|
||||
};
|
||||
registryUI.catalog.display();
|
||||
</script>
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
if (registryUI.changeTag.dialog.getServerUrl().length > 0) {
|
||||
registryUI.changeServer(registryUI.changeTag.dialog.getServerUrl());
|
||||
}
|
||||
rg.router.go('home');
|
||||
registryUI.home();
|
||||
registryUI.changeTag.dialog.close();
|
||||
};
|
||||
registryUI.changeTag.close = function () {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<!-- Begin of tag -->
|
||||
<material-card ref="taglist-tag" class="taglist">
|
||||
<div class="material-card-title-action">
|
||||
<a href="#" onclick="registryUI.taglist.back();">
|
||||
<a href="#!" onclick="registryUI.home();">
|
||||
<i class="material-icons">arrow_back</i>
|
||||
</a>
|
||||
<h2>Tags of { registryUI.url() + '/' + registryUI.taglist.name }</h2>
|
||||
@@ -48,12 +48,9 @@
|
||||
<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();
|
||||
oReq.addEventListener('load', function () {
|
||||
registryUI.taglist.tags = [];
|
||||
@@ -74,7 +71,7 @@
|
||||
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;
|
||||
}
|
||||
@@ -92,11 +89,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 -->
|
||||
|
||||
Reference in New Issue
Block a user