mirror of
https://github.com/Joxit/docker-registry-ui.git
synced 2026-02-19 21:29:51 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
115105a2a0 | ||
|
|
5173110883 | ||
|
|
d3e93f7064 | ||
|
|
6221958c78 | ||
|
|
05c2cf2425 | ||
|
|
82efd33c14 | ||
|
|
13936aadb1 | ||
|
|
6bff056086 | ||
|
|
b28fe68dcd | ||
|
|
d6523a4205 | ||
|
|
3b5f5201a6 |
38
.gitignore
vendored
38
.gitignore
vendored
@@ -1,3 +1,37 @@
|
||||
.project
|
||||
node_modules
|
||||
# dependencies
|
||||
/node_modules
|
||||
package-lock.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
*.iml
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
.vscode/
|
||||
|
||||
registry-data
|
||||
|
||||
@@ -24,7 +24,7 @@ This web user interface uses [Riot](https://github.com/Riot/riot) the react-like
|
||||
- Use `joxit/docker-registry-ui:static` as reverse proxy to your docker registry (This will avoid CORS).
|
||||
- Display image size (see #30)
|
||||
- Add Title when using REGISTRY_URL (see #28)
|
||||
- Alpine and Debian based images with supports for arm32v7
|
||||
- Alpine and Debian based images with supports for arm32v7 and arm64v8
|
||||
- Copy `docker pull` command to clipbloard
|
||||
- Show sha256 for specific tag (hover image tag)
|
||||
|
||||
@@ -122,6 +122,8 @@ 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 -e REGISTRY_TITLE="My registry" joxit/docker-registry-ui:static
|
||||
```
|
||||
|
||||
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/master/examples/ui-as-proxy/) or docker-registry-ui as standalone [here](https://github.com/Joxit/docker-registry-ui/tree/master/examples/ui-as-standalone/).
|
||||
|
||||
## Using CORS
|
||||
|
||||
Your server should be configured to accept CORS.
|
||||
|
||||
@@ -20,8 +20,7 @@ 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 dist/scripts/docker-registry-ui-static.js /usr/share/nginx/html/scripts/docker-registry-ui.js
|
||||
COPY bin/entrypoint /bin
|
||||
|
||||
ENTRYPOINT entrypoint
|
||||
|
||||
26
arm64v8-static.dockerfile
Normal file
26
arm64v8-static.dockerfile
Normal file
@@ -0,0 +1,26 @@
|
||||
# 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 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/>.
|
||||
FROM arm64v8/nginx
|
||||
|
||||
LABEL 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/docker-registry-ui-static.js /usr/share/nginx/html/scripts/docker-registry-ui.js
|
||||
COPY bin/entrypoint /bin
|
||||
|
||||
ENTRYPOINT entrypoint
|
||||
21
arm64v8.dockerfile
Normal file
21
arm64v8.dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
# 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 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/>.
|
||||
FROM arm64v8/nginx
|
||||
|
||||
LABEL maintainer="Jones MAGLOIRE @Joxit"
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
COPY dist/ /usr/share/nginx/html/
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/bin/sh
|
||||
$@
|
||||
sed -i "s,\${URL},${URL}," scripts/script.js
|
||||
sed -i "s,\${REGISTRY_TITLE},${REGISTRY_TITLE}," scripts/script.js
|
||||
sed -i "s,\${URL},${URL}," scripts/docker-registry-ui.js
|
||||
sed -i "s,\${REGISTRY_TITLE},${REGISTRY_TITLE}," scripts/docker-registry-ui.js
|
||||
|
||||
if [ -z "${DELETE_IMAGES}" ] || [ "${DELETE_IMAGES}" = false ] ; then
|
||||
sed -i "s/registryUI.isImageRemoveActivated *= *[^,;]*/registryUI.isImageRemoveActivated=false/" scripts/script.js
|
||||
sed -i "s/registryUI.isImageRemoveActivated *= *[^,;]*/registryUI.isImageRemoveActivated=false/" scripts/docker-registry-ui.js
|
||||
fi
|
||||
|
||||
if [ -n "${REGISTRY_URL}" ] ; then
|
||||
|
||||
@@ -20,8 +20,7 @@ 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 dist/scripts/docker-registry-ui-static.js /usr/share/nginx/html/scripts/docker-registry-ui.js
|
||||
COPY bin/entrypoint /bin
|
||||
|
||||
ENTRYPOINT entrypoint
|
||||
|
||||
@@ -28,8 +28,7 @@
|
||||
<body>
|
||||
<app></app>
|
||||
<script src="../dist/scripts/vendor.js"></script>
|
||||
<script src="../dist/scripts/tags.js"></script>
|
||||
<script src="../dist/scripts/script.js"></script>
|
||||
<script src="../dist/scripts/docker-registry-ui.js"></script>
|
||||
<script>
|
||||
(function(i, s, o, g, r, a, m) {
|
||||
i['GoogleAnalyticsObject'] = r;
|
||||
|
||||
2
dist/index.html
vendored
2
dist/index.html
vendored
@@ -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&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>
|
||||
--><!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&lang=en" rel="stylesheet" type="text/css"><title>Docker Registry UI</title></head><body><app></app><script src="scripts/init.js"></script><script src="scripts/vendor.js"></script><script src="scripts/docker-registry-ui.js"></script></body></html>
|
||||
18
dist/scripts/docker-registry-ui-static.js
vendored
Normal file
18
dist/scripts/docker-registry-ui-static.js
vendored
Normal file
File diff suppressed because one or more lines are too long
18
dist/scripts/docker-registry-ui.js
vendored
Normal file
18
dist/scripts/docker-registry-ui.js
vendored
Normal file
File diff suppressed because one or more lines are too long
27
dist/scripts/init.js
vendored
Normal file
27
dist/scripts/init.js
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
const REGISTRY_SERVER_KEY = 'registryServer';
|
||||
let registryServersStr = localStorage.getItem(REGISTRY_SERVER_KEY);
|
||||
let registryServers = [];
|
||||
if (registryServersStr !== null){
|
||||
let rs = JSON.parse(registryServersStr);
|
||||
if (rs instanceof Array) {
|
||||
registryServers = rs;
|
||||
}
|
||||
}
|
||||
|
||||
const apiHost = location.protocol+'//'+location.host;
|
||||
if (!registryServers.includes(apiHost)) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', apiHost+"/v2/", true);
|
||||
xhr.onload = function (e) {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
const version = xhr.getResponseHeader('Docker-Distribution-Api-Version');
|
||||
if (version.startsWith('registry/2.')) {
|
||||
registryServers.unshift(apiHost);
|
||||
localStorage.setItem(REGISTRY_SERVER_KEY, JSON.stringify(registryServers));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
18
dist/scripts/script-static.js
vendored
18
dist/scripts/script-static.js
vendored
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* docker-registry-ui
|
||||
* 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
|
||||
* 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/>.
|
||||
*/
|
||||
function Http(){this.oReq=new XMLHttpRequest,this.oReq.hasHeader=Http.hasHeader,this.oReq.getErrorMessage=Http.getErrorMessage,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.getErrorMessage=Http.getErrorMessage,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)})},Http.getErrorMessage=function(){return registryUI.url()&®istryUI.url().match("^http://")&&"https:"===window.location.protocol?"Mixed Content: The page at `"+window.location.origin+"` was loaded over HTTPS, but requested an insecure server endpoint `"+registryUI.url()+"`. This request has been blocked; the content must be served over HTTPS.":registryUI.url()?"An error occured":"Incorrect server endpoint."};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");
|
||||
18
dist/scripts/script.js
vendored
18
dist/scripts/script.js
vendored
@@ -1,18 +0,0 @@
|
||||
/*!
|
||||
* docker-registry-ui
|
||||
* 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
|
||||
* 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/>.
|
||||
*/
|
||||
function Http(){this.oReq=new XMLHttpRequest,this.oReq.hasHeader=Http.hasHeader,this.oReq.getErrorMessage=Http.getErrorMessage,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.getErrorMessage=Http.getErrorMessage,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)})},Http.getErrorMessage=function(){return registryUI.url()&®istryUI.url().match("^http://")&&"https:"===window.location.protocol?"Mixed Content: The page at `"+window.location.origin+"` was loaded over HTTPS, but requested an insecure server endpoint `"+registryUI.url()+"`. This request has been blocked; the content must be served over HTTPS.":registryUI.url()?"An error occured":"Incorrect server endpoint."};var registryUI={};registryUI.URL_QUERY_PARAM_REGEX=/[&?]url=/,registryUI.URL_PARAM_REGEX=/^url=/,registryUI.name=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)),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 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("*");
|
||||
18
dist/scripts/tags-static.js
vendored
18
dist/scripts/tags-static.js
vendored
File diff suppressed because one or more lines are too long
18
dist/scripts/tags.js
vendored
18
dist/scripts/tags.js
vendored
File diff suppressed because one or more lines are too long
4
dist/scripts/vendor.js
vendored
4
dist/scripts/vendor.js
vendored
File diff suppressed because one or more lines are too long
21
examples/ui-as-proxy/README.md
Normal file
21
examples/ui-as-proxy/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# Docker Registry Static as proxy example
|
||||
|
||||
You can set up the static user interface as proxy in several ways.
|
||||
|
||||
If you want to populate your registry, use `populate.sh` script.
|
||||
The interface and the docker registry will be accessible with <http://localhost>.
|
||||
|
||||
The simplest way is with `simple.yml` docker-compose file.
|
||||
|
||||
```sh
|
||||
docker-compose -f simple.yml up -d
|
||||
./populate.sh
|
||||
```
|
||||
|
||||
You can add some credentials to access your registry wit `credentials.yml` docker-compose file.
|
||||
Credentials for this example are login: `registry` and password: `ui` using bcrypt.
|
||||
|
||||
```sh
|
||||
docker-compose -f credentials.yml up -d
|
||||
./populate.sh
|
||||
```
|
||||
25
examples/ui-as-proxy/credentials.yml
Normal file
25
examples/ui-as-proxy/credentials.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
version: '2.0'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2.6.2
|
||||
volumes:
|
||||
- ./registry-data:/var/lib/registry
|
||||
- ./registry-config/credentials.yml:/etc/docker/registry/config.yml
|
||||
- ./registry-config/htpasswd:/etc/docker/registry/htpasswd
|
||||
networks:
|
||||
- registry-ui-net
|
||||
|
||||
ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- REGISTRY_TITLE=My Private Docker Registry
|
||||
- REGISTRY_URL=http://registry:5000
|
||||
depends_on:
|
||||
- registry
|
||||
networks:
|
||||
- registry-ui-net
|
||||
|
||||
networks:
|
||||
registry-ui-net:
|
||||
17
examples/ui-as-proxy/populate.sh
Normal file
17
examples/ui-as-proxy/populate.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker tag joxit/docker-registry-ui:static localhost/joxit/docker-registry-ui:static
|
||||
docker tag joxit/docker-registry-ui:static localhost/joxit/docker-registry-ui:0.3
|
||||
docker tag joxit/docker-registry-ui:static localhost/joxit/docker-registry-ui:0.3.0
|
||||
docker tag joxit/docker-registry-ui:static localhost/joxit/docker-registry-ui:0.3.0-static
|
||||
docker tag joxit/docker-registry-ui:static localhost/joxit/docker-registry-ui:0.3-static
|
||||
|
||||
docker push localhost/joxit/docker-registry-ui
|
||||
|
||||
docker tag registry:2.6.2 localhost/registry:latest
|
||||
docker tag registry:2.6.2 localhost/registry:2.6.2
|
||||
docker tag registry:2.6.2 localhost/registry:2.6
|
||||
docker tag registry:2.6.2 localhost/registry:2.6.0
|
||||
docker tag registry:2.6.2 localhost/registry:2
|
||||
|
||||
docker push localhost/registry
|
||||
25
examples/ui-as-proxy/registry-config/credentials.yml
Normal file
25
examples/ui-as-proxy/registry-config/credentials.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
version: 0.1
|
||||
log:
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
delete:
|
||||
enabled: true
|
||||
cache:
|
||||
blobdescriptor: inmemory
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
http:
|
||||
addr: :5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
Access-Control-Allow-Origin: ['http://localhost']
|
||||
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
|
||||
Access-Control-Allow-Headers: ['Authorization']
|
||||
Access-Control-Max-Age: [1728000]
|
||||
Access-Control-Allow-Credentials: [true]
|
||||
Access-Control-Expose-Headers: ['Docker-Content-Digest']
|
||||
auth:
|
||||
htpasswd:
|
||||
realm: basic-realm
|
||||
path: /etc/docker/registry/htpasswd
|
||||
1
examples/ui-as-proxy/registry-config/htpasswd
Normal file
1
examples/ui-as-proxy/registry-config/htpasswd
Normal file
@@ -0,0 +1 @@
|
||||
registry:$2y$11$1bmuJLK8HrQl5ACS/WeqRuJLUArUZfUcP2R23asmozEpfN76.pCHy
|
||||
23
examples/ui-as-proxy/simple.yml
Normal file
23
examples/ui-as-proxy/simple.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
version: '2.0'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2.6.2
|
||||
volumes:
|
||||
- ./registry-data:/var/lib/registry
|
||||
networks:
|
||||
- docker-registry-ui
|
||||
|
||||
ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- REGISTRY_TITLE=My Private Docker Registry
|
||||
- REGISTRY_URL=http://registry:5000
|
||||
depends_on:
|
||||
- registry
|
||||
networks:
|
||||
- docker-registry-ui
|
||||
|
||||
networks:
|
||||
registry-ui-net:
|
||||
22
examples/ui-as-standalone/README.md
Normal file
22
examples/ui-as-standalone/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Docker Registry Static as standalone example
|
||||
|
||||
You can set up the static user interface as standalone in several ways.
|
||||
|
||||
If you want to populate your registry, use `populate.sh` script.
|
||||
The interface will be accessible with <http://localhost>.
|
||||
Your docker registry will be accessible with <http://localhost:5000>.
|
||||
|
||||
The simplest way is with `simple.yml` docker-compose file.
|
||||
|
||||
```sh
|
||||
docker-compose -f simple.yml up -d
|
||||
./populate.sh
|
||||
```
|
||||
|
||||
You can add some credentials to access your registry wit `credentials.yml` docker-compose file.
|
||||
Credentials for this example are login: `registry` and password: `ui` using bcrypt.
|
||||
|
||||
```sh
|
||||
docker-compose -f credentials.yml up -d
|
||||
./populate.sh
|
||||
```
|
||||
20
examples/ui-as-standalone/credentials.yml
Normal file
20
examples/ui-as-standalone/credentials.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
version: '2.0'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2.6.2
|
||||
ports:
|
||||
- 5000:5000
|
||||
volumes:
|
||||
- ./registry-data:/var/lib/registry
|
||||
- ./registry-config/credentials.yml:/etc/docker/registry/config.yml
|
||||
- ./registry-config/htpasswd:/etc/docker/registry/htpasswd
|
||||
|
||||
ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- REGISTRY_TITLE=My Private Docker Registry
|
||||
- URL=http://localhost:5000
|
||||
depends_on:
|
||||
- registry
|
||||
17
examples/ui-as-standalone/populate.sh
Executable file
17
examples/ui-as-standalone/populate.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:static
|
||||
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:0.3
|
||||
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:0.3.0
|
||||
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:0.3.0-static
|
||||
docker tag joxit/docker-registry-ui:static localhost:5000/joxit/docker-registry-ui:0.3-static
|
||||
|
||||
docker push localhost:5000/joxit/docker-registry-ui
|
||||
|
||||
docker tag registry:2.6.2 localhost:5000/registry:latest
|
||||
docker tag registry:2.6.2 localhost:5000/registry:2.6.2
|
||||
docker tag registry:2.6.2 localhost:5000/registry:2.6
|
||||
docker tag registry:2.6.2 localhost:5000/registry:2.6.0
|
||||
docker tag registry:2.6.2 localhost:5000/registry:2
|
||||
|
||||
docker push localhost:5000/registry
|
||||
25
examples/ui-as-standalone/registry-config/credentials.yml
Normal file
25
examples/ui-as-standalone/registry-config/credentials.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
version: 0.1
|
||||
log:
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
delete:
|
||||
enabled: true
|
||||
cache:
|
||||
blobdescriptor: inmemory
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
http:
|
||||
addr: :5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
Access-Control-Allow-Origin: ['http://localhost']
|
||||
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
|
||||
Access-Control-Allow-Headers: ['Authorization']
|
||||
Access-Control-Max-Age: [1728000]
|
||||
Access-Control-Allow-Credentials: [true]
|
||||
Access-Control-Expose-Headers: ['Docker-Content-Digest']
|
||||
auth:
|
||||
htpasswd:
|
||||
realm: basic-realm
|
||||
path: /etc/docker/registry/htpasswd
|
||||
1
examples/ui-as-standalone/registry-config/htpasswd
Normal file
1
examples/ui-as-standalone/registry-config/htpasswd
Normal file
@@ -0,0 +1 @@
|
||||
registry:$2y$11$1bmuJLK8HrQl5ACS/WeqRuJLUArUZfUcP2R23asmozEpfN76.pCHy
|
||||
21
examples/ui-as-standalone/registry-config/simple.yml
Normal file
21
examples/ui-as-standalone/registry-config/simple.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
version: 0.1
|
||||
log:
|
||||
fields:
|
||||
service: registry
|
||||
storage:
|
||||
delete:
|
||||
enabled: true
|
||||
cache:
|
||||
blobdescriptor: inmemory
|
||||
filesystem:
|
||||
rootdirectory: /var/lib/registry
|
||||
http:
|
||||
addr: :5000
|
||||
headers:
|
||||
X-Content-Type-Options: [nosniff]
|
||||
Access-Control-Allow-Origin: ['http://localhost']
|
||||
Access-Control-Allow-Methods: ['HEAD', 'GET', 'OPTIONS', 'DELETE']
|
||||
Access-Control-Allow-Headers: ['Authorization']
|
||||
Access-Control-Max-Age: [1728000]
|
||||
Access-Control-Allow-Credentials: [true]
|
||||
Access-Control-Expose-Headers: ['Docker-Content-Digest']
|
||||
19
examples/ui-as-standalone/simple.yml
Normal file
19
examples/ui-as-standalone/simple.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
version: '2.0'
|
||||
services:
|
||||
registry:
|
||||
image: registry:2.6.2
|
||||
ports:
|
||||
- 5000:5000
|
||||
volumes:
|
||||
- ./registry-data:/var/lib/registry
|
||||
- ./registry-config/simple.yml:/etc/docker/registry/config.yml
|
||||
|
||||
ui:
|
||||
image: joxit/docker-registry-ui:static
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- REGISTRY_TITLE=My Private Docker Registry
|
||||
- URL=http://localhost:5000
|
||||
depends_on:
|
||||
- registry
|
||||
99
gulpfile.js
99
gulpfile.js
@@ -1,24 +1,47 @@
|
||||
'use strict';
|
||||
var cleanCSS = require('gulp-clean-css');
|
||||
var concat = require('gulp-concat');
|
||||
var del = require('del');
|
||||
var filter = require('gulp-filter');
|
||||
var fs = require('fs');
|
||||
var gIf = require('gulp-if');
|
||||
var gulp = require('gulp');
|
||||
var htmlmin = require('gulp-htmlmin');
|
||||
var license = require('gulp-license');
|
||||
var riot = require('gulp-riot');
|
||||
var minifier = require('gulp-uglify/minifier');
|
||||
var uglify = require('uglify-js-harmony');
|
||||
var useref = require('gulp-useref');
|
||||
var injectVersion = require('gulp-inject-version');
|
||||
const cleanCSS = require('gulp-clean-css');
|
||||
const concat = require('gulp-concat');
|
||||
const del = require('del');
|
||||
const filter = require('gulp-filter');
|
||||
const fs = require('fs');
|
||||
const gIf = require('gulp-if');
|
||||
const gulp = require('gulp');
|
||||
const htmlmin = require('gulp-htmlmin');
|
||||
const license = require('gulp-license');
|
||||
const riot = require('gulp-riot');
|
||||
const uglify = require('uglify-es');
|
||||
const minifier = require('gulp-uglify/composer')(uglify);
|
||||
const useref = require('gulp-useref');
|
||||
const injectVersion = require('gulp-inject-version');
|
||||
const merge = require('stream-series');
|
||||
|
||||
const allTags = 'src/tags/*.tag';
|
||||
|
||||
const allScripts = [
|
||||
'src/scripts/http.js',
|
||||
'src/scripts/script.js'
|
||||
];
|
||||
|
||||
const staticTags = [
|
||||
'src/tags/catalog.tag',
|
||||
'src/tags/app.tag',
|
||||
'src/tags/taglist.tag',
|
||||
'src/tags/copy-to-clipboard.tag',
|
||||
'src/tags/remove-image.tag',
|
||||
'src/tags/image-size.tag',
|
||||
'src/tags/image-tag.tag'
|
||||
];
|
||||
|
||||
const staticScripts = [
|
||||
'src/scripts/http.js',
|
||||
'src/scripts/static.js'
|
||||
];
|
||||
|
||||
gulp.task('html', function() {
|
||||
var htmlFilter = filter('**/*.html', {restore: true});
|
||||
return gulp.src(['src/index.html'])
|
||||
.pipe(useref())
|
||||
.pipe(gIf(['*.js', '!*.min.js'], minifier({}, uglify))) // FIXME
|
||||
.pipe(gIf(['*.js', '!*.min.js'], minifier())) // FIXME
|
||||
.pipe(htmlFilter)
|
||||
.pipe(htmlmin({
|
||||
removeComments: false,
|
||||
@@ -35,11 +58,10 @@ gulp.task('clean', function(done) {
|
||||
return del(['dist']);
|
||||
});
|
||||
|
||||
gulp.task('riot-tag', ['html'], function() {
|
||||
return gulp.src('src/tags/*.tag')
|
||||
.pipe(concat('tags.js'))
|
||||
.pipe(riot())
|
||||
.pipe(minifier({}, uglify))
|
||||
gulp.task('docker-registry-ui-static', ['html'], function() {
|
||||
return merge(gulp.src(staticScripts), gulp.src(staticTags).pipe(riot()))
|
||||
.pipe(concat('docker-registry-ui-static.js'))
|
||||
.pipe(minifier())
|
||||
.pipe(license('agpl3', {
|
||||
tiny: false,
|
||||
project: 'docker-registry-ui',
|
||||
@@ -50,11 +72,10 @@ gulp.task('riot-tag', ['html'], function() {
|
||||
.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/copy-to-clipboard.tag', 'src/tags/remove-image.tag', 'src/tags/image-size.tag', 'src/tags/image-tag.tag'])
|
||||
.pipe(concat('tags-static.js'))
|
||||
.pipe(riot())
|
||||
.pipe(minifier({}, uglify))
|
||||
gulp.task('docker-registry-ui', ['html'], function() {
|
||||
return merge(gulp.src(allScripts), gulp.src(allTags).pipe(riot()))
|
||||
.pipe(concat('docker-registry-ui.js'))
|
||||
.pipe(minifier())
|
||||
.pipe(license('agpl3', {
|
||||
tiny: false,
|
||||
project: 'docker-registry-ui',
|
||||
@@ -65,32 +86,6 @@ gulp.task('riot-static-tag', ['html'], function() {
|
||||
.pipe(gulp.dest('dist/scripts'));
|
||||
});
|
||||
|
||||
gulp.task('scripts-static', ['html'], function() {
|
||||
return gulp.src(['src/scripts/http.js', 'src/scripts/static.js'])
|
||||
.pipe(concat('script-static.js'))
|
||||
.pipe(minifier({}, uglify))
|
||||
.pipe(license('agpl3', {
|
||||
tiny: false,
|
||||
project: 'docker-registry-ui',
|
||||
year: '2016-2018',
|
||||
organization: 'Jones Magloire @Joxit'
|
||||
}))
|
||||
.pipe(gulp.dest('dist/scripts'));
|
||||
});
|
||||
|
||||
gulp.task('scripts', ['html'], function() {
|
||||
return gulp.src(['src/scripts/http.js', 'src/scripts/script.js'])
|
||||
.pipe(concat('script.js'))
|
||||
.pipe(minifier({}, uglify))
|
||||
.pipe(license('agpl3', {
|
||||
tiny: false,
|
||||
project: 'docker-registry-ui',
|
||||
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'))
|
||||
@@ -118,7 +113,7 @@ gulp.task('fonts', function() {
|
||||
.pipe(gulp.dest('dist/fonts'));
|
||||
});
|
||||
|
||||
gulp.task('sources', ['riot-tag', 'riot-static-tag', 'scripts', 'vendor', 'scripts-static', 'styles'], function() {
|
||||
gulp.task('sources', ['docker-registry-ui', 'vendor', 'docker-registry-ui-static', 'styles'], function() {
|
||||
gulp.start();
|
||||
});
|
||||
|
||||
|
||||
10
package.json
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "docker-registry-ui",
|
||||
"version": "0.3.7",
|
||||
"version": "0.4.0",
|
||||
"scripts": {
|
||||
"build": "./node_modules/gulp/bin/gulp.js build"
|
||||
},
|
||||
@@ -23,12 +23,12 @@
|
||||
"gulp-inject-version": "^1.0.1",
|
||||
"gulp-license": "^1.1.0",
|
||||
"gulp-riot": "^1.1.4",
|
||||
"gulp-uglify": "^2.1.2",
|
||||
"gulp-uglify": "^3.0.0",
|
||||
"gulp-useref": "^3.1.5",
|
||||
"riot": "^3.10.0",
|
||||
"riot": "^3.11.1",
|
||||
"riot-mui": "^0.1.1",
|
||||
"riot-route": "^3.1.3",
|
||||
"uglify-js": "^3.4.0",
|
||||
"uglify-js-harmony": "^2.7.7"
|
||||
"stream-series": "^0.1.1",
|
||||
"uglify-es": "^3.3.10"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,12 +32,13 @@
|
||||
|
||||
<body>
|
||||
<app></app>
|
||||
<script src="scripts/init.js"></script>
|
||||
<!-- build:js scripts/vendor.js -->
|
||||
<script src="../node_modules/riot/riot+compiler.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 -->
|
||||
<!-- build:js scripts/docker-registry-ui.js -->
|
||||
<script src="tags/catalog.tag" type="riot/tag"></script>
|
||||
<script src="tags/taglist.tag" type="riot/tag"></script>
|
||||
<script src="tags/image-tag.tag" type="riot/tag"></script>
|
||||
@@ -49,8 +50,6 @@
|
||||
<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 -->
|
||||
<script src="scripts/http.js"></script>
|
||||
<script src="scripts/script.js"></script>
|
||||
<!-- endbuild -->
|
||||
|
||||
@@ -92,7 +92,9 @@ Http.getErrorMessage = function() {
|
||||
if (registryUI.url() && registryUI.url().match('^http://') && window.location.protocol === 'https:') {
|
||||
return 'Mixed Content: The page at `' + window.location.origin + '` was loaded over HTTPS, but requested an insecure server endpoint `' + registryUI.url() + '`. This request has been blocked; the content must be served over HTTPS.';
|
||||
} else if (!registryUI.url()) {
|
||||
return 'Incorrect server endpoint.'
|
||||
return 'Incorrect server endpoint.';
|
||||
} else if (this.withCredentials && !this.hasHeader('Access-Control-Allow-Credentials')) {
|
||||
return 'The `Access-Control-Allow-Credentials` header in the response is missing and must be set to `true` when the request\'s credentials mode is on. Origin `'+ registryUI.url() +'` is therefore not allowed access.';
|
||||
}
|
||||
return 'An error occured';
|
||||
return 'An error occured: Check your connection and your registry must have `Access-Control-Allow-Origin` header set to `' + window.location.origin + '`';
|
||||
};
|
||||
27
src/scripts/init.js
Normal file
27
src/scripts/init.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const REGISTRY_SERVER_KEY = 'registryServer';
|
||||
let registryServersStr = localStorage.getItem(REGISTRY_SERVER_KEY);
|
||||
let registryServers = [];
|
||||
if (registryServersStr !== null){
|
||||
let rs = JSON.parse(registryServersStr);
|
||||
if (rs instanceof Array) {
|
||||
registryServers = rs;
|
||||
}
|
||||
}
|
||||
|
||||
const apiHost = location.protocol+'//'+location.host;
|
||||
if (!registryServers.includes(apiHost)) {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', apiHost+"/v2/", true);
|
||||
xhr.onload = function (e) {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === 200) {
|
||||
const version = xhr.getResponseHeader('Docker-Distribution-Api-Version');
|
||||
if (version.startsWith('registry/2.')) {
|
||||
registryServers.unshift(apiHost);
|
||||
localStorage.setItem(REGISTRY_SERVER_KEY, JSON.stringify(registryServers));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send();
|
||||
}
|
||||
@@ -111,4 +111,6 @@ registryUI.isImageRemoveActivated = true;
|
||||
registryUI.catalog = {};
|
||||
registryUI.taglist = {};
|
||||
|
||||
riot.mount('*');
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
riot.mount('*');
|
||||
});
|
||||
|
||||
@@ -25,6 +25,6 @@ registryUI.isImageRemoveActivated = true;
|
||||
registryUI.catalog = {};
|
||||
registryUI.taglist = {};
|
||||
|
||||
riot.mount('catalog');
|
||||
riot.mount('taglist');
|
||||
riot.mount('app');
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
riot.mount('*');
|
||||
});
|
||||
|
||||
@@ -72,13 +72,13 @@
|
||||
}
|
||||
};
|
||||
registryUI.snackbar = function (message, isError) {
|
||||
registryUI.appTag.tags['material-snackbar'].addToast({'message': message, 'isError': isError});
|
||||
registryUI.appTag.tags['material-snackbar'].addToast({'message': message, 'isError': isError}, 15000);
|
||||
};
|
||||
registryUI.errorSnackbar = function (message) {
|
||||
return registryUI.snackbar(message, true);
|
||||
}
|
||||
registryUI.cleanName = function() {
|
||||
var url = registryUI.url() || registryUI.name();
|
||||
var url = (registryUI.url() && registryUI.url().length > 0 && registryUI.url()) || window.location.host;
|
||||
if (url) {
|
||||
return url.startsWith('http') ? url.replace(/https?:\/\//, '') : url;
|
||||
}
|
||||
@@ -94,6 +94,10 @@
|
||||
if (args) return args.slice(1)
|
||||
});
|
||||
|
||||
registryUI.isDigit = function(char) {
|
||||
return char >= '0' && char <= '9';
|
||||
}
|
||||
|
||||
registryUI.DockerImage = function (name, tag) {
|
||||
this.name = name;
|
||||
this.tag = tag;
|
||||
@@ -112,8 +116,31 @@
|
||||
});
|
||||
};
|
||||
|
||||
registryUI.DockerImage._tagReduce = function (acc, e) {
|
||||
if (acc.length > 0 && registryUI.isDigit(acc[acc.length - 1].charAt(0)) == registryUI.isDigit(e)) {
|
||||
acc[acc.length - 1] += e;
|
||||
} else {
|
||||
acc.push(e);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
registryUI.DockerImage.compare = function(e1, e2) {
|
||||
return e1.tag.localeCompare(e2);
|
||||
var tag1 = e1.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce, []);
|
||||
var tag2 = e2.tag.match(/./g).reduce(registryUI.DockerImage._tagReduce, []);
|
||||
|
||||
for (var i = 0; i < tag1.length && i < tag2.length; i++) {
|
||||
var compare = tag1[i].localeCompare(tag2[i]);
|
||||
if (registryUI.isDigit(tag1[i].charAt(0)) && registryUI.isDigit(tag2[i].charAt(0))) {
|
||||
var diff = tag1[i] - tag2[i];
|
||||
if (diff != 0) {
|
||||
return diff;
|
||||
}
|
||||
} else if (compare != 0) {
|
||||
return compare;
|
||||
}
|
||||
}
|
||||
return e1.tag.length - e2.tag.length;
|
||||
};
|
||||
|
||||
registryUI.DockerImage.prototype.fillInfo = function() {
|
||||
@@ -129,7 +156,7 @@
|
||||
self.size = response.layers.reduce(function (acc, e) {
|
||||
return acc + e.size;
|
||||
}, 0);
|
||||
self.sha256 = response.config.digest;
|
||||
self.sha256 = response.config.digest;
|
||||
self.trigger('size', self.size);
|
||||
self.trigger('sha256', self.sha256);
|
||||
} else if (this.status == 404) {
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
copyText.select();
|
||||
document.execCommand('copy');
|
||||
copyText.style.display = 'none';
|
||||
|
||||
registryUI.snackbar('`' + this.dockerCmd + '` has been copied to clipbloard.')
|
||||
|
||||
registryUI.snackbar('`' + this.dockerCmd + '` has been copied to clipboard.')
|
||||
};
|
||||
</script>
|
||||
</copy-to-clipboard>
|
||||
@@ -20,8 +20,7 @@ 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 dist/scripts/docker-registry-ui-static.js /usr/share/nginx/html/scripts/docker-registry-ui.js
|
||||
COPY bin/entrypoint /bin
|
||||
|
||||
ENTRYPOINT entrypoint
|
||||
|
||||
Reference in New Issue
Block a user